Browse Source

Add LightProbe, and probebaker ( wip )

ShiroSmith 4 years ago
parent
commit
93dd5a9d62

+ 9 - 3
hrt/prefab/l3d/Environment.hx

@@ -138,8 +138,10 @@ class Environment extends Object3D {
 		var lutBytes = bytes.sub(curPos, lutSize);
 		var lutBytes = bytes.sub(curPos, lutSize);
 		curPos += lutBytes.length;
 		curPos += lutBytes.length;
 		if( curPos > bytes.length ) return false;
 		if( curPos > bytes.length ) return false;
-		var lutPixels : hxd.Pixels.PixelsFloat = new hxd.Pixels(env.lut.width, env.lut.height, lutBytes, env.lut.format);
-		env.lut.uploadPixels(lutPixels);
+
+		// TO DO : Remove LUT from save 
+		/*var lutPixels : hxd.Pixels.PixelsFloat = new hxd.Pixels(env.lut.width, env.lut.height, lutBytes, env.lut.format);
+		env.lut.uploadPixels(lutPixels);*/
 
 
 		var diffSize = hxd.Pixels.calcStride(env.diffuse.width, env.diffuse.format) * env.diffuse.height;
 		var diffSize = hxd.Pixels.calcStride(env.diffuse.width, env.diffuse.format) * env.diffuse.height;
 		for( i in 0 ... 6 ) {
 		for( i in 0 ... 6 ) {
@@ -207,6 +209,7 @@ class Environment extends Object3D {
 
 
 		if( env == null ) {
 		if( env == null ) {
 			env = new h3d.scene.pbr.Environment(sourceMap);
 			env = new h3d.scene.pbr.Environment(sourceMap);
+			env.equiToCube();
 			needCompute = true;
 			needCompute = true;
 		}
 		}
 
 
@@ -253,7 +256,10 @@ class Environment extends Object3D {
 		}
 		}
 
 
 		var scene = ctx.local3d.getScene();
 		var scene = ctx.local3d.getScene();
-		if( scene != null ) applyToRenderer(scene.renderer);
+
+		// Auto Apply on change
+		if( scene != null ) 
+			applyToRenderer(scene.renderer);
 	}
 	}
 
 
 	public function applyToRenderer( r : h3d.scene.Renderer) {
 	public function applyToRenderer( r : h3d.scene.Renderer) {

+ 735 - 0
hrt/prefab/vlm/LightProbe.hx

@@ -0,0 +1,735 @@
+package hrt.prefab.vlm;
+
+import hrt.prefab.l3d.Level3D;
+import h3d.scene.pbr.Environment;
+
+@:enum abstract ProbeMode(String) {
+	var Texture = "Texture";
+	var Capture = "Capture";
+}
+
+@:enum abstract ProbeFadeMode(String) {
+	var Linear = "Linear";
+	var Smoothstep = "Smoothstep";
+	var Pow2 = "Pow2";
+}
+
+class DebugView extends hxsl.Shader {
+
+	static var SRC = {
+
+		var pixelColor : Vec4;
+		@param var source : SamplerCube;
+		var transformedNormal : Vec3;
+
+		function fragment() {
+			var color = source.get(transformedNormal).rgb;
+			pixelColor = vec4(color, 1);
+		}
+
+	}
+}
+
+class BoundsFade extends hxsl.Shader {
+
+	static var SRC = {
+		
+		@const var SMOOTHSTEP : Bool;
+		@const var POWER2 : Bool;
+
+		@param var scale : Vec3;
+		@param var fadeDist : Float;
+
+		var pixelRelativePosition : Vec3;
+		var pixelColor : Vec4;
+
+		function fragment() {
+			var normalizedPos = abs(pixelRelativePosition) * 2.0;
+			var maxDist = min(min(scale.x - normalizedPos.x * scale.x, scale.y - normalizedPos.y * scale.y), scale.z - normalizedPos.z * scale.z);
+			var fadeAmount = saturate(maxDist / fadeDist);
+			if( SMOOTHSTEP )
+				fadeAmount = smoothstep(0.0, 1.0, fadeAmount);
+			else if( POWER2 )
+				fadeAmount = fadeAmount * fadeAmount;
+			pixelColor.a *= fadeAmount;
+		}
+	};
+
+}
+
+class BoundsClipping extends hxsl.Shader {
+
+	static var SRC = {
+
+		@global var global : {
+			@perObject var modelViewInverse : Mat4;
+		};
+
+		var transformedPosition : Vec3;
+		var pixelRelativePosition : Vec3;
+
+		function fragment() {
+			pixelRelativePosition = (transformedPosition * global.modelViewInverse.mat3x4()).xyz;
+			if( abs(pixelRelativePosition.x) > 0.5 || abs(pixelRelativePosition.y) > 0.5 || abs(pixelRelativePosition.z) > 0.5 )
+				discard;
+		}
+	};
+
+}
+
+class LightProbeObject extends h3d.scene.Mesh {
+
+	public var env : Environment;
+	public var indirectShader : h3d.shader.pbr.Lighting.Indirect;
+	public var boundClippingShader : BoundsClipping;
+	public var boundFadeShader : BoundsFade;
+	public var fadeDist : Float;
+	public var fadeMode : ProbeFadeMode;
+	public var priority : Int;
+
+	public function new(?parent) {
+		var probeMaterial = h3d.mat.MaterialSetup.current.createMaterial();
+		super(h3d.prim.Cube.defaultUnitCube(), probeMaterial, parent);
+		material.castShadows = false;
+		material.mainPass.setPassName("lightProbe");
+		boundClippingShader = new BoundsClipping();
+		material.mainPass.addShader(boundClippingShader);
+		indirectShader = new h3d.shader.pbr.Lighting.Indirect();
+		indirectShader.drawIndirectDiffuse = true;
+		indirectShader.drawIndirectSpecular = true;
+		indirectShader.showSky = false;
+		indirectShader.gammaCorrect = false;
+		material.mainPass.addShader(indirectShader);
+		material.mainPass.setBlendMode(Alpha);
+		material.mainPass.depthTest = GreaterEqual;
+		material.mainPass.culling = Front;
+		material.mainPass.depthWrite = false;
+
+		var r : h3d.scene.pbr.Renderer = cast getScene().renderer;
+		if( r != null ) {
+			@:privateAccess material.mainPass.addShader(r.pbrProps);
+			var props : h3d.scene.pbr.Renderer.RenderProps = r.props;
+			indirectShader.emissivePower = props.emissive;
+		}
+
+		boundFadeShader = new BoundsFade();
+		material.mainPass.addShader(boundFadeShader);
+	}
+
+	public function clear() {
+		if( env.env != null ) 
+			env.env.clear(0);
+		if( env.diffuse != null )
+			env.diffuse.clear(0);
+		if( env.specular != null ) {
+			env.specular.dispose();
+		 	env.specular.alloc();
+		}
+	}
+
+	override function emit( ctx : h3d.scene.RenderContext ) {
+		if( env == null )
+			return;
+		indirectShader.cameraPosition = ctx.camera.pos;
+		indirectShader.irrLut = env.lut;
+		indirectShader.irrDiffuse = env.diffuse;
+		indirectShader.irrSpecular = env.specular;
+		indirectShader.irrSpecularLevels = env.specLevels;
+		indirectShader.irrPower = env.power * env.power;
+		indirectShader.rot = env.rot;
+		super.emit(ctx);
+	}
+
+	override function sync( ctx : h3d.scene.RenderContext ) {
+		super.sync(ctx);
+		getAbsPos().getScale(boundFadeShader.scale);
+		getAbsPos()._44 = priority;
+		boundFadeShader.fadeDist = fadeDist;
+		switch fadeMode {
+			case Linear:
+				boundFadeShader.POWER2 = false;
+				boundFadeShader.SMOOTHSTEP = false;
+			case Smoothstep:
+				boundFadeShader.POWER2 = false;
+				boundFadeShader.SMOOTHSTEP = true;
+			case Pow2:
+				boundFadeShader.POWER2 = true;
+				boundFadeShader.SMOOTHSTEP = false;
+		}
+	}
+
+}
+
+@:access(h3d.scene.pbr.Environment)
+class LightProbe extends Object3D {
+	
+	// Probe 
+	public var mode : ProbeMode = Texture;
+	public var priority : Int = 0;
+
+	// Fade 
+	public var fadeDist : Float = 0.0;
+	public var fadeMode : ProbeFadeMode = Linear;
+
+	// Texture Mode
+	public var texturePath : String = null;
+	public var threshold : Float = 1.0;
+	public var scale : Float = 1.0;
+	public var rotation : Float = 0.0;
+
+	// Capture Mode
+	public var bounce : Int = 1;
+
+	// Shared 
+	public var power : Float = 1.0;
+	public var sampleBits : Int = 12;
+	public var diffSize : Int = 16;
+	public var specSize : Int = 64;
+	public var ignoredSpecLevels : Int = 2;
+
+	// Debug
+	public var debugDisplay : Bool = true;
+	public var sphereRadius : Float = 0.5;
+
+	public function new( ?parent : Prefab ) {
+		super(parent);
+		type = "lightProbe";
+
+		// Duplicate Name Fix - Prevent baked data conflict
+		var root : Prefab = this;
+		while( root.parent != null ) {
+			root = root.parent;
+		}
+		var probeList : Array<LightProbe> = cast root.findAll( p -> Std.is(p, LightProbe) ? p : null );
+		var curIndex = 0;
+		var needCheck = true;
+		while( needCheck ) {
+			needCheck = false;
+			for( p in probeList ) {
+				if( p.name != null && p.name.indexOf("_" + curIndex) != -1 ) {
+					curIndex++;
+					needCheck = true;
+					continue;
+				}
+			}
+		}
+		name = "lightProbe_" + curIndex;
+	}
+
+	override function load( obj : Dynamic ) {
+		super.load(obj);
+		if( obj.mode != null ) mode = obj.mode;
+		if( obj.bounce != null ) bounce = obj.bounce;
+		if( obj.priority != null ) priority = obj.priority;
+		if( obj.texturePath != null) texturePath = obj.texturePath;
+		if( obj.fadeDist != null ) fadeDist = obj.fadeDist;
+		if( obj.fadeMode != null ) fadeMode = obj.fadeMode;
+		if( obj.power != null ) power = obj.power;
+		if( obj.threshold != null ) threshold = obj.threshold;
+		if( obj.scale != null ) scale = obj.scale;
+		if( obj.rotation != null ) rotation = obj.rotation;
+		if( obj.sampleBits != null ) sampleBits = obj.sampleBits;
+		if( obj.diffSize != null ) diffSize = obj.diffSize;
+		if( obj.specSize != null ) specSize = obj.specSize;
+		if( obj.ignoredSpecLevels != null ) ignoredSpecLevels = obj.ignoredSpecLevels;
+		if( obj.debugDisplay != null ) debugDisplay = obj.debugDisplay;
+		if( obj.sphereRadius != null ) sphereRadius = obj.sphereRadius;
+	}
+
+	override function save() {
+		var o : Dynamic = super.save();
+		o.mode = mode;
+		o.bounce = bounce;
+		o.priority = priority;
+		o.texturePath = texturePath;
+		o.fadeDist = fadeDist;
+		o.fadeMode = fadeMode;
+		o.power = power;
+		o.threshold = threshold;
+		o.scale = scale;
+		o.rotation = rotation;
+		o.sampleBits = sampleBits;
+		o.diffSize = diffSize;
+		o.specSize = specSize;
+		o.ignoredSpecLevels = ignoredSpecLevels;
+		o.debugDisplay = debugDisplay;
+		o.sphereRadius = sphereRadius;
+		return o;
+	}
+
+	override function makeInstance( ctx : Context ) : Context {
+		ctx = ctx.clone(this);
+		var lpo = new LightProbeObject(ctx.local3d);
+		lpo.material.castShadows = false;
+		lpo.material.mainPass.setPassName("lightProbe");
+		lpo.ignoreCollide = true;
+		ctx.local3d = lpo;
+		ctx.local3d.name = name;
+
+		#if editor
+		var wire = new h3d.scene.Box(lpo);
+		wire.thickness = 2.0;
+		wire.material.mainPass.setPassName("overlay");
+		wire.name = "wire_select";
+		wire.color = 0xFFFFFF;
+		wire.ignoreCollide = true;
+		wire.material.shadows = false;
+		wire.visible = false;
+		wire.material.mainPass.depthTest = Always;
+
+		var previewSphereDiffuse = new h3d.scene.Mesh(h3d.prim.Sphere.defaultUnitSphere(), lpo);
+		previewSphereDiffuse.name = "preview_sphere_diffuse";
+		previewSphereDiffuse.material.mainPass.setPassName("overlay");
+		previewSphereDiffuse.material.mainPass.addShader(new DebugView());
+		previewSphereDiffuse.material.castShadows = false;
+
+		var previewSphereSpecular = new h3d.scene.Mesh(h3d.prim.Sphere.defaultUnitSphere(), lpo);
+		previewSphereSpecular.name = "preview_sphere_specular";
+		previewSphereSpecular.material.mainPass.setPassName("overlay");
+		previewSphereSpecular.material.mainPass.addShader(new DebugView());
+		previewSphereSpecular.material.castShadows = false;
+		#end
+
+		updateInstance(ctx, texturePath == null ? null : "texturePath");
+		return ctx;
+	}
+
+	override function updateInstance( ctx : Context, ?propName : String ) {
+		super.updateInstance(ctx, propName);
+		var lpo : LightProbeObject = cast ctx.local3d;
+		lpo.fadeDist = fadeDist;
+		lpo.fadeMode = fadeMode;
+		lpo.priority = priority;
+
+		// Full Reset 
+		if( propName == "mode" ) {
+			if( lpo.env != null ) {
+				lpo.env.dispose();
+				lpo.env = null;
+			}
+		}
+
+		switch mode {
+			case Texture:
+				var needCompute = false;
+				if( propName == "texturePath" || propName == "mode" ) {
+					var t = texturePath == null ? null : ctx.loadTexture(texturePath);
+					if( t != null ) {
+						lpo.env = new Environment(t);
+						needCompute = true;
+					}
+				}
+				if( lpo.env != null ) {
+					lpo.env.power = power;
+					lpo.env.threshold = threshold;
+					lpo.env.scale = scale;
+					lpo.env.rot = hxd.Math.degToRad(rotation);
+					lpo.env.sampleBits = sampleBits;
+					lpo.env.ignoredSpecLevels = ignoredSpecLevels;
+					if( lpo.env.specSize != specSize ) {
+						if( lpo.env.specular != null ) lpo.env.specular.dispose();
+						lpo.env.specular = null;
+						needCompute = true;
+					}
+					lpo.env.specSize = specSize;
+					if( lpo.env.diffSize != diffSize ) {
+						if( lpo.env.diffuse != null ) lpo.env.diffuse.dispose();
+						lpo.env.diffuse = null;
+						needCompute = true;
+					}
+					lpo.env.diffSize = diffSize;
+
+					if( propName == "threshold" || propName == "scale" || 
+						propName == "sampleBits" || propName == "ignoredSpecLevels" )
+						needCompute = true;
+
+					if( needCompute ) {
+						if( lpo.env.source.flags.has(Loading) )
+							lpo.env.source.waitLoad(lpo.env.compute);
+						else
+							lpo.env.compute();
+					}
+				}
+
+			case Capture:
+
+				var needCompute = false;
+
+				if( lpo.env == null )
+					lpo.env = new Environment(null);
+
+				lpo.env.power = power;
+				lpo.env.ignoredSpecLevels = ignoredSpecLevels;
+
+				if( propName == "threshold" || propName == "scale" || 
+					propName == "sampleBits" || propName == "ignoredSpecLevels" )
+					needCompute = true;
+
+				var res = ctx.shared.loadPrefabDat("probe", "bake", name);
+				if( res != null ) {
+					loadBinary(lpo.env, res.entry.getBytes());
+					needCompute = false; // No Env available with binary load, everything else is already baked
+				}
+				
+				if( needCompute )
+					lpo.env.compute();
+		}
+
+		updatePreviewSphere(lpo);
+	}
+
+	function updatePreviewSphere( o : h3d.scene.Object ) {
+		#if editor
+		var lpo : LightProbeObject = cast o;
+		var previewSphereDiffuse : h3d.scene.Mesh = Std.downcast(lpo.find( o -> o.name == "preview_sphere_diffuse" ? o : null), h3d.scene.Mesh);
+		var previewSphereSpecular : h3d.scene.Mesh = Std.downcast(lpo.find( o -> o.name == "preview_sphere_specular" ? o : null), h3d.scene.Mesh);
+		var parentScale = lpo.getAbsPos().getScale();
+
+		// Don't use scale from parent for preview phere 
+		function updateScale( m : h3d.scene.Mesh ) {
+			m.scaleX = sphereRadius / parentScale.x;
+			m.scaleY = sphereRadius / parentScale.y;
+			m.scaleZ = sphereRadius / parentScale.z;
+		}
+
+		if( previewSphereDiffuse != null ) {
+			previewSphereDiffuse.visible = debugDisplay;
+			previewSphereDiffuse.x = (sphereRadius + (sphereRadius * 0.5)) / parentScale.x;
+			var s = previewSphereDiffuse.material.mainPass.getShader(DebugView);
+			if( lpo.env != null ) {
+				if( lpo.env.source != null && lpo.env.source.flags.has(Loading) )
+					lpo.env.source.waitLoad( () ->  s.source = lpo.env.diffuse );
+				else 
+					s.source = lpo.env.diffuse; 
+			}
+			updateScale(previewSphereDiffuse);
+		}
+		if( previewSphereSpecular != null ) {
+			previewSphereSpecular.visible = debugDisplay;
+			previewSphereSpecular.x = -(sphereRadius + (sphereRadius * 0.5)) / parentScale.x;
+			var s = previewSphereSpecular.material.mainPass.getShader(DebugView);
+			if( lpo.env != null ) {
+				if( lpo.env.source != null && lpo.env.source.flags.has(Loading) )
+					lpo.env.source.waitLoad( () -> s.source = lpo.env.specular );
+				else 
+					s.source = lpo.env.specular; 
+			}
+			updateScale(previewSphereSpecular);
+		}
+		#end
+	}
+
+	override function applyTransform( o : h3d.scene.Object ) {
+		super.applyTransform(o);
+		updatePreviewSphere(o);
+	}
+
+	function serialize( env : Environment ) : haxe.io.Bytes {
+
+		var diffusePixels : Array<hxd.Pixels.PixelsFloat> = [ for( i in 0 ... 6) env.diffuse.capturePixels(i) ];
+		var mipLevels = env.getMipLevels();
+		var specularPixels : Array<hxd.Pixels.PixelsFloat> =
+		 [
+			for( i in 0 ... 6 ) {
+				for( m in 0 ... mipLevels ) {
+					env.specular.capturePixels(i, m);
+				}
+			}
+		];
+
+		var totalBytes = 0;
+		totalBytes += 4 + 4 + 4 + 4 + 8 + 8; // diffSize + specSize + ignoredSpecLevels + sampleBits + threshold + scale
+		for( p in diffusePixels )
+			totalBytes += p.bytes.length;
+		for( p in specularPixels )
+			totalBytes += p.bytes.length;
+
+		var bytes = haxe.io.Bytes.alloc(totalBytes);
+
+		var curPos = 0;
+		bytes.setInt32(curPos, env.sampleBits); 		curPos += 4;
+		bytes.setInt32(curPos, env.diffSize); 			curPos += 4;
+		bytes.setInt32(curPos, env.specSize); 			curPos += 4;
+		bytes.setInt32(curPos, env.ignoredSpecLevels); 	curPos += 4;
+		bytes.setDouble(curPos, env.threshold); 		curPos += 8;
+		bytes.setDouble(curPos, env.scale);			 	curPos += 8;
+
+		for( p in diffusePixels ) {
+			bytes.blit(curPos, p.bytes, 0, p.bytes.length);
+			curPos += p.bytes.length;
+		}
+
+		for( p in specularPixels ) {
+			bytes.blit(curPos, p.bytes, 0, p.bytes.length);
+			curPos += p.bytes.length;
+		}
+
+		return bytes;
+	}
+
+	function loadBinary( env : Environment, bytes : haxe.io.Bytes ) {
+
+		var curPos = 0;
+
+		env.sampleBits = bytes.getInt32(curPos); 		curPos += 4;
+		env.diffSize = bytes.getInt32(curPos); 			curPos += 4;
+		env.specSize = bytes.getInt32(curPos); 			curPos += 4;
+		env.ignoredSpecLevels = bytes.getInt32(curPos); curPos += 4;
+		env.threshold = bytes.getDouble(curPos); 		curPos += 8;
+		env.scale = bytes.getDouble(curPos); 			curPos += 8;
+
+		env.createTextures();
+
+		var diffSize = hxd.Pixels.calcStride(env.diffuse.width, env.diffuse.format) * env.diffuse.height;
+		for( i in 0 ... 6 ) {
+			var diffByte = bytes.sub(curPos, diffSize);
+			curPos += diffByte.length;
+			var diffPixels : hxd.Pixels.PixelsFloat = new hxd.Pixels(env.diffuse.width, env.diffuse.height, diffByte, env.diffuse.format);
+			env.diffuse.uploadPixels(diffPixels, 0, i);
+		}
+
+		var mipLevels = env.getMipLevels();
+		env.specLevels = mipLevels - ignoredSpecLevels;
+		for( i in 0 ... 6 ) {
+			for( m in 0 ... mipLevels ) {
+				var mipMapSize = hxd.Pixels.calcStride(env.specular.width >> m, env.specular.format) * env.specular.height >> m;
+				var specByte = bytes.sub(curPos, mipMapSize);
+				curPos += specByte.length;
+				var specPixels : hxd.Pixels.PixelsFloat = new hxd.Pixels(env.specular.width >> m, env.specular.height >> m, specByte, env.specular.format);
+				env.specular.uploadPixels(specPixels, m, i);
+			}
+		}
+	}
+
+	#if editor
+
+	override function getHideProps() : HideProps {
+		return { icon : "map-o", name : "LightProbe" };
+	}
+
+	override function setSelected( ctx : Context, b : Bool ) {
+		var w = ctx.local3d.find( o -> o.name == "wire_select" ? o : null);
+		if( w != null )
+			w.visible = b;
+		return true;
+	}
+
+	override function edit( ctx : EditContext ) {
+		super.edit(ctx);
+
+		var captureModeParams = 
+		'<div class="group" name="Environment" >
+			<dt>Power</dt><dd><input type="range" min="0" max="10" field="power"/></dd>
+			<dt>Bounce</dt><dd><input type="range" min="1" max="3" step="1" field="bounce"/></dd>
+			<br>
+			<div align="center">
+				<input type="button" value="Bake" class="bake" />
+			</div>
+			<div align="center">
+				<input type="button" value="Clear" class="clear" />
+			</div>
+			<br>
+			<div align="center">
+				<input type="button" value="Export" class="export" />
+			</div>
+			<div align="center">
+				<input type="button" value="Import" class="import" />
+			</div>
+			<br>
+		</div>
+		<div class="group" name="Resolution">
+			<dl>
+				<dt>Diffuse</dt><dd><input type="range" min="1" max="512" step="1" field="diffSize"/></dd>
+				<dt>Specular</dt><dd><input type="range" min="1" max="2048" step="1" field="specSize"/></dd>
+				<dt>Sample Count</dt><dd><input type="range" min="1" max="12" step="1" field="sampleBits"/></dd>
+				<dt>Ignored Spec Levels</dt><dd><input type="range" min="0" max="3" step="1" field="ignoredSpecLevels"/></dd>
+			</dl>
+		</div>';
+
+		var textureModeParams = 
+		'<div class="group" name="Environment">
+			<dl>
+				<dt>Texture</dt><dd><input type="texturepath" field="texturePath"/></dd>
+				<dt>Rotation</dt><dd><input type="range" min="0" max="360" field="rotation"/></dd>
+				<dt>Power</dt><dd><input type="range" min="0" max="10" field="power"/></dd>
+			</dl>
+		</div>
+		<div class="group" name="Resolution">
+			<dl>
+				<dt>Diffuse</dt><dd><input type="range" min="1" max="512" step="1" field="diffSize"/></dd>
+				<dt>Specular</dt><dd><input type="range" min="1" max="2048" step="1" field="specSize"/></dd>
+				<dt>Sample Count</dt><dd><input type="range" min="1" max="12" step="1" field="sampleBits"/></dd>
+				<dt>Ignored Spec Levels</dt><dd><input type="range" min="0" max="3" step="1" field="ignoredSpecLevels"/></dd>
+			</dl>
+		</div>
+		<div class="group" name="HDR">
+			<dl>
+				<dt>Threshold</dt><dd><input type="range" min="0" max="1" step="0.1" field="threshold"/></dd>
+				<dt>Scale</dt><dd><input type="range" min="0" max="10" field="scale"/></dd>
+			</dl>
+		</div>';
+
+		var props = new hide.Element('
+			<div class="group" name="Probe">
+				<dl>
+					<dt>Mode</dt>
+					<dd>
+						<select field="mode">
+							<option value="Texture">Texture</option>
+							<option value="Capture">Capture</option>
+						</select>
+					</dd>
+					<dt>Priority</dt><dd><input type="range" min="0" max="10" step="1" field="priority"/></dd>
+				</dl>
+			</div>
+			' + (mode == Texture ? textureModeParams : captureModeParams) + '
+			<div class="group" name="Fade">
+				<dl>
+					<dt>Mode</dt>
+						<dd>
+							<select field="fadeMode">
+								<option value="Linear">Linear</option>
+								<option value="Smoothstep">SmoothStep</option>
+								<option value="Pow2">Power</option>
+							</select>
+						</dd>
+					<dt>Distance</dt><dd><input type="range" min="0" max="10" field="fadeDist"/></dd>
+				</dl>
+			</div>
+			<div class="group" name="Debug">
+				<dl>
+					<dt>Debug Display</dt><dd><input type="checkbox" field="debugDisplay"/></dd>
+					<dt>Sphere Radius</dt><dd><input type="range" min="0.1" max="4" field="sphereRadius"/></dd>
+				</dl>
+			</div>
+		');
+		ctx.properties.add(props, this, function(pname) {
+			ctx.onChange(this, pname);
+			if( pname == "mode" )
+				ctx.rebuildProperties();
+		});
+
+		var clearButton = props.find(".clear");
+		if( clearButton != null ) {
+			clearButton.click(function(_) {
+				var lpo : LightProbeObject = cast ctx.getContext(this).local3d;
+				lpo.env.createTextures();
+				lpo.clear();
+				ctx.properties.undo.change(Custom(function(undo) {
+					// TO DO
+				}));
+			});
+		}
+
+		var exportButton = props.find(".export");
+		if( exportButton != null ) {
+			exportButton.click(function(_) {
+
+				var lpo : LightProbeObject = cast ctx.getContext(this).local3d;
+				if( lpo.env == null || lpo.env.specular == null || lpo.env.diffuse == null ) {
+					hide.Ide.inst.message("Capture is empty.");
+					return;
+				}
+
+				var data = serialize(lpo.env);
+				function saveData( name : String ) {
+					var path = ctx.ide.getPath(name)+"/"+this.name+"_export.bake";
+					sys.io.File.saveBytes(path, data);
+				}
+				ctx.ide.chooseDirectory(saveData);
+
+			});
+		}
+
+		var importButton = props.find(".import");
+		if( importButton != null ) {
+			importButton.click(function(_) {
+				
+				var lpo : LightProbeObject = cast ctx.getContext(this).local3d;
+
+				function loadData( name : String ) {
+
+					if( name == null || name == "null" )
+						return;
+
+					var b = hxd.res.Loader.currentInstance.load(name).entry.getBytes();
+
+					if( lpo.env != null )
+						lpo.env.dispose();
+
+					lpo.env = new Environment(null);
+					loadBinary(lpo.env, b);
+					
+					// Upate the prefab
+					sampleBits = lpo.env.sampleBits;
+					diffSize = lpo.env.diffSize;
+					specSize = lpo.env.specSize;
+					ignoredSpecLevels = lpo.env.ignoredSpecLevels;
+					threshold = lpo.env.threshold;
+					scale = lpo.env.scale;
+
+					// Save the import
+					ctx.rootContext.shared.savePrefabDat("probe", "bake", this.name, b);
+
+					ctx.onChange(this, null);
+					ctx.rebuildProperties();
+				}
+
+				ctx.ide.chooseFile(["bake"], loadData);
+				
+				ctx.properties.undo.change(Custom(function(undo) {
+					// TO DO
+				}));
+
+			});
+		}
+	
+		var bakeButton = props.find(".bake");
+		if( bakeButton != null ) {
+			bakeButton.click(function(_) {
+
+				var lpo : LightProbeObject = cast ctx.getContext(this).local3d;
+				var captureSize = specSize;
+
+				// Start with a black texture, need to override the default env
+				lpo.env.createTextures();
+				lpo.clear();
+
+				if( lpo.env.env == null || lpo.env.env.width != captureSize ) {
+					if( lpo.env.env != null )
+						lpo.env.env.dispose();
+					lpo.env.env = new h3d.mat.Texture(captureSize, captureSize, [Cube, Target]);
+				}
+
+				var probeBaker = new ProbeBaker();
+				if( bounce > 1 ) {
+					var tmpTexture = new h3d.mat.Texture(captureSize, captureSize, [Cube, Target]);
+					var curCapture : h3d.mat.Texture = tmpTexture;
+					for( b in 0 ... bounce ) {
+						probeBaker.captureEnvironment(lpo.getAbsPos().getPosition(), captureSize, ctx.scene.s3d, curCapture);
+						var tmp = lpo.env.env;
+						lpo.env.env = curCapture;
+						lpo.env.compute();
+						curCapture = tmp;
+					}
+					curCapture.dispose();
+				}
+				else {
+					probeBaker.captureEnvironment(lpo.getAbsPos().getPosition(), captureSize, ctx.scene.s3d, lpo.env.env);
+					lpo.env.compute();
+				}
+
+				probeBaker.dispose();
+
+				var bytes = serialize(lpo.env);
+				ctx.rootContext.shared.savePrefabDat("probe", "bake", name, bytes);
+
+				ctx.onChange(this, null);
+			});
+		}
+	}
+
+	#end
+
+	static var _ = Library.register("lightProbe", LightProbe);
+}

+ 315 - 0
hrt/prefab/vlm/ProbeBaker.hx

@@ -0,0 +1,315 @@
+package hrt.prefab.vlm;
+
+import hrt.prefab.vlm.LightProbe.LightProbeObject;
+
+class ProbeBaker {
+
+	public var useGPU = false;
+
+	// Tmp cube texture for rendring environment
+	var envMap : h3d.mat.Texture;
+
+	// Params for CubeMap rendering
+	var customCamera : h3d.Camera;
+	var cubeDir = [ h3d.Matrix.L([0,0,-1,0, 0,1,0,0, -1,-1,1,0]),
+					h3d.Matrix.L([0,0,1,0, 0,1,0,0, 1,-1,-1,0]),
+	 				h3d.Matrix.L([-1,0,0,0, 0,0,1,0, 1,-1,-1,0]),
+	 				h3d.Matrix.L([-1,0,0,0, 0,0,-1,0, 1,1,1,0]),
+				 	h3d.Matrix.L([-1,0,0,0, 0,1,0,0, 1,-1,1,0]),
+				 	h3d.Matrix.L([1,0,0,0, 0,1,0,0, -1,-1,-1,0]) ];
+
+	// Output for SH computation on GPU
+	var computeSH : h3d.pass.ScreenFx<hrt.shader.ComputeSH>;
+	var output0 : h3d.mat.Texture;
+	var output1 : h3d.mat.Texture;
+	var output2 : h3d.mat.Texture;
+	var output3 : h3d.mat.Texture;
+	var output4 : h3d.mat.Texture;
+	var output5 : h3d.mat.Texture;
+	var output6 : h3d.mat.Texture;
+	var output7 : h3d.mat.Texture;
+	var textureArray: Array<h3d.mat.Texture>;
+	
+	// Tmp Buffer
+	var pixels : hxd.Pixels.PixelsFloat = null;
+	var prim : h3d.prim.Plane2D;
+
+	public function new(){
+		customCamera = new h3d.Camera();
+		customCamera.screenRatio = 1.0;
+		customCamera.fovY = 90;
+		customCamera.zNear = 0.01;
+		customCamera.zFar = 4000;
+	}
+
+	public function dispose() {
+		if( envMap != null ) {
+			envMap.dispose();
+			envMap = null;
+		}
+		if(output0 != null) output0.dispose();
+		if(output1 != null) output1.dispose();
+		if(output2 != null) output2.dispose();
+		if(output3 != null) output3.dispose();
+		if(output4 != null) output4.dispose();
+		if(output5 != null) output5.dispose();
+		if(output6 != null) output6.dispose();
+		if(output7 != null) output7.dispose();
+		if(prim != null) prim.dispose();
+	}
+
+	public function captureEnvironment( position : h3d.Vector, resolution : Int, scene : h3d.scene.Scene, output : h3d.mat.Texture ) {
+		var pbrRenderer = Std.downcast(scene.renderer, h3d.scene.pbr.Renderer);
+
+		var prevCam = scene.camera;
+		var prevRenderMode = pbrRenderer.renderMode;
+		var prevSkyMode = pbrRenderer.skyMode;
+		pbrRenderer.renderMode = LightProbe;
+		scene.camera = customCamera;
+		pbrRenderer.skyMode = Env;
+
+		setupEnvMap(resolution);
+
+		// Bake a Probe
+		var engine = h3d.Engine.getCurrent();
+		for( f in 0 ... 6 ) {
+			engine.begin();
+			customCamera.setCubeMap(f, position);
+			customCamera.update();
+			scene.camera = customCamera;
+			engine.pushTarget(output, f);
+			engine.clear(0,1,0);
+			scene.render(engine);
+			engine.popTarget();
+		}
+
+		scene.camera = prevCam;
+		pbrRenderer.renderMode = prevRenderMode;
+		pbrRenderer.skyMode = prevSkyMode;
+	}
+
+	function setupEnvMap( resolution : Int ) {
+		if( envMap == null || resolution != envMap.width || resolution != envMap.height ) {
+			if( envMap != null ) envMap.dispose();
+			envMap = new h3d.mat.Texture(resolution, resolution, [Cube, Target], RGBA32F);
+		}
+	}
+
+	function setupShaderOutput( order : Int, probeCount: Int ) {
+
+		if( order > 3 || order <= 0 ){ throw "Not Supported"; return; }
+
+		if( !useGPU )
+			probeCount = 1;
+
+		if( order >= 1 && (output0 == null || output0.width != probeCount)){
+			if(output0 != null) output0.dispose();
+			output0 = new h3d.mat.Texture(probeCount, 1, [Target], RGBA32F);
+		}
+		if( order >= 2 && (output1 == null || output1.width != probeCount)){
+			if(output1 != null) output1.dispose();
+			if(output2 != null) output2.dispose();
+			if(output3 != null) output3.dispose();
+			output1 = new h3d.mat.Texture(probeCount, 1, [Target], RGBA32F);
+			output2 = new h3d.mat.Texture(probeCount, 1, [Target], RGBA32F);
+			output3 = new h3d.mat.Texture(probeCount, 1, [Target], RGBA32F);
+		}
+		if( order >= 3 && (output4 == null || output4.width != probeCount)){
+			if(output4 != null) output4.dispose();
+			if(output5 != null) output5.dispose();
+			if(output6 != null) output6.dispose();
+			if(output7 != null) output7.dispose();
+			output4 = new h3d.mat.Texture(probeCount, 1, [Target], RGBA32F);
+			output5 = new h3d.mat.Texture(probeCount, 1, [Target], RGBA32F);
+			output6 = new h3d.mat.Texture(probeCount, 1, [Target], RGBA32F);
+			output7 = new h3d.mat.Texture(probeCount, 1, [Target], RGBA32F);
+		}
+
+		switch(order){
+			case 1:
+				textureArray = [output0];
+				computeSH = new h3d.pass.ScreenFx(new hrt.shader.ComputeSH(),[
+								Vec4([Value("out.coefL00", 3), Const(0)])]);
+			case 2:
+				textureArray = [output0, output1, output2, output3];
+				computeSH = new h3d.pass.ScreenFx(new hrt.shader.ComputeSH(),[
+							Vec4([Value("out.coefL00", 3), Const(0)]),
+							Vec4([Value("out.coefL1n1", 3), Const(0)]),
+							Vec4([Value("out.coefL10", 3), Const(0)]),
+							Vec4([Value("out.coefL11", 3), Const(0)])]);
+
+			case 3:
+				function getSwiz(name,comp) : hxsl.Output { return Swiz(Value(name,3),[comp]); }
+				textureArray = [output0, output1, output2, output3, output4, output5, output6, output7];
+				computeSH = new h3d.pass.ScreenFx(new hrt.shader.ComputeSH(),[
+							Vec4([Value("out.coefL00", 3), getSwiz("out.coefL22",X) ]),
+							Vec4([Value("out.coefL1n1", 3), getSwiz("out.coefL22",Y) ]),
+							Vec4([Value("out.coefL10", 3), getSwiz("out.coefL22",Z) ]),
+							Vec4([Value("out.coefL11", 3), Const(0)]),
+							Vec4([Value("out.coefL2n2", 3), Const(0)]),
+							Vec4([Value("out.coefL2n1", 3), Const(0)]),
+							Vec4([Value("out.coefL20", 3), Const(0)]),
+							Vec4([Value("out.coefL21", 3), Const(0)])]);
+		}
+	}
+
+	function drawSHIntoTexture(renderer : h3d.scene.Renderer, env : h3d.mat.Texture, order : Int, probeIndex: Int) {
+		if( prim == null ){
+			prim = new h3d.prim.Plane2D();
+		}
+
+		function setPrimPos( index : Int ){
+			var v = new hxd.FloatBuffer();
+			var pixelSize = (1.0 / output0.width);
+			var translation =  pixelSize * index * 2;
+			var posX = -1.0 + translation;
+			v.push( posX ) ; v.push( -1 ); v.push(0); v.push(0);
+			v.push( posX ); v.push( 1 ); v.push(0); v.push(0);
+			v.push( posX + pixelSize * 2 ); v.push( -1 ); v.push(0); v.push(0);
+			v.push( posX + pixelSize * 2 ); v.push( 1 ); v.push(0); v.push(0);
+			prim.buffer = h3d.Buffer.ofFloats(v, 4, [Quads, RawFormat]);
+		}
+		setPrimPos(probeIndex);
+
+		@:privateAccess renderer.ctx.engine = h3d.Engine.getCurrent();
+		@:privateAccess renderer.setTargets(textureArray);
+		computeSH.shader.ORDER = order;
+		computeSH.shader.width = env.width;
+		computeSH.shader.environment = env;
+		computeSH.shader.cubeDir = cubeDir;
+		computeSH.primitive = prim;
+		computeSH.render();
+		@:privateAccess renderer.resetTarget();
+		@:privateAccess renderer.ctx.engine.flushTarget();
+	}
+
+	function convertOuputTexturesIntoSH( volumetricLightMap : hrt.prefab.vlm.VolumetricMesh, pixelsOut : hxd.Pixels.PixelsFloat ) {
+
+		var order = volumetricLightMap.shOrder;
+		var sh = new hrt.prefab.vlm.SphericalHarmonic(order);
+		var coefCount = order * order;
+		var maxCoef : Int = Std.int(Math.min(8, coefCount));
+
+		for(coef in 0 ... maxCoef){
+			var pixels : hxd.Pixels.PixelsFloat = textureArray[coef].capturePixels();
+			for( index in 0 ... pixels.width){
+				var coefs : h3d.Vector = pixels.getPixelF(index, 0);
+				var coords = volumetricLightMap.getProbeCoords(index);
+				var u = coords.x + volumetricLightMap.probeCount.x * coef;
+				var v = coords.y + coords.z * volumetricLightMap.probeCount.y;
+				pixelsOut.setPixelF(u, v, new h3d.Vector(coefs.r, coefs.g, coefs.b, 0));
+
+				// Last coefs is inside the alpha channel
+				if( order == 3 ){
+					var u = coords.x + volumetricLightMap.probeCount.x * 8;
+					var v = coords.y + coords.z * volumetricLightMap.probeCount.y;
+					var prev = pixelsOut.getPixelF(u, v);
+					if( coef == 0 ){ prev.r = coefs.a; }
+					if( coef == 1 ){ prev.g = coefs.a; }
+					if( coef == 2 ){ prev.b = coefs.a; }
+					pixelsOut.setPixelF(u, v, prev);
+				}
+			}
+		}
+	}
+
+	function convertEnvIntoSH_CPU( env : h3d.mat.Texture, order : Int ) : hrt.prefab.vlm.SphericalHarmonic {
+		var coefCount = order * order;
+		var sphericalHarmonic = new hrt.prefab.vlm.SphericalHarmonic(order);
+		var face : hxd.Pixels.PixelsFloat;
+		var weightSum = 0.0;
+		var invWidth = 1.0 / env.width;
+		var shData : Array<Float> = [for ( value in 0...coefCount ) 0];
+
+		for( f in 0...6 ) {
+			face = env.capturePixels(f, 0);
+			for (u in 0...face.width) {
+				var fU : Float = (u / face.width ) * 2 - 1;// Texture coordinate U in range [-1 to 1]
+				fU *= fU;
+				var uCoord = 2.0 * u * invWidth + invWidth;
+				for (v in 0...face.width) {
+        			var fV : Float = (v / face.height ) * 2 - 1;// Texture coordinate V in range [-1 to 1]
+					fV *= fV;
+					var vCoord = 2.0 * v * invWidth + invWidth;
+					var dir = getDir(uCoord, vCoord, f);// Get direction from center of cube texture to current texel
+           			var diffSolid = 4.0 / ((1.0 + fU + fV) * Math.sqrt(1.0 + fU + fV));	// Scale factor depending on distance from center of the face
+					weightSum += diffSolid;
+					var color = face.getPixelF(u,v);// Get color from the current face
+					evalSH(order, dir, shData);// Calculate coefficients of spherical harmonics for current direction
+					for(i in 0...coefCount){
+						sphericalHarmonic.coefR[i] += shData[i] * color.r * diffSolid;
+						sphericalHarmonic.coefG[i] += shData[i] * color.g * diffSolid;
+						sphericalHarmonic.coefB[i] += shData[i] * color.b * diffSolid;
+					}
+				}
+			}
+		}
+		// Final scale for coefficients
+		var normProj = (4.0 * Math.PI) / weightSum;
+		for( i in 0...coefCount ){
+			sphericalHarmonic.coefR[i] *= normProj;
+			sphericalHarmonic.coefG[i] *= normProj;
+			sphericalHarmonic.coefB[i] *= normProj;
+		}
+		return sphericalHarmonic;
+	}
+
+	inline function evalSH( order : Int, dir : h3d.Vector, shData : Array<Float> ) {
+		for (l in 0...order) {
+       		for (m in -l...l+1) {
+				shData[getIndex(l, m)] = evalCoef(l, m, dir);
+			}
+		}
+	}
+
+	inline function getIndex( l : Int, m :Int ) : Int {
+		return l * (l + 1) + m;
+	}
+
+	inline function getDir( u : Float, v : Float, face : Int ) : h3d.Vector {
+		var dir = new h3d.Vector();
+		switch( face ) {
+			case 0: dir.x = -1.0; dir.y = -1.0 + v; dir.z = 1.0 - u;
+			case 1: dir.x = 1.0; dir.y = -1.0 + v; dir.z = -1.0 + u;
+			case 2: dir.x = 1.0 - u; dir.y = -1.0; dir.z = -1.0 + v;
+			case 3: dir.x = 1.0 - u; dir.y = 1.0; dir.z = 1.0 - v;
+			case 4: dir.x = 1.0 - u;  dir.y = -1.0 + v; dir.z = 1.0;
+			case 5: dir.x = -1.0 + u; dir.y = -1.0 + v;  dir.z = -1.0;
+			default:
+		}
+		dir.normalizeFast();
+		return dir;
+	}
+
+	inline function evalCoef( l : Int, m : Int, dir : h3d.Vector ) : Float {
+		// Coef from Stupid Spherical Harmonics (SH) Peter-Pike Sloan Microsoft Corporation
+		return switch [l,m] {
+			case[0,0]:	0.282095; // 0.5 * sqrt(1/pi)
+			case[1,-1]:	-0.488603 * dir.y;  // -sqrt(3/(4pi)) * y
+			case[1,0]:	0.488603 * dir.z; // sqrt(3/(4pi)) * z
+			case[1,1]:	-0.488603 * dir.x; // -sqrt(3/(4pi)) * x
+			case[2,-2]:	1.092548 * dir.y * dir.x;// 0.5 * sqrt(15/pi) * y * x
+			case[2,-1]:	-1.092548 * dir.y * dir.z; // -0.5 * sqrt(15/pi) * y * z
+			case[2,0]:	0.315392 * (-dir.x * dir.x - dir.y * dir.y + 2.0 * dir.z * dir.z);// 0.25 * sqrt(5/pi) * (-x^2-y^2+2z^2)
+			case[2,1]:  -1.092548 * dir.x * dir.z; // -0.5 * sqrt(15/pi) * x * z
+			case[2,2]:	0.546274 * (dir.x * dir.x - dir.y * dir.y); // 0.25 * sqrt(15/pi) * (x^2 - y^2)
+			case[3,-3]:	-0.590044 * dir.y * (3.0 * dir.x * dir.x - dir.y * dir.y);// -0.25 * sqrt(35/(2pi)) * y * (3x^2 - y^2)
+			case[3,-2]: 2.890611 * dir.x * dir.y * dir.z; // 0.5 * sqrt(105/pi) * x * y * z
+			case[3,-1]: -0.457046 * dir.y * (4.0 * dir.z * dir.z - dir.x * dir.x - dir.y * dir.y); // -0.25 * sqrt(21/(2pi)) * y * (4z^2-x^2-y^2)
+			case[3,0]:  0.373176 * dir.z * (2.0 * dir.z * dir.z - 3.0 * dir.x * dir.x - 3.0 * dir.y * dir.y);  // 0.25 * sqrt(7/pi) * z * (2z^2 - 3x^2 - 3y^2)
+			case[3,1]:	-0.457046 * dir.x * (4.0 * dir.z * dir.z - dir.x * dir.x - dir.y * dir.y); // -0.25 * sqrt(21/(2pi)) * x * (4z^2-x^2-y^2)
+			case[3,2]:  1.445306 * dir.z * (dir.x * dir.x - dir.y * dir.y); // 0.25 * sqrt(105/pi) * z * (x^2 - y^2)
+			case[3,3]:	-0.590044 * dir.x * (dir.x * dir.x - 3.0 * dir.y * dir.y); // -0.25 * sqrt(35/(2pi)) * x * (x^2-3y^2)
+			case[4,-4]: 2.503343 * dir.x * dir.y * (dir.x * dir.x - dir.y * dir.y);// 0.75 * sqrt(35/pi) * x * y * (x^2-y^2)
+			case[4,-3]: -1.770131 * dir.y * dir.z * (3.0 * dir.x * dir.x - dir.y * dir.y); // -0.75 * sqrt(35/(2pi)) * y * z * (3x^2-y^2)
+			case[4,-2]: 0.946175 * dir.x * dir.y * (7.0 * dir.z * dir.z - 1.0);// 0.75 * sqrt(5/pi) * x * y * (7z^2-1)
+			case[4,-1]: -0.669047 * dir.y * dir.z * (7.0 * dir.z * dir.z - 3.0);// -0.75 * sqrt(5/(2pi)) * y * z * (7z^2-3)
+			case[4,0]:  0.105786 * (35.0 * dir.z * dir.z * dir.z * dir.z - 30.0 * dir.z * dir.z + 3.0);// 3/16 * sqrt(1/pi) * (35z^4-30z^2+3)
+			case[4,1]:  -0.669047 * dir.x * dir.z * (7.0 * dir.z * dir.z - 3.0);// -0.75 * sqrt(5/(2pi)) * x * z * (7z^2-3)
+			case[4,2]:  0.473087 * (dir.x * dir.x - dir.y * dir.y) * (7.0 * dir.z * dir.z - 1.0);// 3/8 * sqrt(5/pi) * (x^2 - y^2) * (7z^2 - 1)
+			case[4,3]:  -1.770131 * dir.x * dir.z * (dir.x * dir.x - 3.0 * dir.y * dir.y);// -0.75 * sqrt(35/(2pi)) * x * z * (x^2 - 3y^2)
+			case[4,4]:  0.625836 * (dir.x * dir.x * (dir.x * dir.x - 3.0 * dir.y * dir.y) - dir.y * dir.y * (3.0 * dir.x * dir.x - dir.y * dir.y)); // 3/16*sqrt(35/pi) * (x^2 * (x^2 - 3y^2) - y^2 * (3x^2 - y^2))
+			default: 0;
+		}
+	}
+}

+ 2 - 0
hrt/prefab/vlm/SphericalHarmonic.hx

@@ -5,8 +5,10 @@ class SphericalHarmonic {
 	public var coefR : Array<Float> = [];
 	public var coefR : Array<Float> = [];
 	public var coefG : Array<Float> = [];
 	public var coefG : Array<Float> = [];
 	public var coefB : Array<Float> = [];
 	public var coefB : Array<Float> = [];
+	public var order : Int;
 
 
 	public function new(order:Int) {
 	public function new(order:Int) {
+		this.order = order;
 		var coefCount = order * order;
 		var coefCount = order * order;
 		coefR = [for (value in 0...coefCount) 0];
 		coefR = [for (value in 0...coefCount) 0];
 		coefG = [for (value in 0...coefCount) 0];
 		coefG = [for (value in 0...coefCount) 0];

+ 17 - 22
hrt/shader/ComputeSH.hx

@@ -30,13 +30,12 @@ class ComputeSH extends h3d.shader.ScreenShader {
 		var coefL21Final : Vec3;
 		var coefL21Final : Vec3;
 		var coefL22Final : Vec3;
 		var coefL22Final : Vec3;
 
 
-		function vertex(){
+		function vertex() {
 			var position = vec3(input.position.x, input.position.y * flipY, 0);
 			var position = vec3(input.position.x, input.position.y * flipY, 0);
 			output.position = vec4(position, 1.0);
 			output.position = vec4(position, 1.0);
 		}
 		}
 
 
-		function evalSH(dir:Vec3) {
-
+		function evalSH( dir : Vec3 ) {
 			if (ORDER >= 1){
 			if (ORDER >= 1){
 				shCoefL00 = evalCoefL00(dir);
 				shCoefL00 = evalCoefL00(dir);
 			}
 			}
@@ -54,13 +53,13 @@ class ComputeSH extends h3d.shader.ScreenShader {
 			}
 			}
 		}
 		}
 
 
-		function getDir(u: Float, v:Float, face:Int) : Vec3 {
+		function getDir( u : Float, v : Float, face : Int ) : Vec3 {
 			var dir = vec3(u, v, 1) * cubeDir[face];
 			var dir = vec3(u, v, 1) * cubeDir[face];
 			return dir.normalize();
 			return dir.normalize();
 		}
 		}
 
 
-		function evalCoefL00(dir: Vec3) : Float { return 0.282095; }
-		function evalCoefL1n1(dir: Vec3) : Float { return -0.488603 * dir.y; }
+		function evalCoefL00(dir: Vec3) : 	Float { return 0.282095; }
+		function evalCoefL1n1(dir: Vec3) : 	Float { return -0.488603 * dir.y; }
 		function evalCoefL10(dir: Vec3) : Float { return 0.488603 * dir.z; }
 		function evalCoefL10(dir: Vec3) : Float { return 0.488603 * dir.z; }
 		function evalCoefL11(dir: Vec3) : Float { return -0.488603 * dir.x; }
 		function evalCoefL11(dir: Vec3) : Float { return -0.488603 * dir.x; }
 		function evalCoefL2n2(dir: Vec3) : Float { return 1.092548 * dir.y * dir.x; }
 		function evalCoefL2n2(dir: Vec3) : Float { return 1.092548 * dir.y * dir.x; }
@@ -84,15 +83,15 @@ class ComputeSH extends h3d.shader.ScreenShader {
 
 
 		function fragment() {
 		function fragment() {
 
 
-			if (ORDER >= 1){
+			if( ORDER >= 1 ) {
 				coefL00Final = vec3(0);
 				coefL00Final = vec3(0);
 			}
 			}
-			if (ORDER >= 2){
+			if( ORDER >= 2 ) {
 				coefL1n1Final = vec3(0);
 				coefL1n1Final = vec3(0);
 				coefL10Final = vec3(0);
 				coefL10Final = vec3(0);
 				coefL11Final = vec3(0);
 				coefL11Final = vec3(0);
 			}
 			}
-			if (ORDER >= 3){
+			if( ORDER >= 3 ) {
 				coefL2n2Final = vec3(0);
 				coefL2n2Final = vec3(0);
 				coefL2n1Final = vec3(0);
 				coefL2n1Final = vec3(0);
 				coefL20Final = vec3(0);
 				coefL20Final = vec3(0);
@@ -104,12 +103,12 @@ class ComputeSH extends h3d.shader.ScreenShader {
 			var fWidth : Float = width;
 			var fWidth : Float = width;
 			var invWidth : Float = 1.0 / fWidth;
 			var invWidth : Float = 1.0 / fWidth;
 
 
-			for(f in 0...6) {
-				for (u in 0...width) {
+			for( f in 0...6 ) {
+				for( u in 0...width ) {
 					var fU : Float = (u / fWidth - 0.5) * 2.0;// Texture coordinate U in range [-1 to 1]
 					var fU : Float = (u / fWidth - 0.5) * 2.0;// Texture coordinate U in range [-1 to 1]
 					fU *= fU;
 					fU *= fU;
 					var uCoord = 2.0 * u * invWidth + invWidth;
 					var uCoord = 2.0 * u * invWidth + invWidth;
-					for (v in 0...width) {
+					for( v in 0...width ) {
 						var fV : Float = (v / fWidth - 0.5) * 2.0;// Texture coordinate V in range [-1 to 1]
 						var fV : Float = (v / fWidth - 0.5) * 2.0;// Texture coordinate V in range [-1 to 1]
 						fV *= fV;
 						fV *= fV;
 						var vCoord = 2.0 * v * invWidth + invWidth;
 						var vCoord = 2.0 * v * invWidth + invWidth;
@@ -124,15 +123,15 @@ class ComputeSH extends h3d.shader.ScreenShader {
 
 
 						evalSH(dir);// Calculate coefficients of spherical harmonics for current direction
 						evalSH(dir);// Calculate coefficients of spherical harmonics for current direction
 
 
-						if (ORDER >= 1){
+						if( ORDER >= 1 ) {
 							coefL00Final += shCoefL00 * color * diffSolid;
 							coefL00Final += shCoefL00 * color * diffSolid;
 						}
 						}
-						if (ORDER >= 2){
+						if( ORDER >= 2 ) {
 							coefL1n1Final += shCoefL1n1 * color * diffSolid;
 							coefL1n1Final += shCoefL1n1 * color * diffSolid;
 							coefL10Final += shCoefL10 * color * diffSolid;
 							coefL10Final += shCoefL10 * color * diffSolid;
 							coefL11Final += shCoefL11 * color * diffSolid;
 							coefL11Final += shCoefL11 * color * diffSolid;
 						}
 						}
-						if (ORDER >= 3){
+						if( ORDER >= 3 ) {
 							coefL2n2Final += shCoefL2n2 * color * diffSolid;
 							coefL2n2Final += shCoefL2n2 * color * diffSolid;
 							coefL2n1Final += shCoefL2n1 * color * diffSolid;
 							coefL2n1Final += shCoefL2n1 * color * diffSolid;
 							coefL20Final += shCoefL20 * color * diffSolid;
 							coefL20Final += shCoefL20 * color * diffSolid;
@@ -146,25 +145,21 @@ class ComputeSH extends h3d.shader.ScreenShader {
 			// Final scale for coefficients
 			// Final scale for coefficients
 			var normProj = (4.0 * PI) / weightSum;
 			var normProj = (4.0 * PI) / weightSum;
 
 
-			if (ORDER >= 1){
+			if( ORDER >= 1 ) {
 				out.coefL00 = coefL00Final * normProj;
 				out.coefL00 = coefL00Final * normProj;
 			}
 			}
-
-			if (ORDER >= 2){
+			if( ORDER >= 2 ) {
 				out.coefL1n1 = coefL1n1Final * normProj;
 				out.coefL1n1 = coefL1n1Final * normProj;
 				out.coefL10 = coefL10Final * normProj;
 				out.coefL10 = coefL10Final * normProj;
 				out.coefL11 = coefL11Final * normProj;
 				out.coefL11 = coefL11Final * normProj;
 			}
 			}
-			else{ out.coefL1n1 = vec3(0); out.coefL10 = vec3(0); out.coefL11 = vec3(0); }
-
-			if (ORDER >= 3){
+			if( ORDER >= 3 ) {
 				out.coefL2n2 = coefL2n2Final * normProj;
 				out.coefL2n2 = coefL2n2Final * normProj;
 				out.coefL2n1 = coefL2n1Final * normProj;
 				out.coefL2n1 = coefL2n1Final * normProj;
 				out.coefL20 = coefL20Final * normProj;
 				out.coefL20 = coefL20Final * normProj;
 				out.coefL21 = coefL21Final * normProj;
 				out.coefL21 = coefL21Final * normProj;
 				out.coefL22 = coefL22Final * normProj;
 				out.coefL22 = coefL22Final * normProj;
 			}
 			}
-			else{ out.coefL2n2 = vec3(0); out.coefL2n1 = vec3(0); out.coefL20 = vec3(0); out.coefL21 = vec3(0); out.coefL22 = vec3(0); }
 		}
 		}
 	}
 	}
 
 

+ 21 - 24
hrt/shader/DisplaySH.hx

@@ -4,6 +4,20 @@ class DisplaySH extends hxsl.Shader {
 
 
 	static var SRC = {
 	static var SRC = {
 
 
+		@const var order : Int;
+		@const var SIZE : Int; // Equal to order*order
+
+		@param var shCoefsRed : Array<Float,SIZE>;
+		@param var shCoefsGreen : Array<Float,SIZE>;
+		@param var shCoefsBlue : Array<Float,SIZE>;
+		@param var strength : Float;
+
+		var transformedNormal : Vec3;
+
+		var output : {
+			color : Vec4
+		};
+
 		function computeIrradiance(norm : Vec3) : Vec3 {
 		function computeIrradiance(norm : Vec3) : Vec3 {
 			// An Efficient Representation for Irradiance Environment Maps
 			// An Efficient Representation for Irradiance Environment Maps
 			// Ravi Ramamoorthi Pat Hanrahan
 			// Ravi Ramamoorthi Pat Hanrahan
@@ -16,52 +30,35 @@ class DisplaySH extends hxsl.Shader {
 
 
 			var irradiance = vec3(0,0,0);
 			var irradiance = vec3(0,0,0);
 
 
-			if (order >= 1){
+			if( order >= 1 ) {
 				var L00 = vec3(shCoefsRed[0], shCoefsGreen[0], shCoefsBlue[0]);
 				var L00 = vec3(shCoefsRed[0], shCoefsGreen[0], shCoefsBlue[0]);
 				irradiance += c4 * L00;
 				irradiance += c4 * L00;
 			}
 			}
-
-			if (order >= 2){
+			if( order >= 2 ) {
 				var L1n1 = vec3(shCoefsRed[1], shCoefsGreen[1], shCoefsBlue[1]);
 				var L1n1 = vec3(shCoefsRed[1], shCoefsGreen[1], shCoefsBlue[1]);
 				var L10 = vec3(shCoefsRed[2], shCoefsGreen[2], shCoefsBlue[2]);
 				var L10 = vec3(shCoefsRed[2], shCoefsGreen[2], shCoefsBlue[2]);
 				var L11 = vec3(shCoefsRed[3], shCoefsGreen[3], shCoefsBlue[3]);
 				var L11 = vec3(shCoefsRed[3], shCoefsGreen[3], shCoefsBlue[3]);
 				irradiance += 2 * c2 * (L11 * norm.x + L1n1 * norm.y + L10 * norm.z);
 				irradiance += 2 * c2 * (L11 * norm.x + L1n1 * norm.y + L10 * norm.z);
 			}
 			}
-
-			if(order >= 3){
+			if( order >= 3 ) {
 				var L2n2 = vec3(shCoefsRed[4], shCoefsGreen[4], shCoefsBlue[4]);
 				var L2n2 = vec3(shCoefsRed[4], shCoefsGreen[4], shCoefsBlue[4]);
 				var L2n1 = vec3(shCoefsRed[5], shCoefsGreen[5], shCoefsBlue[5]);
 				var L2n1 = vec3(shCoefsRed[5], shCoefsGreen[5], shCoefsBlue[5]);
 				var L20 = vec3(shCoefsRed[6], shCoefsGreen[6], shCoefsBlue[6]);
 				var L20 = vec3(shCoefsRed[6], shCoefsGreen[6], shCoefsBlue[6]);
 				var L21 = vec3(shCoefsRed[7], shCoefsGreen[7], shCoefsBlue[7]);
 				var L21 = vec3(shCoefsRed[7], shCoefsGreen[7], shCoefsBlue[7]);
 				var L22 = vec3(shCoefsRed[8], shCoefsGreen[8], shCoefsBlue[8]);
 				var L22 = vec3(shCoefsRed[8], shCoefsGreen[8], shCoefsBlue[8]);
-				irradiance += c1 * L22 * (norm.x*norm.x - norm.y*norm.y) + c3 * L20  * (norm.z * norm.z) - c5 * L20 +
+				irradiance += c1 * L22 * (norm.x*norm.x - norm.y*norm.y) + c3 * L20 * (norm.z * norm.z) - c5 * L20 +
 				2 * c1 * (L2n2 * (norm.x*norm.y) + L21 * (norm.x*norm.z) + L2n1 * (norm.y*norm.z));
 				2 * c1 * (L2n2 * (norm.x*norm.y) + L21 * (norm.x*norm.z) + L2n1 * (norm.y*norm.z));
 			}
 			}
 
 
-			return irradiance;
+			return irradiance / 3.14;
 		}
 		}
 
 
-		@const var order : Int;
-		@const var SIZE : Int; // Equal to order*order
-
-		@param var shCoefsRed : Array<Float,SIZE>;
-		@param var shCoefsGreen : Array<Float,SIZE>;
-		@param var shCoefsBlue : Array<Float,SIZE>;
-		@param var strength : Float;
-
-		var transformedNormal : Vec3;
-
-		var output : {
-			color : Vec4
-		};
-
 		function fragment() {
 		function fragment() {
-			var normal = vec4(transformedNormal.x, transformedNormal.y, transformedNormal.z, 1.0);
-			output.color = vec4(computeIrradiance(normal.xyz) / 3.14 * strength, 1.0);
+			output.color = vec4(computeIrradiance(transformedNormal.xyz) * strength, 1.0);
 		}
 		}
 	}
 	}
 
 
-	public function new(){
+	public function new() {
 		super();
 		super();
 		strength = 1.0;
 		strength = 1.0;
 	}
 	}