Преглед изворни кода

Wrap VolumetricLighting.hx raymarching into raymarch function

clementlandrin пре 8 месеци
родитељ
комит
a9f8e40f2c
1 измењених фајлова са 462 додато и 457 уклоњено
  1. 462 457
      hrt/prefab/rfx/VolumetricLighting.hx

+ 462 - 457
hrt/prefab/rfx/VolumetricLighting.hx

@@ -1,458 +1,463 @@
-package hrt.prefab.rfx;
-
-class VolumetricLightingShader extends h3d.shader.pbr.DefaultForward {
-
-	static var SRC = {
-
-		@global var global : {
-			var time : Float;
-		}
-
-		@param var invViewProj : Mat4;
-
-		@param var noiseTurmoil : Float;
-		@param var noiseScale : Float;
-		@param var noiseLacunarity : Float;
-		@param var noisePersistence : Float;
-		@param var noiseSharpness : Float;
-		@param var noiseOctave : Int;
-		@param var noiseTex : Sampler2D;
-
-		@param var depthMap : Sampler2D;
-
-		@param var steps : Int;
-		@param var startDistance : Float;
-		@param var endDistance : Float;
-		@param var maxCamDist : Float = 0.0;
-		@param var distanceOpacity : Float;
-
-		@param var ditheringNoise : Sampler2D;
-		@param var ditheringSize : Vec2;
-		@param var targetSize : Vec2;
-		@param var ditheringIntensity : Float;
-
-		@param var color : Vec3;
-		@param var fogDensity : Float;
-		@param var fogUseNoise : Float;
-		@param var fogBottom : Float;
-		@param var fogTop : Float;
-		@param var fogHeightFalloff : Float;
-		@param var fogEnvPower : Float;
-		@param var fogEnvColorMult : Float;
-
-		@param var secondFogColor : Vec3;
-		@param var secondFogDensity : Float;
-		@param var secondFogUseNoise : Float;
-		@param var secondFogBottom : Float;
-		@param var secondFogTop : Float;
-		@param var secondFogHeightFalloff : Float;
-
-		var calculatedUV : Vec2;
-		var depthInvDimensions : Vec2;
-
-		function noise( pos : Vec3 ) : Float {
-			var i = floor(pos);
-    		var f = fract(pos);
-			f = f*f*(3.0-2.0*f);
-			var uv = (i.xy+vec2(37.0,239.0)*i.z) + f.xy;
-			var rg = noiseTex.getLod( (uv+0.5) / 256.0, 0 ).yx;
-			return mix( rg.x, rg.y, f.z );
-		}
-
-		function noiseAt( pos : Vec3 ) : Float {
-			var amount = 0.;
-			if ( noiseOctave > 0 ) {
-				var p = pos * 0.1 * noiseScale;
-				var t = global.time * noiseTurmoil;
-				amount += noise(p - t * vec3(0, 0, 1));
-				var tot = 1.;
-				var k = noisePersistence;
-				p *= noiseLacunarity;
-				if ( noiseOctave >= 2 ) {
-					amount += noise(p + t * vec3(0, 0, -0.6)) * k;
-					k *= noisePersistence;
-					p *= noiseLacunarity;
-					tot += k;
-				}
-				if ( noiseOctave >= 3 ) {
-					amount += noise(p + t * vec3(-0.9, 0, 1.1)) * k;
-					k *= noisePersistence;
-					p *= noiseLacunarity;
-					tot += k;
-				}
-				if ( noiseOctave >= 4 ) {
-					amount += noise(p + t * vec3(0.8, 0.95,-1.2)) * k;
-					k *= noisePersistence;
-					p *= noiseLacunarity;
-					tot += k;
-				}
-
-				if ( noiseOctave >= 5 ) {
-					amount += noise(p + t * vec3(0,-0.84,-1.3)) * k;
-					tot += k;
-					p *= noiseLacunarity;
-					k *= noisePersistence;
-					tot += k;
-				}
-				amount = pow(amount / tot, noiseSharpness);
-			} else {
-				amount = 1.0;
-			}
-			return amount;
-		}
-
-		function indirectLighting() : Vec3 {
-			return envColor * irrPower * fogEnvPower;
-		}
-
-		function getFogColor() : Vec3 {
-			return mix(color, secondFogColor, useSecondColor);
-		}
-
-		function directLighting(lightColor : Vec3, lightDirection : Vec3) : Vec3 {
-			return lightColor;
-		}
-
-		function pointLightIntensity( delta : Vec3, size : Float, invRange4 : Float ) : Float {
-			var dist = delta.dot(delta);
-			var falloff = saturate(1 - dist*dist * invRange4);
-			if( size > 0 ) {
-				dist = (dist.sqrt() - size).max(0.);
-				dist *= dist;
-			}
-			falloff *= falloff;
-			return falloff * falloff * exp(-fog * dist);
-		}
-
-		var useSecondColor : Float;
-		function fogAt(pos : Vec3) : Float {
-			var n = noiseAt(pos);
-			var hNorm = smoothstep(0.0, 1.0, (pos.z - fogBottom) / (fogTop - fogBottom));
-			var firstFog = exp(-hNorm * fogHeightFalloff) * (1.0 - hNorm) * fogDensity;
-
-			var secondHNorm = smoothstep(0.0, 1.0, (pos.z - secondFogBottom) / (secondFogTop - secondFogBottom));
-			var secondFog = exp(-secondHNorm * secondFogHeightFalloff) * (1.0 - secondHNorm) * secondFogDensity;
-			firstFog *= mix(1.0, n, fogUseNoise);
-			secondFog *= mix(1.0, n, secondFogUseNoise);
-
-			useSecondColor = saturate(secondFog / max(firstFog, secondFog));
-			return max(firstFog, secondFog);
-		}
-
-		function getWPos() : Vec3 {
-			var pixels = fragCoord.xy;
-			var depth = depthMap.get( pixels * depthInvDimensions ).r;
-			var uv2 = uvToScreen(calculatedUV);
-			var temp = vec4(uv2, depth, 1) * invViewProj;
-			return temp.xyz / temp.w;
-		}
-
-		function getDistBlend(dist: Float) : Float {
-			var dfactor = smoothstep(0, 1, dist / endDistance);
-			return dfactor * dfactor;
-		}
-
-		var camDir : Vec3;
-		var envColor : Vec3;
-		var fog : Float;
-		function fragment() {
-			metalness = 0.0;
-			emissive = 0.0;
-			albedoGamma = vec3(0.0);
-			useSecondColor = 0.0;
-
-			depthInvDimensions = 1.0 / depthMap.size();
-			var endPos = getWPos();
-
-			camDir = normalize(endPos - camera.position);
-			var startPos = camera.position + camDir * startDistance;
-			var cameraDistance = length(endPos - startPos);
-			if ( dot(camDir, endPos - startPos) < 0.0 )
-				discard;
-			if ( maxCamDist > 0.0 )
-				cameraDistance = min(cameraDistance, maxCamDist);
-			endPos = startPos + camDir * cameraDistance;
-
-			envColor = irrDiffuse.getLod(-camDir, 0.0).rgb;
-			view = -camDir;
-
-			var stepSize = length(endPos - startPos) / float(steps);
-			var dithering = ditheringNoise.getLod(calculatedUV * targetSize / ditheringSize, 0.0).r * stepSize * ditheringIntensity;
-			startPos += dithering * camDir;
-			var opacity = 0.0;
-			var totalScattered = vec3(0.0);
-			var opticalDepth = 0.0;
-			pixelColor = vec4(1.0);
-			var colorAcc = vec4(0.0);
-			var curDist = 0.0;
-			for ( i in 0...steps ) {
-				if ( colorAcc.a > 0.99 )
-					break;
-				transformedPosition = startPos + camDir * curDist;
-				fog = fogAt(transformedPosition);
-				var fColor = getFogColor();
-				var stepColor = evaluateLighting() * fColor * mix(vec3(1.0), saturate(envColor), fogEnvColorMult);
-
-				var stepColor = vec4(stepColor * fog, fog);
-
-				colorAcc += stepColor * (1.0 - colorAcc.a) * getDistBlend(curDist) * stepSize;
-
-				curDist += stepSize;
-			}
-			pixelColor.rgb = colorAcc.rgb / (0.001 + colorAcc.a);
-			pixelColor.a = saturate(distanceOpacity * colorAcc.a);
-		}
-	};
-}
-
-@:access(h3d.scene.Renderer)
-class VolumetricLighting extends RendererFX {
-
-	var pass = new h3d.pass.ScreenFx(new h3d.shader.ScreenShader());
-	var halfingDepthPass = new h3d.pass.ScreenFx(new h3d.shader.CheckerboardDepth() );
-	var upsamplingPass = new h3d.pass.ScreenFx(new h3d.shader.DepthAwareUpsampling());
-
-	var blurPass = new h3d.pass.Blur();
-	var vshader = new VolumetricLightingShader();
-
-	@:s public var AFTER_FX : Bool = false;
-	@:s public var blend : h3d.mat.PbrMaterial.PbrBlend = Alpha;
-	@:s public var color : Int = 0xFFFFFF;
-	@:s public var steps : Int = 10;
-	@:s public var blur : Float = 0.0;
-	@:s public var blurDepthThreshold : Float = 10.0;
-	@:s public var startDistance : Float = 0.0;
-	@:s public var endDistance : Float = 200.0;
-	@:s public var distanceOpacity : Float = 1.0;
-	@:s public var ditheringIntensity : Float = 1.0;
-
-	@:s public var noiseScale : Float = 1.0;
-	@:s public var noiseLacunarity : Float = 2.0;
-	@:s public var noiseSharpness : Float = 1.0;
-	@:s public var noisePersistence : Float = 0.5;
-	@:s public var noiseTurmoil : Float = 1.0;
-	@:s public var noiseOctave : Int = 1;
-
-	@:s public var fogDensity : Float = 1.0;
-	@:s public var fogUseNoise : Float = 1.0;
-	@:s public var fogHeightFalloff : Float = 1.0;
-	@:s public var fogEnvPower : Float = 1.0;
-	@:s public var fogBottom : Float = 0.0;
-	@:s public var fogTop : Float = 200.0;
-	@:s public var fogEnvColorMult : Float = 0.0;
-
-	@:s public var secondFogColor : Int = 0xFFFFFF;
-	@:s public var secondFogUseNoise : Float = 1.0;
-	@:s public var secondFogDensity : Float = 0.0;
-	@:s public var secondFogHeightFalloff : Float = 5.0;
-	@:s public var secondFogBottom : Float = 0.0;
-	@:s public var secondFogTop : Float = 50.0;
-
-	var noiseTex : h3d.mat.Texture;
-
-	function execute(r : h3d.scene.Renderer, step : h3d.impl.RendererFX.Step) {
-		if( step == BeforeTonemapping ) {
-			var r = cast(r, h3d.scene.pbr.Renderer);
-			r.mark("VolumetricLighting");
-
-			if ( noiseTex == null )
-				noiseTex = makeNoiseTex();
-
-			var depth = r.textures.albedo.depthBuffer;
-
-			var prevFilter = depth.filter;
-			depth.filter = Nearest;
-
-			var halfDepth = r.allocTarget("halfDepth", false, 0.5, R32F);
-			halfDepth.filter = Nearest;
-			r.ctx.engine.pushTarget(halfDepth);
-			halfingDepthPass.shader.source = depth;
-			halfingDepthPass.shader.texRatio = 2.0;
-			halfingDepthPass.render();
-			r.ctx.engine.popTarget();
-
-			var tex = r.allocTarget("volumetricLighting", false, 0.5, RGBA16F);
-			tex.clear(0, 0.0);
-			r.ctx.engine.pushTarget(tex);
-
-			vshader.USE_INDIRECT = false;
-			if ( pass.getShader(h3d.shader.pbr.DefaultForward) == null )
-				pass.addShader(vshader);
-			var ls = cast(r.getLightSystem(), h3d.scene.pbr.LightSystem);
-			ls.lightBuffer.setBuffers(vshader);
-			vshader.depthMap = halfDepth;
-			vshader.startDistance = startDistance;
-			vshader.endDistance = endDistance;
-			vshader.distanceOpacity = distanceOpacity;
-			vshader.steps = steps;
-			vshader.invViewProj = r.ctx.camera.getInverseViewProj();
-			if ( vshader.ditheringNoise == null ) {
-				vshader.ditheringNoise = hxd.res.Embed.getResource("hrt/prefab/rfx/blueNoise.png").toImage().toTexture();
-				vshader.ditheringNoise.wrap = Repeat;
-			}
-			vshader.targetSize.set(tex.width, tex.height);
-			vshader.ditheringSize.set(vshader.ditheringNoise.width, vshader.ditheringNoise.height);
-			vshader.ditheringIntensity = ditheringIntensity;
-			vshader.noiseTex = noiseTex;
-			vshader.noiseScale = noiseScale;
-			vshader.noiseOctave = noiseOctave;
-			vshader.noiseTurmoil = noiseTurmoil;
-			vshader.noiseSharpness = noiseSharpness;
-			vshader.noisePersistence = noisePersistence;
-			vshader.noiseLacunarity = noiseLacunarity;
-			vshader.fogEnvPower = fogEnvPower;
-
-			vshader.color.load(h3d.Vector.fromColor(color));
-			vshader.fogDensity = fogDensity * 0.002;
-			vshader.fogUseNoise = fogUseNoise;
-			vshader.fogBottom = fogBottom;
-			vshader.fogTop = fogTop;
-			vshader.fogEnvColorMult = fogEnvColorMult;
-			vshader.fogHeightFalloff = fogHeightFalloff;
-
-			vshader.secondFogColor.load(h3d.Vector.fromColor(secondFogColor));
-			vshader.secondFogDensity = secondFogDensity * 0.002;
-			vshader.secondFogUseNoise = secondFogUseNoise;
-			vshader.secondFogBottom = secondFogBottom;
-			vshader.secondFogTop = secondFogTop;
-			vshader.secondFogHeightFalloff = secondFogHeightFalloff;
-			pass.pass.setBlendMode(Alpha);
-			pass.render();
-
-			r.ctx.engine.popTarget();
-
-			var inverseProj = r.ctx.camera.getInverseProj();
-
-			blurPass.radius = blur;
-			blurPass.shader.isDepthDependant = true;
-			blurPass.shader.depthTexture = halfDepth;
-			blurPass.shader.inverseProj = inverseProj;
-			blurPass.shader.depthThreshold = blurDepthThreshold;
-			blurPass.apply(r.ctx, tex);
-
-			var b : h3d.mat.BlendMode = switch ( blend ) {
-			case None: None;
-			case Alpha: Alpha;
-			case Add: Add;
-			case AlphaAdd: AlphaAdd;
-			case Multiply: Multiply;
-			case AlphaMultiply: AlphaMultiply;
-			}
-
-			r.ctx.engine.pushTarget(r.textures.hdr, 0, 0, NotBound);
-			upsamplingPass.pass.setBlendMode(b);
-			upsamplingPass.shader.source = tex;
-			upsamplingPass.shader.sourceDepth = halfDepth;
-			upsamplingPass.shader.destDepth = depth;
-			upsamplingPass.shader.inverseProj = inverseProj;
-			upsamplingPass.render();
-			r.ctx.engine.popTarget();
-
-			depth.filter = prevFilter;
-		}
-	}
-
-	override function begin(r:h3d.scene.Renderer, step:h3d.impl.RendererFX.Step) {
-		if ( !AFTER_FX )
-			execute(r, step);
-	}
-
-	override function end(r:h3d.scene.Renderer, step:h3d.impl.RendererFX.Step) {
-		if ( AFTER_FX )
-			execute(r, step);
-	}
-
-	function makeNoiseTex() : h3d.mat.Texture {
-		var rands : Array<Int> = [];
-		var rand = new hxd.Rand(0);
-		for(x in 0...256)
-			for(y in 0...256)
-				rands.push(rand.random(256));
-		var pix = hxd.Pixels.alloc(256, 256, RGBA);
-		for(x in 0...256) {
-			for(y in 0...256) {
-				var r = rands[x + y * 256];
-				var g = rands[((x - 37) & 255) + ((y - 239) & 255) * 256];
-				var off = (x + y*256) * 4;
-				pix.bytes.set(off, r);
-				pix.bytes.set(off+1, g);
-				pix.bytes.set(off+3, 255);
-			}
-		}
-		var tex = new h3d.mat.Texture(pix.width, pix.height, [], RGBA);
-		tex.uploadPixels(pix);
-		tex.wrap = Repeat;
-		return tex;
-	}
-
-	#if editor
-
-	override function edit( ctx : hide.prefab.EditContext ) {
-		super.edit(ctx);
-		ctx.properties.add(new hide.Element(
-			'<div class="group" name="Fog">
-				<dl>
-					<dt>Blend</dt>
-					<dd>
-						<select field="blend">
-							<option value="None">None</option>
-							<option value="Alpha">Alpha</option>
-							<option value="Add">Add</option>
-							<option value="AlphaAdd">AlphaAdd</option>
-							<option value="Multiply">Multiply</option>
-							<option value="AlphaMultiply">AlphaMultiply</option>
-						</select>
-					</dd>
-					<dt>After fx</dt><dd><input type="checkbox" field="AFTER_FX"/></dd>
-					<dt>Begin</dt><dd><input type="range" min="0" field="startDistance"/></dd>
-					<dt>Start distance</dt><dd><input type="range" min="0" field="startDistance"/></dd>
-					<dt>End distance</dt><dd><input type="range" min="0" field="endDistance"/></dd>
-					<dt>Distance opacity</dt><dd><input type="range" min="0" max="1" field="distanceOpacity"/></dd>
-					<dt>Env power</dt><dd><input type="range" min="0" max="2" field="fogEnvPower"/></dd>
-					<dt>Env color mult</dt><dd><input type="range" min="0" max="1" field="fogEnvColorMult"/></dd>
-					<dt>Color</dt><dd><input type="color" field="color"/></dd>
-					<dt>Density</dt><dd><input type="range" min="0" max="2" field="fogDensity"/></dd>
-					<dt>Use noise</dt><dd><input type="range" min="0" max="1" field="fogUseNoise"/></dd>
-					<dt>Bottom [m]</dt><dd><input type="range" min="0" max="1000" field="fogBottom"/></dd>
-					<dt>Top [m]</dt><dd><input type="range" min="0" max="1000" field="fogTop"/></dd>
-					<dt>Height falloff</dt><dd><input type="range" min="0" max="3" field="fogHeightFalloff"/></dd>
-				</dl>
-			</div>
-			<div class="group" name="Second fog">
-				<dl>
-					<dt>Color</dt><dd><input type="color" field="secondFogColor"/></dd>
-					<dt>Density</dt><dd><input type="range" min="0" max="2" field="secondFogDensity"/></dd>
-					<dt>Use noise</dt><dd><input type="range" min="0" max="1" field="secondFogUseNoise"/></dd>
-					<dt>Bottom [m]</dt><dd><input type="range" min="0" max="1000" field="secondFogBottom"/></dd>
-					<dt>Top [m]</dt><dd><input type="range" min="0" max="1000" field="secondFogTop"/></dd>
-					<dt>Height falloff</dt><dd><input type="range" min="0" max="3" field="secondFogHeightFalloff"/></dd>
-				</dl>
-			</div>
-			<div class="group" name="Noise">
-				<dl>
-					<dt><font color=#FF0000>Octaves</font></dt><dd><input type="range" step="1" min="0" max="4" field="noiseOctave"/></dd>
-					<dt>Scale</dt><dd><input type="range" min="0" max="100" field="noiseScale"/></dd>
-					<dt>Turmoil</dt><dd><input type="range" min="0" max="100" field="noiseTurmoil"/></dd>
-					<dt>Persistence</dt><dd><input type="range" min="0" max="1" field="noisePersistence"/></dd>
-					<dt>Lacunarity</dt><dd><input type="range" min="0" max="2" field="noiseLacunarity"/></dd>
-					<dt>Sharpness</dt><dd><input type="range" min="0" max="2" field="noiseSharpness"/></dd>
-				</dl>
-			</div>
-			<div class="group" name="Rendering">
-				<dl>
-					<dt><font color=#FF0000>Steps</font></dt><dd><input type="range" step="1" min="0" max="255" field="steps"/></dd>
-					<dt>Blur</dt><dd><input type="range" step="1" min="0" max="100" field="blur"/></dd>
-					<dt>Blur depth threshold</dt><dd><input type="range" field="blurDepthThreshold"/></dd>
-					<dt>Dithering intensity</dt><dd><input type="range" min="0" max="1" field="ditheringIntensity"/></dd>
-				</dl>
-			</div>
-			'), this, function(pname) {
-				ctx.onChange(this, pname);
-		});
-	}
-
-	#end
-
-	static var _ = Prefab.register("rfx.volumetricLighting", VolumetricLighting);
-
+package hrt.prefab.rfx;
+
+class VolumetricLightingShader extends h3d.shader.pbr.DefaultForward {
+
+	static var SRC = {
+
+		@global var global : {
+			var time : Float;
+		}
+
+		@param var invViewProj : Mat4;
+
+		@param var noiseTurmoil : Float;
+		@param var noiseScale : Float;
+		@param var noiseLacunarity : Float;
+		@param var noisePersistence : Float;
+		@param var noiseSharpness : Float;
+		@param var noiseOctave : Int;
+		@param var noiseTex : Sampler2D;
+
+		@param var halfDepthMap : Sampler2D;
+
+		@param var steps : Int;
+		@param var startDistance : Float;
+		@param var endDistance : Float;
+		@param var maxCamDist : Float = 0.0;
+		@param var distanceOpacity : Float;
+
+		@param var ditheringNoise : Sampler2D;
+		@param var ditheringSize : Vec2;
+		@param var targetSize : Vec2;
+		@param var ditheringIntensity : Float;
+
+		@param var color : Vec3;
+		@param var fogDensity : Float;
+		@param var fogUseNoise : Float;
+		@param var fogBottom : Float;
+		@param var fogTop : Float;
+		@param var fogHeightFalloff : Float;
+		@param var fogEnvPower : Float;
+		@param var fogEnvColorMult : Float;
+
+		@param var secondFogColor : Vec3;
+		@param var secondFogDensity : Float;
+		@param var secondFogUseNoise : Float;
+		@param var secondFogBottom : Float;
+		@param var secondFogTop : Float;
+		@param var secondFogHeightFalloff : Float;
+
+		var calculatedUV : Vec2;
+
+		function noise( pos : Vec3 ) : Float {
+			var i = floor(pos);
+    		var f = fract(pos);
+			f = f*f*(3.0-2.0*f);
+			var uv = (i.xy+vec2(37.0,239.0)*i.z) + f.xy;
+			var rg = noiseTex.getLod( (uv+0.5) / 256.0, 0 ).yx;
+			return mix( rg.x, rg.y, f.z );
+		}
+
+		function noiseAt( pos : Vec3 ) : Float {
+			var amount = 0.;
+			if ( noiseOctave > 0 ) {
+				var p = pos * 0.1 * noiseScale;
+				var t = global.time * noiseTurmoil;
+				amount += noise(p - t * vec3(0, 0, 1));
+				var tot = 1.;
+				var k = noisePersistence;
+				p *= noiseLacunarity;
+				if ( noiseOctave >= 2 ) {
+					amount += noise(p + t * vec3(0, 0, -0.6)) * k;
+					k *= noisePersistence;
+					p *= noiseLacunarity;
+					tot += k;
+				}
+				if ( noiseOctave >= 3 ) {
+					amount += noise(p + t * vec3(-0.9, 0, 1.1)) * k;
+					k *= noisePersistence;
+					p *= noiseLacunarity;
+					tot += k;
+				}
+				if ( noiseOctave >= 4 ) {
+					amount += noise(p + t * vec3(0.8, 0.95,-1.2)) * k;
+					k *= noisePersistence;
+					p *= noiseLacunarity;
+					tot += k;
+				}
+
+				if ( noiseOctave >= 5 ) {
+					amount += noise(p + t * vec3(0,-0.84,-1.3)) * k;
+					tot += k;
+					p *= noiseLacunarity;
+					k *= noisePersistence;
+					tot += k;
+				}
+				amount = pow(amount / tot, noiseSharpness);
+			} else {
+				amount = 1.0;
+			}
+			return amount;
+		}
+
+		function indirectLighting() : Vec3 {
+			return envColor * irrPower * fogEnvPower;
+		}
+
+		function getFogColor() : Vec3 {
+			return mix(color, secondFogColor, useSecondColor);
+		}
+
+		function directLighting(lightColor : Vec3, lightDirection : Vec3) : Vec3 {
+			return lightColor;
+		}
+
+		function pointLightIntensity( delta : Vec3, size : Float, invRange4 : Float ) : Float {
+			var dist = delta.dot(delta);
+			var falloff = saturate(1 - dist*dist * invRange4);
+			if( size > 0 ) {
+				dist = (dist.sqrt() - size).max(0.);
+				dist *= dist;
+			}
+			falloff *= falloff;
+			return falloff * falloff * exp(-fog * dist);
+		}
+
+		var useSecondColor : Float;
+		function fogAt(pos : Vec3) : Float {
+			var n = noiseAt(pos);
+			var hNorm = smoothstep(0.0, 1.0, (pos.z - fogBottom) / (fogTop - fogBottom));
+			var firstFog = exp(-hNorm * fogHeightFalloff) * (1.0 - hNorm) * fogDensity;
+
+			var secondHNorm = smoothstep(0.0, 1.0, (pos.z - secondFogBottom) / (secondFogTop - secondFogBottom));
+			var secondFog = exp(-secondHNorm * secondFogHeightFalloff) * (1.0 - secondHNorm) * secondFogDensity;
+			firstFog *= mix(1.0, n, fogUseNoise);
+			secondFog *= mix(1.0, n, secondFogUseNoise);
+
+			useSecondColor = saturate(secondFog / max(firstFog, secondFog));
+			return max(firstFog, secondFog);
+		}
+
+		function getWPos() : Vec3 {
			var depth = halfDepthMap.get( fragCoord.xy / halfDepthMap.size() ).r;
+			var uv2 = uvToScreen(calculatedUV);
+			var temp = vec4(uv2, depth, 1) * invViewProj;
+			return temp.xyz / temp.w;
+		}
+
+		function getDistBlend(dist: Float) : Float {
+			var dfactor = smoothstep(0, 1, dist / endDistance);
+			return dfactor * dfactor;
+		}
+
+		var camDir : Vec3;
+		var envColor : Vec3;
+		var fog : Float;
+		function rayMarch() : Vec4 {
+			metalness = 0.0;
+			emissive = 0.0;
+			albedoGamma = vec3(0.0);
+			useSecondColor = 0.0;
+
+			var endPos = getWPos();
+			camDir = normalize(endPos - camera.position);
+			var startPos = camera.position + camDir * startDistance;
+			var cameraDistance = length(endPos - startPos);
+			if ( dot(camDir, endPos - startPos) < 0.0 )
+				discard;
+			if ( maxCamDist > 0.0 )
+				cameraDistance = min(cameraDistance, maxCamDist);
+			endPos = startPos + camDir * cameraDistance;
+
+			envColor = irrDiffuse.getLod(-camDir, 0.0).rgb;
+			view = -camDir;
+
+			var stepSize = length(endPos - startPos) / float(steps);
+			var dithering = ditheringNoise.getLod(calculatedUV * targetSize / ditheringSize, 0.0).r * stepSize * ditheringIntensity;
+			startPos += dithering * camDir;
+			var opacity = 0.0;
+			var totalScattered = vec3(0.0);
+			var opticalDepth = 0.0;
+			var prevPixelColor = pixelColor;
+			pixelColor = vec4(1.0);
+			var colorAcc = vec4(0.0);
+			var curDist = 0.0;
+			for ( i in 0...steps ) {
+				if ( colorAcc.a > 0.99 )
+					break;
+				transformedPosition = startPos + camDir * curDist;
+				fog = fogAt(transformedPosition);
+				var fColor = getFogColor();
+				var stepColor = evaluateLighting() * fColor * mix(vec3(1.0), saturate(envColor), fogEnvColorMult);
+
+				var stepColor = vec4(stepColor * fog, fog);
+
+				colorAcc += stepColor * (1.0 - colorAcc.a) * getDistBlend(curDist) * stepSize;
+
+				curDist += stepSize;
+			}
+			var outColor = colorAcc.rgb / (0.001 + colorAcc.a);
+			var opacity = saturate(distanceOpacity * colorAcc.a);
+
+			return vec4(outColor, opacity);
+		}
+
+		function fragment() {
+			pixelColor = rayMarch();
+		}
+	}
+}
+
+@:access(h3d.scene.Renderer)
+class VolumetricLighting extends RendererFX {
+
+	var pass = new h3d.pass.ScreenFx(new h3d.shader.ScreenShader());
+	var halfingDepthPass = new h3d.pass.ScreenFx(new h3d.shader.CheckerboardDepth() );
+	var upsamplingPass = new h3d.pass.ScreenFx(new h3d.shader.DepthAwareUpsampling());
+
+	var blurPass = new h3d.pass.Blur();
+	var vshader = new VolumetricLightingShader();
+
+	@:s public var AFTER_FX : Bool = false;
+	@:s public var blend : h3d.mat.PbrMaterial.PbrBlend = Alpha;
+	@:s public var color : Int = 0xFFFFFF;
+	@:s public var steps : Int = 10;
+	@:s public var blur : Float = 0.0;
+	@:s public var blurDepthThreshold : Float = 10.0;
+	@:s public var startDistance : Float = 0.0;
+	@:s public var endDistance : Float = 200.0;
+	@:s public var distanceOpacity : Float = 1.0;
+	@:s public var ditheringIntensity : Float = 1.0;
+
+	@:s public var noiseScale : Float = 1.0;
+	@:s public var noiseLacunarity : Float = 2.0;
+	@:s public var noiseSharpness : Float = 1.0;
+	@:s public var noisePersistence : Float = 0.5;
+	@:s public var noiseTurmoil : Float = 1.0;
+	@:s public var noiseOctave : Int = 1;
+
+	@:s public var fogDensity : Float = 1.0;
+	@:s public var fogUseNoise : Float = 1.0;
+	@:s public var fogHeightFalloff : Float = 1.0;
+	@:s public var fogEnvPower : Float = 1.0;
+	@:s public var fogBottom : Float = 0.0;
+	@:s public var fogTop : Float = 200.0;
+	@:s public var fogEnvColorMult : Float = 0.0;
+
+	@:s public var secondFogColor : Int = 0xFFFFFF;
+	@:s public var secondFogUseNoise : Float = 1.0;
+	@:s public var secondFogDensity : Float = 0.0;
+	@:s public var secondFogHeightFalloff : Float = 5.0;
+	@:s public var secondFogBottom : Float = 0.0;
+	@:s public var secondFogTop : Float = 50.0;
+
+	var noiseTex : h3d.mat.Texture;
+
+	function execute(r : h3d.scene.Renderer, step : h3d.impl.RendererFX.Step) {
+		if( step == BeforeTonemapping ) {
+			if ( distanceOpacity <= 0.0 )
+				return;
+
+			var r = cast(r, h3d.scene.pbr.Renderer);
+			r.mark("VolumetricLighting");
+
+			if ( noiseTex == null )
+				noiseTex = makeNoiseTex();
+
+			var depth = r.textures.albedo.depthBuffer;
+
+			var prevFilter = depth.filter;
+			depth.filter = Nearest;
+
+			var halfDepth = r.allocTarget("halfDepth", false, 0.5, R32F);
+			halfDepth.filter = Nearest;
+			r.ctx.engine.pushTarget(halfDepth);
+			halfingDepthPass.shader.source = depth;
+			halfingDepthPass.shader.texRatio = 2.0;
+			halfingDepthPass.render();
+			r.ctx.engine.popTarget();
+
+			var tex = r.allocTarget("volumetricLighting", false, 0.5, RGBA16F);
+			tex.clear(0, 0.0);
+			r.ctx.engine.pushTarget(tex);
+
+			vshader.USE_INDIRECT = false;
+			if ( pass.getShader(h3d.shader.pbr.DefaultForward) == null )
+				pass.addShader(vshader);
+			var ls = cast(r.getLightSystem(), h3d.scene.pbr.LightSystem);
+			ls.lightBuffer.setBuffers(vshader);
+			vshader.halfDepthMap = halfDepth;
+			vshader.startDistance = startDistance;
+			vshader.endDistance = endDistance;
+			vshader.distanceOpacity = distanceOpacity;
+			vshader.steps = steps;
+			vshader.invViewProj = r.ctx.camera.getInverseViewProj();
+			if ( vshader.ditheringNoise == null ) {
+				vshader.ditheringNoise = hxd.res.Embed.getResource("hrt/prefab/rfx/blueNoise.png").toImage().toTexture();
+				vshader.ditheringNoise.wrap = Repeat;
+			}
+			vshader.targetSize.set(tex.width, tex.height);
+			vshader.ditheringSize.set(vshader.ditheringNoise.width, vshader.ditheringNoise.height);
+			vshader.ditheringIntensity = ditheringIntensity;
+			vshader.noiseTex = noiseTex;
+			vshader.noiseScale = noiseScale;
+			vshader.noiseOctave = noiseOctave;
+			vshader.noiseTurmoil = noiseTurmoil;
+			vshader.noiseSharpness = noiseSharpness;
+			vshader.noisePersistence = noisePersistence;
+			vshader.noiseLacunarity = noiseLacunarity;
+			vshader.fogEnvPower = fogEnvPower;
+
+			vshader.color.load(h3d.Vector.fromColor(color));
+			vshader.fogDensity = fogDensity * 0.002;
+			vshader.fogUseNoise = fogUseNoise;
+			vshader.fogBottom = fogBottom;
+			vshader.fogTop = fogTop;
+			vshader.fogEnvColorMult = fogEnvColorMult;
+			vshader.fogHeightFalloff = fogHeightFalloff;
+
+			vshader.secondFogColor.load(h3d.Vector.fromColor(secondFogColor));
+			vshader.secondFogDensity = secondFogDensity * 0.002;
+			vshader.secondFogUseNoise = secondFogUseNoise;
+			vshader.secondFogBottom = secondFogBottom;
+			vshader.secondFogTop = secondFogTop;
+			vshader.secondFogHeightFalloff = secondFogHeightFalloff;
+			pass.pass.setBlendMode(Alpha);
+			pass.render();
+
+			r.ctx.engine.popTarget();
+
+			var inverseProj = r.ctx.camera.getInverseProj();
+
+			blurPass.radius = blur;
+			blurPass.shader.isDepthDependant = true;
+			blurPass.shader.depthTexture = halfDepth;
+			blurPass.shader.inverseProj = inverseProj;
+			blurPass.shader.depthThreshold = blurDepthThreshold;
+			blurPass.apply(r.ctx, tex);
+
+			var b : h3d.mat.BlendMode = switch ( blend ) {
+			case None: None;
+			case Alpha: Alpha;
+			case Add: Add;
+			case AlphaAdd: AlphaAdd;
+			case Multiply: Multiply;
+			case AlphaMultiply: AlphaMultiply;
+			}
+
+			r.ctx.engine.pushTarget(r.textures.hdr, 0, 0, NotBound);
+			upsamplingPass.pass.setBlendMode(b);
+			upsamplingPass.shader.source = tex;
+			upsamplingPass.shader.sourceDepth = halfDepth;
+			upsamplingPass.shader.destDepth = depth;
+			upsamplingPass.shader.inverseProj = inverseProj;
+			upsamplingPass.render();
+			r.ctx.engine.popTarget();
+
+			depth.filter = prevFilter;
+		}
+	}
+
+	override function begin(r:h3d.scene.Renderer, step:h3d.impl.RendererFX.Step) {
+		if ( !AFTER_FX )
+			execute(r, step);
+	}
+
+	override function end(r:h3d.scene.Renderer, step:h3d.impl.RendererFX.Step) {
+		if ( AFTER_FX )
+			execute(r, step);
+	}
+
+	function makeNoiseTex() : h3d.mat.Texture {
+		var rands : Array<Int> = [];
+		var rand = new hxd.Rand(0);
+		for(x in 0...256)
+			for(y in 0...256)
+				rands.push(rand.random(256));
+		var pix = hxd.Pixels.alloc(256, 256, RGBA);
+		for(x in 0...256) {
+			for(y in 0...256) {
+				var r = rands[x + y * 256];
+				var g = rands[((x - 37) & 255) + ((y - 239) & 255) * 256];
+				var off = (x + y*256) * 4;
+				pix.bytes.set(off, r);
+				pix.bytes.set(off+1, g);
+				pix.bytes.set(off+3, 255);
+			}
+		}
+		var tex = new h3d.mat.Texture(pix.width, pix.height, [], RGBA);
+		tex.uploadPixels(pix);
+		tex.wrap = Repeat;
+		return tex;
+	}
+
+	#if editor
+
+	override function edit( ctx : hide.prefab.EditContext ) {
+		super.edit(ctx);
+		ctx.properties.add(new hide.Element(
+			'<div class="group" name="Fog">
+				<dl>
+					<dt>Blend</dt>
+					<dd>
+						<select field="blend">
+							<option value="None">None</option>
+							<option value="Alpha">Alpha</option>
+							<option value="Add">Add</option>
+							<option value="AlphaAdd">AlphaAdd</option>
+							<option value="Multiply">Multiply</option>
+							<option value="AlphaMultiply">AlphaMultiply</option>
+						</select>
+					</dd>
+					<dt>After fx</dt><dd><input type="checkbox" field="AFTER_FX"/></dd>
+					<dt>Begin</dt><dd><input type="range" min="0" field="startDistance"/></dd>
+					<dt>Start distance</dt><dd><input type="range" min="0" field="startDistance"/></dd>
+					<dt>End distance</dt><dd><input type="range" min="0" field="endDistance"/></dd>
+					<dt>Distance opacity</dt><dd><input type="range" min="0" max="1" field="distanceOpacity"/></dd>
+					<dt>Env power</dt><dd><input type="range" min="0" max="2" field="fogEnvPower"/></dd>
+					<dt>Env color mult</dt><dd><input type="range" min="0" max="1" field="fogEnvColorMult"/></dd>
+					<dt>Color</dt><dd><input type="color" field="color"/></dd>
+					<dt>Density</dt><dd><input type="range" min="0" max="2" field="fogDensity"/></dd>
+					<dt>Use noise</dt><dd><input type="range" min="0" max="1" field="fogUseNoise"/></dd>
+					<dt>Bottom [m]</dt><dd><input type="range" min="0" max="1000" field="fogBottom"/></dd>
+					<dt>Top [m]</dt><dd><input type="range" min="0" max="1000" field="fogTop"/></dd>
+					<dt>Height falloff</dt><dd><input type="range" min="0" max="3" field="fogHeightFalloff"/></dd>
+				</dl>
+			</div>
+			<div class="group" name="Second fog">
+				<dl>
+					<dt>Color</dt><dd><input type="color" field="secondFogColor"/></dd>
+					<dt>Density</dt><dd><input type="range" min="0" max="2" field="secondFogDensity"/></dd>
+					<dt>Use noise</dt><dd><input type="range" min="0" max="1" field="secondFogUseNoise"/></dd>
+					<dt>Bottom [m]</dt><dd><input type="range" min="0" max="1000" field="secondFogBottom"/></dd>
+					<dt>Top [m]</dt><dd><input type="range" min="0" max="1000" field="secondFogTop"/></dd>
+					<dt>Height falloff</dt><dd><input type="range" min="0" max="3" field="secondFogHeightFalloff"/></dd>
+				</dl>
+			</div>
+			<div class="group" name="Noise">
+				<dl>
+					<dt><font color=#FF0000>Octaves</font></dt><dd><input type="range" step="1" min="0" max="4" field="noiseOctave"/></dd>
+					<dt>Scale</dt><dd><input type="range" min="0" max="100" field="noiseScale"/></dd>
+					<dt>Turmoil</dt><dd><input type="range" min="0" max="100" field="noiseTurmoil"/></dd>
+					<dt>Persistence</dt><dd><input type="range" min="0" max="1" field="noisePersistence"/></dd>
+					<dt>Lacunarity</dt><dd><input type="range" min="0" max="2" field="noiseLacunarity"/></dd>
+					<dt>Sharpness</dt><dd><input type="range" min="0" max="2" field="noiseSharpness"/></dd>
+				</dl>
+			</div>
+			<div class="group" name="Rendering">
+				<dl>
+					<dt><font color=#FF0000>Steps</font></dt><dd><input type="range" step="1" min="0" max="255" field="steps"/></dd>
+					<dt>Blur</dt><dd><input type="range" step="1" min="0" max="100" field="blur"/></dd>
+					<dt>Blur depth threshold</dt><dd><input type="range" field="blurDepthThreshold"/></dd>
+					<dt>Dithering intensity</dt><dd><input type="range" min="0" max="1" field="ditheringIntensity"/></dd>
+				</dl>
+			</div>
+			'), this, function(pname) {
+				ctx.onChange(this, pname);
+		});
+	}
+
+	#end
+
+	static var _ = Prefab.register("rfx.volumetricLighting", VolumetricLighting);
+
 }