Forráskód Böngészése

- Add shadows for pointLight

ShiroSmith 7 éve
szülő
commit
cc5e17028c

+ 0 - 1
h3d/Camera.hx

@@ -128,7 +128,6 @@ class Camera {
 		}
 		pos.set(position.x,position.y,position.z);
 		target.set(pos.x + dx,pos.y + dy,pos.z + dz);
-		setFovX(90,1);
 	}
 
 	/**

+ 2 - 1
h3d/mat/Texture.hx

@@ -106,7 +106,8 @@ class Texture {
 			if( this.flags.has(f) )
 				flags.push(f);
 		var t = new Texture(width, height, flags, format, allocPos);
-		h3d.pass.Copy.run(this, t);
+		if(this.flags.has(Cube)) h3d.pass.CubeCopy.run(this, t);
+		else h3d.pass.Copy.run(this, t);
 		lastFrame = old;
 		return t;
 	}

+ 65 - 0
h3d/pass/CubeCopy.hx

@@ -0,0 +1,65 @@
+package h3d.pass;
+
+private class CubeCopyShader extends h3d.shader.ScreenShader {
+
+	static var SRC = {
+		@param var texture : SamplerCube;
+		@param var mat : Mat3x4;
+		function fragment() {
+			pixelColor = texture.get(vec3(calculatedUV, 1) * mat);
+		}
+	}
+}
+
+class CubeCopy extends ScreenFx<CubeCopyShader> {
+
+	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]) ];
+
+	public function new() {
+		super(new CubeCopyShader());
+	}
+
+	public function apply( from, to, ?blend : h3d.mat.BlendMode, ?customPass : h3d.mat.Pass ) {
+		for(i in 0 ... 6){
+			if( to != null )
+				engine.pushTarget(to, i);
+			shader.texture = from;
+			shader.mat = cubeDir[i];
+			if( customPass != null ) {
+				var old = pass;
+				pass = customPass;
+				if( blend != null ) pass.setBlendMode(blend);
+				var h = shaders;
+				while( h.next != null )
+					h = h.next;
+				h.next = @:privateAccess pass.shaders;
+				render();
+				pass = old;
+				h.next = null;
+			} else {
+				pass.setBlendMode(blend == null ? None : blend);
+				render();
+			}
+			shader.texture = null;
+			if( to != null )
+				engine.popTarget();
+		}
+	}
+
+	public static function run( from : h3d.mat.Texture, to : h3d.mat.Texture, ?blend : h3d.mat.BlendMode, ?pass : h3d.mat.Pass ) {
+		var engine = h3d.Engine.getCurrent();
+		if( to != null && from != null && (blend == null || blend == None) && pass == null && engine.driver.copyTexture(from, to) )
+			return;
+		var inst : CubeCopy = @:privateAccess engine.resCache.get(CubeCopy);
+		if( inst == null ) {
+			inst = new CubeCopy();
+			@:privateAccess engine.resCache.set(CubeCopy, inst);
+		}
+		return inst.apply(from, to, blend, pass);
+	}
+}

+ 203 - 0
h3d/pass/PointShadowMap.hx

@@ -0,0 +1,203 @@
+package h3d.pass;
+
+class PointShadowMap extends Shadows {
+
+	var lightCamera : h3d.Camera;
+	var customDepth : Bool;
+	var depth : h3d.mat.DepthBuffer;
+	var pshader : h3d.shader.PointShadow;
+	var mergePass = new h3d.pass.ScreenFx(new h3d.shader.MinMaxShader.CubeMinMaxShader());
+
+	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]) ];
+
+	public function new( light : h3d.scene.Light, useWorldDist : Bool ) {
+		super(light);
+		lightCamera = new h3d.Camera();
+		lightCamera.screenRatio = 1.0;
+		lightCamera.fovY = 90;
+		shader = pshader = new h3d.shader.PointShadow();
+		customDepth = h3d.Engine.getCurrent().driver.hasFeature(AllocDepthBuffer);
+		if( !customDepth ) depth = h3d.mat.DepthBuffer.getDefault();
+	}
+
+	override function set_mode(m:Shadows.RenderMode) {
+		pshader.enable = m != None;
+		return mode = m;
+	}
+
+	override function set_size(s) {
+		return super.set_size(s);
+	}
+
+	override function dispose() {
+		super.dispose();
+		if( customDepth && depth != null ) depth.dispose();
+	}
+
+	override function isUsingWorldDist(){
+		return true;
+	}
+
+	function getShadowProj() {
+		return lightCamera.m;
+	}
+
+	public dynamic function calcShadowBounds( camera : h3d.Camera ) {
+		var pt : Array<h3d.Vector> = camera.getFrustumCorners();
+		for( p in pt) {
+			camera.orthoBounds.addPos(p.x, p.y, p.z);
+		}
+	}
+
+	override function setGlobals() {
+		super.setGlobals();
+		cameraViewProj = getShadowProj();
+		cameraFar = lightCamera.zFar;
+		cameraPos = lightCamera.pos;
+	}
+
+	function syncShader(texture) {
+		pshader.shadowMap = texture;
+		pshader.shadowBias = bias;
+		pshader.shadowPower = power;
+		pshader.lightPos = lightCamera.pos;
+		pshader.zFar = lightCamera.zFar;
+	}
+
+	override function saveStaticData() {
+		if( mode != Mixed && mode != Static )
+			return null;
+		if( staticTexture == null )
+			throw "Data not computed";
+
+		var buffer = new haxe.io.BytesBuffer();
+		buffer.addInt32(staticTexture.width);
+		buffer.addFloat(lightCamera.pos.x);
+		buffer.addFloat(lightCamera.pos.y);
+		buffer.addFloat(lightCamera.pos.z);
+
+		for(i in 0 ... 6){
+			var bytes = haxe.zip.Compress.run(staticTexture.capturePixels(i).bytes,9);
+			buffer.addInt32(bytes.length);
+			buffer.add(bytes);
+		}
+
+		return buffer.getBytes();
+	}
+
+	override function loadStaticData( bytes : haxe.io.Bytes ) {
+		if( (mode != Mixed && mode != Static) || bytes == null )
+			return false;
+		var buffer = new haxe.io.BytesInput(bytes);
+		var size = buffer.readInt32();
+		if( size != this.size )
+			return false;
+
+		lightCamera.pos.x = buffer.readFloat();
+		lightCamera.pos.y = buffer.readFloat();
+		lightCamera.pos.z = buffer.readFloat();
+		lightCamera.update();
+
+		if( staticTexture != null ) staticTexture.dispose();
+			staticTexture = new h3d.mat.Texture(size, size, [Target, Cube], format);
+
+		for(i in 0 ... 6){
+			var len = buffer.readInt32();
+			var pixels = new hxd.Pixels(size, size, haxe.zip.Uncompress.run(buffer.read(len)), format);
+			staticTexture.uploadPixels(pixels, 0, i);
+		}
+		syncShader(staticTexture);
+		return true;
+	}
+
+	function createDefaultShadowMap(){
+		var tex = new h3d.mat.Texture(1,1, [Cube,Target], format);
+		for(i in 0 ... 6){
+			tex.clear(0xFFFFFF, i);
+		}
+		return tex;
+	}
+
+	override function draw( passes ) {
+
+		if( !ctx.computingStatic ){
+			switch( mode ) {
+			case None:
+				return passes;
+			case Dynamic:	// nothing
+			case Mixed:		// nothing
+			case Static:
+				if( staticTexture == null || staticTexture.isDisposed() ) staticTexture = createDefaultShadowMap();
+				syncShader(staticTexture);
+				return passes;
+			}
+		}
+
+		passes = filterPasses(passes);
+
+		var texture = ctx.textures.allocTarget("pointShadowMap", size, size, false, format, [Target, Cube]);
+		if(depth == null || depth.width != size || depth.height != size || depth.isDisposed() ) {
+				if( depth != null ) depth.dispose();
+				depth = new h3d.mat.DepthBuffer(size, size);
+		}
+		texture.depthBuffer = depth;
+
+		var textureTest = ctx.textures.allocTarget("pointShadowMap", size, size, false, format);
+		textureTest.depthBuffer = depth;
+
+		var merge : h3d.mat.Texture = null;
+		if( mode == Mixed && !ctx.computingStatic )
+			merge = ctx.textures.allocTarget("pointShadowMap", size, size, false, format, [Target, Cube]);
+
+		for(i in 0 ... 6){
+
+			var pointLight = cast(light, h3d.scene.pbr.PointLight);
+
+			if( mode != Mixed || ctx.computingStatic ) {
+				var absPos = light.getAbsPos();
+				lightCamera.setCubeMap(i, new h3d.Vector(absPos.tx, absPos.ty, absPos.tz));
+				lightCamera.zFar = pointLight.range;
+				lightCamera.update();
+			}
+
+			ctx.engine.pushTarget(texture, i);
+			ctx.engine.clear(0xFFFFFF, 1);
+			passes = super.draw(passes);
+			ctx.engine.popTarget();
+
+			if( mode == Mixed && !ctx.computingStatic ) {
+				mergePass.shader.texA = texture;
+				mergePass.shader.texB = staticTexture;
+				mergePass.shader.mat = cubeDir[i];
+				ctx.engine.pushTarget(merge, i);
+				mergePass.render();
+				ctx.engine.popTarget();
+			}
+
+			// To Do
+			//if( blur.radius > 0 && (mode != Mixed || !ctx.computingStatic) )
+				//blur.apply(ctx, texture);
+		}
+
+		if( mode == Mixed && !ctx.computingStatic )
+			texture = merge;
+
+		syncShader(texture);
+		return passes;
+	}
+
+	override function computeStatic( passes : h3d.pass.Object ) {
+		if( mode != Static && mode != Mixed )
+			return;
+		draw(passes);
+		var texture = pshader.shadowMap;
+		if( staticTexture != null ) staticTexture.dispose();
+		staticTexture = texture.clone();
+		pshader.shadowMap = staticTexture;
+	}
+}

+ 7 - 0
h3d/pass/Shadows.hx

@@ -50,7 +50,14 @@ class Shadows extends Default {
 		if( staticTexture != null ) staticTexture.dispose();
 	}
 
+	function isUsingWorldDist(){
+		return false;
+	}
+
 	override function getOutputs() : Array<hxsl.Output> {
+		if(isUsingWorldDist())
+			return [Swiz(Value("output.worldDist",1),[X,X,X,X])];
+
 		if( format == h3d.mat.Texture.nativeFormat )
 			return [PackFloat(Value("output.depth"))];
 		return [Swiz(Value("output.depth",1),[X,X,X,X])];

+ 4 - 1
h3d/scene/pbr/LightProbeBaker.hx

@@ -8,7 +8,7 @@ class LightProbeBaker {
 	public var environment : h3d.scene.pbr.Environment;
 
 	var envMap : h3d.mat.Texture;
-	var customCamera = new h3d.Camera();
+	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]),
@@ -30,6 +30,9 @@ class LightProbeBaker {
 	var textureArray: Array<h3d.mat.Texture>;
 
 	public function new(){
+		customCamera = new h3d.Camera();
+		customCamera.screenRatio = 1.0;
+		customCamera.fovY = 90;
 	}
 
 	public function bake(renderer : h3d.scene.Renderer, s3d : Scene, volumetricLightMap : VolumetricLightmap, resolution : Int, ?time :Float) {

+ 3 - 0
h3d/scene/pbr/PointLight.hx

@@ -13,6 +13,7 @@ class PointLight extends Light {
 		pbr = new h3d.shader.pbr.Light.PointLight();
 		super(pbr,parent);
 		range = 10;
+		shadows = new h3d.pass.PointShadowMap(this, true);
 		primitive = h3d.prim.Sphere.defaultUnitSphere();
 	}
 
@@ -41,6 +42,8 @@ class PointLight extends Light {
 		pbr.lightPos.set(absPos.tx, absPos.ty, absPos.tz);
 		pbr.invLightRange4 = 1 / (range * range * range * range);
 		pbr.pointSize = size;
+
+		super.emit(ctx);
 	}
 
 }

+ 4 - 0
h3d/shader/BaseMesh.hx

@@ -34,6 +34,7 @@ class BaseMesh extends hxsl.Shader {
 			var color : Vec4;
 			var depth : Float;
 			var normal : Vec3;
+			var worldDist : Float;
 		};
 
 		var relativePosition : Vec3;
@@ -46,6 +47,7 @@ class BaseMesh extends hxsl.Shader {
 		var screenUV : Vec2;
 		var specPower : Float;
 		var specColor : Vec3;
+		var worldDist : Float;
 
 		@param var color : Vec4;
 		@range(0,100) @param var specularPower : Float;
@@ -64,6 +66,7 @@ class BaseMesh extends hxsl.Shader {
 			specColor = specularColor * specularAmount;
 			screenUV = screenToUv(projectedPosition.xy / projectedPosition.w);
 			depth = projectedPosition.z / projectedPosition.w;
+			worldDist = length(transformedPosition - camera.position) / camera.zFar;
 		}
 
 		function __init__fragment() {
@@ -84,6 +87,7 @@ class BaseMesh extends hxsl.Shader {
 			output.color = pixelColor;
 			output.depth = depth;
 			output.normal = transformedNormal;
+			output.worldDist = worldDist;
 		}
 
 	};

+ 17 - 0
h3d/shader/MinMaxShader.hx

@@ -15,6 +15,23 @@ class MinMaxShader extends ScreenShader {
 		}
 
 	};
+}
+
+class CubeMinMaxShader extends ScreenShader {
+
+	static var SRC = {
+
+		@param var texA : SamplerCube;
+		@param var texB : SamplerCube;
+		@const var isMax : Bool;
+		@param var mat : Mat3x4;
 
+		function fragment() {
+			var dir = vec3(calculatedUV , 1) * mat;
+			var a = texA.get(dir);
+			var b = texB.get(dir);
+			pixelColor = isMax ? max(a,b) : min(a,b);
+		}
 
+	};
 }

+ 29 - 0
h3d/shader/PointShadow.hx

@@ -0,0 +1,29 @@
+package h3d.shader;
+
+class PointShadow extends hxsl.Shader {
+
+	static var SRC = {
+
+		@const var enable : Bool;
+
+		@param var shadowMap : SamplerCube;
+		@param var lightPos : Vec3;
+		@param var shadowPower : Float;
+		@param var shadowBias : Float;
+		@param var zFar : Float;
+
+		var transformedPosition : Vec3;
+		var shadow : Float;
+
+		function fragment() {
+			if( enable ) {
+				var posToLight = transformedPosition.xyz - lightPos;
+				var dir = normalize(vec3(posToLight.x, posToLight.y, posToLight.z));
+				var depth = shadowMap.get(dir).r * zFar;
+				var zMax = length(posToLight);
+				var delta = (depth + shadowBias).min(zMax) - zMax;
+				shadow = exp( shadowPower * delta ).saturate();
+			}
+		}
+	}
+}