2
0
Эх сурвалжийг харах

Forward : Improved light buffer packing to allow more lights. Fix directional lighting when a cascade shadow map is present.

TothBenoit 2 өдөр өмнө
parent
commit
30c8bf9c27

+ 0 - 1
h3d/scene/RenderContext.hx

@@ -108,7 +108,6 @@ class RenderContext extends h3d.impl.RenderContext {
 	}
 
 	public function start() {
-		lights = null;
 		drawPass = null;
 		passes = [];
 		lights = null;

+ 63 - 51
h3d/scene/pbr/LightBuffer.hx

@@ -6,13 +6,9 @@ class LightBuffer {
 
 	public var defaultForwardShader = new h3d.shader.pbr.DefaultForward();
 
-	var MAX_DIR_LIGHT = 2;
-	var MAX_SPOT_LIGHT = 10;
-	var MAX_POINT_LIGHT = 25;
 	var MAX_DIR_SHADOW = 1;
 	var MAX_SPOT_SHADOW = 2;
 	var MAX_POINT_SHADOW = 2;
-	var MAX_CASCADE_COUNT = 4;
 
 	var pointLightsShadow : Array<PointLight> = [];
 	var spotLightsShadow : Array<SpotLight> = [];
@@ -27,50 +23,41 @@ class LightBuffer {
 	final SPOT_LIGHT_INFO_SIZE = 8;
 	final DIR_LIGHT_INFO_SIZE = 5;
 	final CASCADE_SHADOW_INFO_SIZE = 13;
-
+	final BUFFER_MAX_SIZE = 4096;
 
 	public function new() {
 		createBuffers();
 	}
 
 	function createBuffers() {
-		var stride = 4;
-		var size = 0;
-		size += MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE;
-		size += MAX_POINT_LIGHT * POINT_LIGHT_INFO_SIZE;
-		size += MAX_SPOT_LIGHT * SPOT_LIGHT_INFO_SIZE;
-		size += CASCADE_SHADOW_INFO_SIZE;
-		size = hxd.Math.imax(1, size); // Avoid empty buffer
-		lightInfos = new hxd.FloatBuffer(size * stride);
-		defaultForwardShader.lightInfos = new h3d.Buffer(size, hxd.BufferFormat.make([{ name : "uniformData", type : DVec4 }]), [UniformBuffer, Dynamic]);
-		defaultForwardShader.BUFFER_SIZE = size;
-		defaultForwardShader.dirLightStride = DIR_LIGHT_INFO_SIZE * MAX_DIR_LIGHT;
-		defaultForwardShader.pointLightStride = POINT_LIGHT_INFO_SIZE * MAX_POINT_LIGHT;
-		defaultForwardShader.spotLightStride = SPOT_LIGHT_INFO_SIZE * MAX_SPOT_LIGHT;
+		lightInfos = new hxd.FloatBuffer(BUFFER_MAX_SIZE << 2);
+		defaultForwardShader.lightInfos = new h3d.Buffer(BUFFER_MAX_SIZE, hxd.BufferFormat.make([{ name : "uniformData", type : DVec4 }]), [UniformBuffer, Dynamic]);
 	}
 
 	public function setBuffers( s : h3d.shader.pbr.DefaultForward ) {
 		s.lightInfos = defaultForwardShader.lightInfos;
-		s.dirLightStride = defaultForwardShader.dirLightStride;
 		s.pointLightStride = defaultForwardShader.pointLightStride;
 		s.spotLightStride = defaultForwardShader.spotLightStride;
+		s.cascadeLightStride = defaultForwardShader.cascadeLightStride;
 		s.cameraPosition = defaultForwardShader.cameraPosition;
 		s.emissivePower = defaultForwardShader.emissivePower;
-		s.BUFFER_SIZE = defaultForwardShader.BUFFER_SIZE;
 
 		s.pointLightCount = defaultForwardShader.pointLightCount;
 		s.spotLightCount = defaultForwardShader.spotLightCount;
 		s.dirLightCount = defaultForwardShader.dirLightCount;
-		s.DIR_SHADOW_COUNT = defaultForwardShader.DIR_SHADOW_COUNT;
-		s.POINT_SHADOW_COUNT = defaultForwardShader.POINT_SHADOW_COUNT;
-		s.SPOT_SHADOW_COUNT = defaultForwardShader.SPOT_SHADOW_COUNT;
+		s.pointShadowCount = defaultForwardShader.pointShadowCount;
+		s.spotShadowCount = defaultForwardShader.spotShadowCount;
+		s.dirShadowCount = defaultForwardShader.dirShadowCount;
+		s.MAX_DIR_SHADOW_COUNT = defaultForwardShader.MAX_DIR_SHADOW_COUNT;
+		s.MAX_POINT_SHADOW_COUNT = defaultForwardShader.MAX_POINT_SHADOW_COUNT;
+		s.MAX_SPOT_SHADOW_COUNT = defaultForwardShader.MAX_SPOT_SHADOW_COUNT;
 		s.CASCADE_COUNT = defaultForwardShader.CASCADE_COUNT;
 
-		for( i in 0 ... defaultForwardShader.POINT_SHADOW_COUNT )
+		for( i in 0 ... defaultForwardShader.MAX_POINT_SHADOW_COUNT )
 			s.pointShadowMaps[i] = defaultForwardShader.pointShadowMaps[i];
-		for( i in 0 ... defaultForwardShader.SPOT_SHADOW_COUNT )
+		for( i in 0 ... defaultForwardShader.MAX_SPOT_SHADOW_COUNT )
 			s.spotShadowMaps[i] = defaultForwardShader.spotShadowMaps[i];
-		for( i in 0 ... defaultForwardShader.DIR_SHADOW_COUNT )
+		for( i in 0 ... defaultForwardShader.MAX_DIR_SHADOW_COUNT )
 			s.dirShadowMaps[i] = defaultForwardShader.dirShadowMaps[i];
 		for ( i in 0... defaultForwardShader.CASCADE_COUNT )
 			s.cascadeShadowMaps[i] = defaultForwardShader.cascadeShadowMaps[i];
@@ -100,10 +87,16 @@ class LightBuffer {
 		b[i+3] = 1;
 	}
 
-	function sortingCriteria ( l1 : Light, l2 : Light, cameraTarget : Vector ) {
+	function sortingCriteria ( l1 : Light, l2 : Light, cameraTarget : h3d.Vector ) {
+		var dirL1 = Std.isOfType(l1, DirLight);
+		var dirL2 = Std.isOfType(l2, DirLight);
+		if ( dirL1 && !dirL2 )
+			return -1;
+		if ( dirL2 && !dirL1 )
+			return 1;
 		var d1 = l1.getAbsPos().getPosition().sub(cameraTarget).length();
 		var d2 = l2.getAbsPos().getPosition().sub(cameraTarget).length();
-		return d1 > d2 ? 1 : -1;
+		return d1 == d2 ? 0 : (d1 > d2 ? 1 : -1);
 	}
 
 	public function sortLights ( ctx : h3d.scene.RenderContext ) : Array<Light> @:privateAccess {
@@ -112,12 +105,12 @@ class LightBuffer {
 		if ( l == null )
 			return null;
 		while ( l != null ) {
-			if (l.enableForward) {
+			if (l.enableForward && l.inFrustum(ctx.camera.frustum)) {
 				lights.push(l);
 			}
 			l = Std.downcast(l.next, Light);
 		}
-		lights.sort(function(l1,l2) { return sortingCriteria(l1, l2, @:privateAccess ctx.camera.target); });
+		lights.sort(function(l1,l2) { return sortingCriteria(l1, l2, ctx.camera.target); });
 		return lights;
 	}
 
@@ -140,16 +133,22 @@ class LightBuffer {
 		var pointLightCount = 0;
 		var spotLightCount = 0;
 
+		var curSize = 0;
 		for (l in lights) {
 			var dl = Std.downcast(l, DirLight);
 			if (dl != null) {
-				if (dirLightCount + dirShadowCount < MAX_DIR_LIGHT) {
+				if ( curSize + DIR_LIGHT_INFO_SIZE < BUFFER_MAX_SIZE ) {
+					curSize += DIR_LIGHT_INFO_SIZE;
 					var hasShadow = dl.shadows != null && dl.shadows.enabled && dl.shadows.mode != None && shadows;
 					if (hasShadow && dirShadowCount < MAX_DIR_SHADOW) {
 						var cascade = Std.downcast(dl.shadows, CascadeShadowMap);
-						if ( cascade != null )
-							cascadeLight = dl;
-						else
+						if ( cascade != null ) {
+							curSize -= DIR_LIGHT_INFO_SIZE;
+							if ( curSize + CASCADE_SHADOW_INFO_SIZE < BUFFER_MAX_SIZE ) {
+								curSize += CASCADE_SHADOW_INFO_SIZE;
+								cascadeLight = dl;
+							}
+						} else
 							dirLightsShadow.push(dl);
 						dirShadowCount++;
 					} else {
@@ -161,7 +160,8 @@ class LightBuffer {
 
 			var pl = Std.downcast(l, PointLight);
 			if (pl != null) {
-				if (pointLightCount + pointShadowCount < MAX_POINT_LIGHT) {
+				if ( curSize + POINT_LIGHT_INFO_SIZE < BUFFER_MAX_SIZE ) {
+					curSize += POINT_LIGHT_INFO_SIZE;
 					var hasShadow = pl.shadows != null && pl.shadows.enabled && pl.shadows.mode != None && shadows;
 					if (hasShadow && pointShadowCount < MAX_POINT_SHADOW) {
 						pointLightsShadow.push(pl);
@@ -176,7 +176,8 @@ class LightBuffer {
 
 			var sl = Std.downcast(l, SpotLight);
 			if (sl != null) {
-				if (spotLightCount + spotShadowCount < MAX_SPOT_LIGHT) {
+				if ( curSize + SPOT_LIGHT_INFO_SIZE < BUFFER_MAX_SIZE ) {
+					curSize += SPOT_LIGHT_INFO_SIZE;
 					var hasShadow = sl.shadows != null && sl.shadows.enabled && sl.shadows.mode != None && shadows;
 					if (hasShadow && spotShadowCount < MAX_SPOT_SHADOW) {
 						spotLightsShadow.push(sl);
@@ -217,10 +218,11 @@ class LightBuffer {
 		fillLights(lights, pbrRenderer.shadows);
 
 		// Dir Light With Shadow
+		var stride = 0;
 		for (li in 0...dirLightsShadow.length) {
 			var dl = dirLightsShadow[li];
-			var i = li * DIR_LIGHT_INFO_SIZE * 4;
 			var pbr = @:privateAccess dl.pbr;
+			var i = stride << 2;
 			fillVector(lightInfos, pbr.lightColor, i);
 			lightInfos[i+3] = 1.0;
 			fillVector(lightInfos, pbr.lightDir, i+4);
@@ -230,23 +232,26 @@ class LightBuffer {
 			fillFloats(lightInfos, mat._11, mat._21, mat._31, mat._41, i+8);
 			fillFloats(lightInfos, mat._12, mat._22, mat._32, mat._42, i+12);
 			fillFloats(lightInfos, mat._13, mat._23, mat._33, mat._43, i+16);
+			stride += DIR_LIGHT_INFO_SIZE;
 		}
 
 		// Dir Light
 		for (li in 0...dirLights.length) {
 			var dl = dirLights[li];
-			var i = (li + dirLightsShadow.length) * DIR_LIGHT_INFO_SIZE * 4;
+			var i = stride << 2;
 			var pbr = @:privateAccess dl.pbr;
 			fillVector(lightInfos, pbr.lightColor, i);
 			lightInfos[i+3] = -1.0;
 			fillVector(lightInfos, pbr.lightDir, i+4);
+			stride += DIR_LIGHT_INFO_SIZE;
 		}
 
+		s.pointLightStride = stride;
+
 		// Point Light With Shadows
 		for (li in 0...pointLightsShadow.length) {
 			var pl = pointLightsShadow[li];
-			var offset = MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE * 4;
-			var i = li * POINT_LIGHT_INFO_SIZE * 4 + offset;
+			var i = stride << 2;
 			var pbr = @:privateAccess pl.pbr;
 			fillVector(lightInfos, pbr.lightColor, i);
 			lightInfos[i+3] = pbr.pointSize;
@@ -256,13 +261,13 @@ class LightBuffer {
 			lightInfos[i+9] = 1.0;
 			lightInfos[i+10] = pl.shadows.bias;
 			s.pointShadowMaps[li] = pl.shadows.getShadowTex();
+			stride += POINT_LIGHT_INFO_SIZE;
 		}
 
 		// Point Light
 		for (li in 0...pointLights.length) {
 			var pl = pointLights[li];
-			var offset = MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE * 4;
-			var i = (li + pointLightsShadow.length) * POINT_LIGHT_INFO_SIZE * 4 + offset;
+			var i = stride << 2;
 			var pbr = @:privateAccess pl.pbr;
 			fillVector(lightInfos, pbr.lightColor, i);
 			lightInfos[i+3] = pbr.pointSize;
@@ -270,13 +275,15 @@ class LightBuffer {
 			lightInfos[i+7] = pbr.invLightRange4;
 			lightInfos[i+8] = pl.range;
 			lightInfos[i+9] = -1.0;
+			stride += POINT_LIGHT_INFO_SIZE;
 		}
 
+		s.spotLightStride = stride;
+
 		// Spot Light With Shadow
 		for (li in 0...spotLightsShadow.length) {
 			var sl = spotLightsShadow[li];
-			var offset = (MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE + MAX_POINT_LIGHT * POINT_LIGHT_INFO_SIZE) * 4 ;
-			var i = li * SPOT_LIGHT_INFO_SIZE * 4 + offset;
+			var i = stride << 2;
 			var pbr = @:privateAccess sl.pbr;
 			fillVector(lightInfos, pbr.lightColor, i);
 			lightInfos[i+3] = pbr.range;
@@ -293,13 +300,13 @@ class LightBuffer {
 			fillFloats(lightInfos, mat._13, mat._23, mat._33, mat._43, i+24);
 			fillFloats(lightInfos, mat._14, mat._24, mat._34, mat._44, i+28);
 			s.spotShadowMaps[li] = sl.shadows.getShadowTex();
+			stride += SPOT_LIGHT_INFO_SIZE;
 		}
 
 		// Spot Light
 		for (li in 0...spotLights.length) {
 			var sl = spotLights[li];
-			var offset = (MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE + MAX_POINT_LIGHT * POINT_LIGHT_INFO_SIZE) * 4 ;
-			var i = (li + spotLightsShadow.length) * SPOT_LIGHT_INFO_SIZE * 4 + offset;
+			var i = stride << 2;
 			var pbr = @:privateAccess sl.pbr;
 			fillVector(lightInfos, pbr.lightColor, i);
 			lightInfos[i+3] = pbr.range;
@@ -309,12 +316,14 @@ class LightBuffer {
 			lightInfos[i+12] = pbr.angle;
 			lightInfos[i+13] = pbr.fallOff;
 			lightInfos[i+14] = -1.0;
+			stride += SPOT_LIGHT_INFO_SIZE;
 		}
 
+		s.cascadeLightStride = stride;
+
 		// Cascade shadows
 		if ( cascadeLight != null ) {
-			var offset = (MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE + MAX_POINT_LIGHT * POINT_LIGHT_INFO_SIZE + MAX_SPOT_LIGHT * SPOT_LIGHT_INFO_SIZE) * 4 ;
-			var i = offset;
+			var i = stride << 2;
 			var pbr = @:privateAccess cascadeLight.pbr;
 			fillVector(lightInfos, pbr.lightColor, i);
 			lightInfos[i+3] = 0.0;
@@ -337,9 +346,12 @@ class LightBuffer {
 		s.dirLightCount = dirLights.length;
 		s.pointLightCount = pointLights.length;
 		s.spotLightCount = spotLights.length;
-		s.DIR_SHADOW_COUNT = MAX_DIR_SHADOW;
-		s.POINT_SHADOW_COUNT = MAX_POINT_SHADOW;
-		s.SPOT_SHADOW_COUNT = MAX_SPOT_SHADOW;
+		s.MAX_DIR_SHADOW_COUNT = MAX_DIR_SHADOW;
+		s.MAX_POINT_SHADOW_COUNT = MAX_POINT_SHADOW;
+		s.MAX_SPOT_SHADOW_COUNT = MAX_SPOT_SHADOW;
+		s.dirShadowCount = dirLightsShadow.length;
+		s.pointShadowCount = pointLightsShadow.length;
+		s.spotShadowCount = spotLightsShadow.length;
 		s.lightInfos.uploadFloats(lightInfos, 0, s.lightInfos.vertices, 0);
 		pointLights = [];
 		spotLights = [];

+ 35 - 32
h3d/shader/pbr/DefaultForward.hx

@@ -12,9 +12,9 @@ class DefaultForward extends hxsl.Shader {
 		}
 
 		@const(4) var CASCADE_COUNT:Int;
-		@const(2) var DIR_SHADOW_COUNT:Int;
-		@const(16) var POINT_SHADOW_COUNT:Int;
-		@const(16) var SPOT_SHADOW_COUNT:Int;
+		@const(2) var MAX_DIR_SHADOW_COUNT:Int;
+		@const(16) var MAX_POINT_SHADOW_COUNT:Int;
+		@const(16) var MAX_SPOT_SHADOW_COUNT:Int;
 
 		@:import h3d.shader.pbr.Light.LightEvaluation;
 		@:import h3d.shader.pbr.BRDF;
@@ -22,22 +22,24 @@ class DefaultForward extends hxsl.Shader {
 		// Import pbr info
 		var output : {color : Vec4, metalness : Float, roughness : Float, occlusion : Float, emissive : Float, depth : Float };
 
-		@const(256) var BUFFER_SIZE : Int = 1;
-		@param var lightInfos : Buffer<Vec4, BUFFER_SIZE>;
+		@param var lightInfos : Buffer<Vec4, 4096>;
 
 		// Buffer Info
 		@param var dirLightCount : Int;
+		@param var dirShadowCount : Int;
 		@param var pointLightCount : Int;
+		@param var pointShadowCount : Int;
 		@param var spotLightCount : Int;
-		@param var dirLightStride : Int;
+		@param var spotShadowCount : Int;
 		@param var pointLightStride : Int;
 		@param var spotLightStride : Int;
+		@param var cascadeLightStride : Int;
 
 		// ShadowMaps
 		@param var cascadeShadowMaps : Array<Sampler2D, CASCADE_COUNT>;
-		@param var dirShadowMaps : Array<Sampler2D, DIR_SHADOW_COUNT>;
-		@param var pointShadowMaps : Array<SamplerCube, POINT_SHADOW_COUNT>;
-		@param var spotShadowMaps : Array<Sampler2D, SPOT_SHADOW_COUNT>;
+		@param var dirShadowMaps : Array<Sampler2D, MAX_DIR_SHADOW_COUNT>;
+		@param var pointShadowMaps : Array<SamplerCube, MAX_POINT_SHADOW_COUNT>;
+		@param var spotShadowMaps : Array<Sampler2D, MAX_SPOT_SHADOW_COUNT>;
 
 		// Direct Lighting
 		@param var cameraPosition : Vec3;
@@ -140,7 +142,7 @@ class DefaultForward extends hxsl.Shader {
 		}
 
 		function evaluatePointShadow( index : Int ) : Float {
-			var i = index * 3 + dirLightStride;
+			var i = index * 3 + pointLightStride;
 
 			var shadow = 1.0;
 			if (lightInfos[i+2].g > 0) {
@@ -157,7 +159,7 @@ class DefaultForward extends hxsl.Shader {
 		}
 
 		function evaluatePointLight( index : Int ) : Vec3 {
-			var i = index * 3 + dirLightStride;
+			var i = index * 3 + pointLightStride;
 			var lightColor = lightInfos[i].rgb;
 			var size = lightInfos[i].a;
 			var lightPos = lightInfos[i+1].rgb;
@@ -168,7 +170,7 @@ class DefaultForward extends hxsl.Shader {
 		}
 
 		function evaluateSpotShadow( index : Int ) : Float {
-			var i = index * 8 + dirLightStride + pointLightStride;
+			var i = index * 8 + spotLightStride;
 
 			var shadow = 1.0;
 			if (lightInfos[i+3].b > 0) {
@@ -184,7 +186,7 @@ class DefaultForward extends hxsl.Shader {
 		}
 
 		function evaluateSpotLight( index : Int ) : Vec3 {
-			var i = index * 8 + dirLightStride + pointLightStride;
+			var i = index * 8 + spotLightStride;
 			var lightColor = lightInfos[i].rgb;
 			var range = lightInfos[i].a;
 			var lightPos = lightInfos[i+1].xyz;
@@ -202,7 +204,7 @@ class DefaultForward extends hxsl.Shader {
 		}
 
 		function evaluateCascadeLight() : Vec3 {
-			var i = dirLightStride + pointLightStride + spotLightStride;
+			var i = cascadeLightStride;
 			var lightColor = lightInfos[i].rgb;
 			var lightDir = lightInfos[i+1].xyz;
 
@@ -217,7 +219,7 @@ class DefaultForward extends hxsl.Shader {
 		}
 
 		function evaluateCascadeShadow() : Float {
-			var i = dirLightStride + pointLightStride + spotLightStride;
+			var i = cascadeLightStride;
 			var shadow = 1.0;
 			var shadowProj = mat3x4(lightInfos[i + 2], lightInfos[i + 3], lightInfos[i + 4]);
 
@@ -243,39 +245,40 @@ class DefaultForward extends hxsl.Shader {
 			F0 = mix(pbrSpecularColor, albedoGamma, metalness);
 
 			// Dir Light With Shadow
-			@unroll for( l in 0 ... DIR_SHADOW_COUNT ) {
-				var c = evaluateDirLight(l);
-				if ( dot(c, c) > 1e-6 )
-					c *= evaluateDirShadow(l);
-				lightAccumulation += c;
+			@unroll for( l in 0 ... MAX_DIR_SHADOW_COUNT ) {
+				if ( l < dirShadowCount ) {
+					var c = evaluateDirLight(l);
+					if ( dot(c, c) > 1e-6 )
+						c *= evaluateDirShadow(l);
+					lightAccumulation += c;
+				}
 			}
 			// Dir Light
-			var start = DIR_SHADOW_COUNT;
-			if ( CASCADE_COUNT > 0 )
-				start++;
-			@unroll for( l in start ... dirLightCount + DIR_SHADOW_COUNT )
+			for( l in dirShadowCount ... dirLightCount )
 				lightAccumulation += evaluateDirLight(l);
 
 			// Point Light With Shadow
-			@unroll for( l in 0 ... POINT_SHADOW_COUNT ) {
-				var c = evaluatePointLight(l);
-				if ( dot(c, c) > 1e-6 )
-					c *= evaluatePointShadow(l);
-				lightAccumulation += c;
+			@unroll for( l in 0 ... MAX_POINT_SHADOW_COUNT ) {
+				if ( l < pointShadowCount ) {
+					var c = evaluatePointLight(l);
+					if ( dot(c, c) > 1e-6 )
+						c *= evaluatePointShadow(l);
+					lightAccumulation += c;
+				}
 			}
 			// Point Light
-			@unroll for( l in POINT_SHADOW_COUNT ... pointLightCount + POINT_SHADOW_COUNT )
+			for( l in pointShadowCount ... pointLightCount + pointShadowCount )
 				lightAccumulation += evaluatePointLight(l);
 
 			// Spot Light With Shadow
-			@unroll for( l in 0 ... SPOT_SHADOW_COUNT ) {
+			@unroll for( l in 0 ... MAX_SPOT_SHADOW_COUNT ) {
 				var c = evaluateSpotLight(l);
 				if ( dot(c, c) > 1e-6 )
 					c *= evaluateSpotShadow(l);
 				lightAccumulation += c;
 			}
 			// Spot Light
-			@unroll for( l in SPOT_SHADOW_COUNT ... spotLightCount + SPOT_SHADOW_COUNT )
+			for( l in spotShadowCount ... spotLightCount + spotShadowCount )
 				lightAccumulation += evaluateSpotLight(l);
 
 			// Cascade shadows