瀏覽代碼

[PBR FORWARD][SHADOWS] Adding shadows in forward lighting. Shadows are limited in number for hardware reasons.

clandrin 4 年之前
父節點
當前提交
05de6b5dfb
共有 2 個文件被更改,包括 247 次插入124 次删除
  1. 185 85
      h3d/scene/pbr/LightBuffer.hx
  2. 62 39
      h3d/shader/pbr/DefaultForward.hx

+ 185 - 85
h3d/scene/pbr/LightBuffer.hx

@@ -7,6 +7,16 @@ class LightBuffer {
 	var MAX_DIR_LIGHT = 2;
 	var MAX_SPOT_LIGHT = 10;
 	var MAX_POINT_LIGHT = 10;
+	var MAX_DIR_SHADOW = 2;
+	var MAX_SPOT_SHADOW = 3;
+	var MAX_POINT_SHADOW = 3;
+
+	var pointLightsShadow : Array<PointLight> = [];
+	var spotLightsShadow : Array<SpotLight> = [];
+	var dirLightsShadow : Array<DirLight> = [];
+	var pointLights : Array<PointLight> = [];
+	var spotLights : Array<SpotLight> = [];
+	var dirLights : Array<DirLight> = [];
 
 	var lightInfos : hxd.FloatBuffer;
 	final POINT_LIGHT_INFO_SIZE = 3;
@@ -30,6 +40,9 @@ class LightBuffer {
 		defaultForwardShader.BUFFER_SIZE = size;
 		defaultForwardShader.dirLightStride = DIR_LIGHT_INFO_SIZE * MAX_DIR_LIGHT;
 		defaultForwardShader.pointLightStride = POINT_LIGHT_INFO_SIZE * MAX_POINT_LIGHT;
+		defaultForwardShader.MAX_DIR_SHADOWS = MAX_DIR_SHADOW;
+		defaultForwardShader.MAX_POINT_SHADOWS = MAX_POINT_SHADOW;
+		defaultForwardShader.MAX_SPOT_SHADOWS = MAX_SPOT_SHADOW;
 	}
 
 	public function setBuffers( s : h3d.shader.pbr.DefaultForward ) {
@@ -44,12 +57,12 @@ class LightBuffer {
 		s.spotLightCount = defaultForwardShader.spotLightCount;
 		s.dirLightCount = defaultForwardShader.dirLightCount;
 
-		/*for( i in 0 ... s.pointLightCount )
+		for( i in 0 ... MAX_POINT_SHADOW )
 			s.pointShadowMaps[i] = defaultForwardShader.pointShadowMaps[i];
-		for( i in 0 ... s.spotLightCount )
+		for( i in 0 ... MAX_SPOT_SHADOW )
 			s.spotShadowMaps[i] = defaultForwardShader.spotShadowMaps[i];
-		for( i in 0 ... s.dirLightCount )
-			s.dirShadowMaps[i] = defaultForwardShader.dirShadowMaps[i];*/
+		for( i in 0 ... MAX_DIR_SHADOW )
+			s.dirShadowMaps[i] = defaultForwardShader.dirShadowMaps[i];
 
 		s.USE_INDIRECT = defaultForwardShader.USE_INDIRECT;
 		if( s.USE_INDIRECT ) {
@@ -77,16 +90,6 @@ class LightBuffer {
 	}
 
 	function sortingCriteria ( l1 : Light, l2 : Light, cameraTarget : Vector ) {
-		var pbr1 = Std.downcast(l1, h3d.scene.pbr.Light);
-		var pbr2 = Std.downcast(l2, h3d.scene.pbr.Light);
-		var last1 = pbr1 != null && !pbr1.enableForward;
-		var last2 = pbr2 != null && !pbr2.enableForward;
-		if (last1 && !last2) {
-			return 1;
-		}
-		if (last2 && !last1) {
-			return -1;
-		}
 		var d1 = l1.getAbsPos().getPosition().sub(cameraTarget).length();
 		var d2 = l2.getAbsPos().getPosition().sub(cameraTarget).length();
 		return d1 > d2 ? 1 : -1;
@@ -98,13 +101,81 @@ class LightBuffer {
 		if ( l == null )
 			return null;
 		while ( l != null ) {
-			lights.push(l);
+			if (l.enableForward) {
+				lights.push(l);
+			}
 			l = Std.downcast(l.next, Light);
 		}
 		lights.sort(function(l1,l2) { return sortingCriteria(l1, l2, @:privateAccess ctx.camera.target); });
 		return lights;
 	}
 
+	public function fillLights (lights : Array<Light>, shadows : Bool) {
+		if (lights == null)
+			return;
+		pointLightsShadow = [];
+		spotLightsShadow = [];
+		dirLightsShadow = [];
+		pointLights = [];
+		spotLights = [];
+		dirLights = [];
+
+		var dirShadowCount = 0;
+		var pointShadowCount = 0;
+		var spotShadowCount = 0;
+
+		var dirLightCount = 0;
+		var pointLightCount = 0;
+		var spotLightCount = 0;
+
+		for (l in lights) {
+			var dl = Std.downcast(l, DirLight);
+			if (dl != null) {
+				if (dirLightCount + dirShadowCount < MAX_DIR_LIGHT) {
+					var hasShadow = dl.shadows != null && dl.shadows.mode != None && shadows;
+					if (hasShadow && dirShadowCount < MAX_DIR_SHADOW) {
+						dirLightsShadow.push(dl);
+						dirShadowCount++;
+					} else {
+						dirLights.push(dl);
+						dirLightCount++;
+					}
+				}
+
+			}
+
+			var pl = Std.downcast(l, PointLight);
+			if (pl != null) {
+				if (pointLightCount + pointShadowCount < MAX_POINT_LIGHT) {
+					var hasShadow = pl.shadows != null && pl.shadows.mode != None && shadows;
+					if (hasShadow && pointShadowCount < MAX_POINT_SHADOW) {
+						pointLightsShadow.push(pl);
+						pointShadowCount++;
+					} else {
+						pointLights.push(pl);
+						pointLightCount++;
+					}
+				}
+
+			}
+
+			var sl = Std.downcast(l, SpotLight);
+			if (sl != null) {
+				if (spotLightCount + spotShadowCount < MAX_SPOT_LIGHT) {
+					var hasShadow = sl.shadows != null && sl.shadows.mode != None && shadows;
+					if (hasShadow && spotShadowCount < MAX_SPOT_SHADOW) {
+						spotLightsShadow.push(sl);
+						spotShadowCount++;
+					} else {
+						spotLights.push(sl);
+						spotLightCount++;
+					}
+				}
+
+			}
+		}
+	}
+
 	public function sync( ctx : h3d.scene.RenderContext ) {
 		if (defaultForwardShader.lightInfos.isDisposed())
 			createBuffers();
@@ -128,77 +199,106 @@ class LightBuffer {
 			lightInfos[i] = 0;
 
 		var lights = sortLights(ctx);
-		if (lights != null) {
-			for ( l in lights ) {
-
-				// Dir Light
-				var dl = Std.downcast(l, DirLight);
-				if( dl != null && s.dirLightCount < MAX_DIR_LIGHT ) {
-					var li = s.dirLightCount;
-					var i = li * DIR_LIGHT_INFO_SIZE * 4;
-					var pbr = @:privateAccess dl.pbr;
-					fillVector(lightInfos, pbr.lightColor, i);
-					lightInfos[i+3] = (dl.shadows != null && dl.shadows.mode != None) ? 1.0 : -1.0;
-					fillVector(lightInfos, pbr.lightDir, i+4);
-					/*if( lightInfos[i+3] > 0 ) {
-						lightInfos[i+7] = dl.shadows.bias;
-						s.dirShadowMaps[li] = dl.shadows.getShadowTex();
-						var mat = dl.shadows.getShadowProj();
-						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);
-					}*/
-					s.dirLightCount++;
-				}
-	
-				// Point Light
-				var pl = Std.downcast(l, PointLight);
-				if( pl != null && s.pointLightCount < MAX_POINT_LIGHT ) {
-					var offset = MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE * 4;
-					var li = s.pointLightCount;
-					var i = li * POINT_LIGHT_INFO_SIZE * 4 + offset;
-					var pbr = @:privateAccess pl.pbr;
-					fillVector(lightInfos, pbr.lightColor, i);
-					lightInfos[i+3] = pbr.pointSize;
-					fillVector(lightInfos, pbr.lightPos, i+4);
-					lightInfos[i+7] = pbr.invLightRange4;
-					lightInfos[i+8] = pl.range;
-					lightInfos[i+9] = (pl.shadows != null && pl.shadows.mode != None) ? 1.0 : -1.0;
-					/*if( lightInfos[i+9] > 0 ) {
-						lightInfos[i+10] = pl.shadows.bias;
-						s.pointShadowMaps[li] = pl.shadows.getShadowTex();
-					}*/
-					s.pointLightCount++;
-				}
-	
-				// Spot Light
-				var sl = Std.downcast(l, SpotLight);
-				if( sl != null && s.spotLightCount < MAX_SPOT_LIGHT ) {
-					var offset = (MAX_DIR_LIGHT * DIR_LIGHT_INFO_SIZE + MAX_POINT_LIGHT * POINT_LIGHT_INFO_SIZE) * 4 ;
-					var li = s.spotLightCount;
-					var i = s.spotLightCount * SPOT_LIGHT_INFO_SIZE * 4 + offset;
-					var pbr = @:privateAccess sl.pbr;
-					fillVector(lightInfos, pbr.lightColor, i);
-					lightInfos[i+3] = pbr.range;
-					fillVector(lightInfos, pbr.lightPos, i+4);
-					lightInfos[i+7] = pbr.invLightRange4;
-					fillVector(lightInfos, pbr.spotDir, i+8);
-					lightInfos[i+12] = pbr.angle;
-					lightInfos[i+13] = pbr.fallOff;
-					lightInfos[i+14] = (sl.shadows != null && sl.shadows.mode != None) ? 1.0 : -1.0;
-					/*if( lightInfos[i+14] > 0 ) {
-						lightInfos[i+15] = sl.shadows.bias;
-						var mat = sl.shadows.getShadowProj();
-						fillFloats(lightInfos, mat._11, mat._21, mat._31, mat._41, i+16);
-						fillFloats(lightInfos, mat._12, mat._22, mat._32, mat._42, i+20);
-						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();
-					}*/
-					s.spotLightCount++;
-				}
-			}
+		fillLights(lights, pbrRenderer.shadows);
+
+		// Dir Light With Shadow
+		for (li in 0...dirLightsShadow.length) {
+			var dl = dirLightsShadow[li];
+			var i = li * DIR_LIGHT_INFO_SIZE * 4;
+			var pbr = @:privateAccess dl.pbr;
+			fillVector(lightInfos, pbr.lightColor, i);
+			fillVector(lightInfos, pbr.lightDir, i+4);
+			lightInfos[i+3] = 1.0;
+			lightInfos[i+7] = dl.shadows.bias;
+			s.dirShadowMaps[li] = dl.shadows.getShadowTex();
+			var mat = dl.shadows.getShadowProj();
+			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);
+		}
+
+		// Dir Light
+		for (li in 0...dirLights.length) {
+			var dl = dirLights[li];
+			var i = (li + dirLightsShadow.length) * DIR_LIGHT_INFO_SIZE * 4;
+			var pbr = @:privateAccess dl.pbr;
+			fillVector(lightInfos, pbr.lightColor, i);
+			fillVector(lightInfos, pbr.lightDir, i+4);
+			lightInfos[i+3] = -1.0;
 		}
+
+		// 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 pbr = @:privateAccess pl.pbr;
+			fillVector(lightInfos, pbr.lightColor, i);
+			lightInfos[i+3] = pbr.pointSize;
+			fillVector(lightInfos, pbr.lightPos, i+4);
+			lightInfos[i+7] = pbr.invLightRange4;
+			lightInfos[i+8] = pl.range;
+			lightInfos[i+9] = 1.0;
+			lightInfos[i+10] = pl.shadows.bias;
+			s.pointShadowMaps[li] = pl.shadows.getShadowTex();
+		}
+
+		// 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 pbr = @:privateAccess pl.pbr;
+			fillVector(lightInfos, pbr.lightColor, i);
+			lightInfos[i+3] = pbr.pointSize;
+			fillVector(lightInfos, pbr.lightPos, i+4);
+			lightInfos[i+7] = pbr.invLightRange4;
+			lightInfos[i+8] = pl.range;
+			lightInfos[i+9] = -1.0;
+		}
+
+		// 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 pbr = @:privateAccess sl.pbr;
+			fillVector(lightInfos, pbr.lightColor, i);
+			lightInfos[i+3] = pbr.range;
+			fillVector(lightInfos, pbr.lightPos, i+4);
+			lightInfos[i+7] = pbr.invLightRange4;
+			fillVector(lightInfos, pbr.spotDir, i+8);
+			lightInfos[i+12] = pbr.angle;
+			lightInfos[i+13] = pbr.fallOff;
+			lightInfos[i+14] = 1.0;
+			lightInfos[i+15] = sl.shadows.bias;
+			var mat = sl.shadows.getShadowProj();
+			fillFloats(lightInfos, mat._11, mat._21, mat._31, mat._41, i+16);
+			fillFloats(lightInfos, mat._12, mat._22, mat._32, mat._42, i+20);
+			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();
+		}
+
+		// 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 pbr = @:privateAccess sl.pbr;
+			fillVector(lightInfos, pbr.lightColor, i);
+			lightInfos[i+3] = pbr.range;
+			fillVector(lightInfos, pbr.lightPos, i+4);
+			lightInfos[i+7] = pbr.invLightRange4;
+			fillVector(lightInfos, pbr.spotDir, i+8);
+			lightInfos[i+12] = pbr.angle;
+			lightInfos[i+13] = pbr.fallOff;
+			lightInfos[i+14] = -1.0;
+		}
+
+		s.dirLightCount = dirLights.length + dirLightsShadow.length;
+		s.pointLightCount = pointLights.length + pointLightsShadow.length;
+		s.spotLightCount = spotLights.length + spotLightsShadow.length;
 		s.lightInfos.uploadVector(lightInfos, 0, s.lightInfos.vertices, 0);
 
 		var pbrIndirect = @:privateAccess pbrRenderer.pbrIndirect;

+ 62 - 39
h3d/shader/pbr/DefaultForward.hx

@@ -4,6 +4,10 @@ class DefaultForward extends hxsl.Shader {
 
 	static var SRC = {
 
+		@const(16) var MAX_DIR_SHADOWS:Int;
+		@const(16) var MAX_POINT_SHADOWS:Int;
+		@const(16) var MAX_SPOT_SHADOWS:Int;
+
 		@:import h3d.shader.pbr.Light.LightEvaluation;
 		@:import h3d.shader.pbr.BDRF;
 
@@ -21,9 +25,9 @@ class DefaultForward extends hxsl.Shader {
 		@param var pointLightStride : Int;
 
 		// ShadowMaps
-		//@param var dirShadowMaps : Array<Sampler2D, 2>;
-		//@param var pointShadowMaps : Array<SamplerCube, 3>;
-		//@param var spotShadowMaps : Array<Sampler2D, 3>;
+		@param var dirShadowMaps : Array<Sampler2D, MAX_DIR_SHADOWS>;
+		@param var pointShadowMaps : Array<SamplerCube, MAX_POINT_SHADOWS>;
+		@param var spotShadowMaps : Array<Sampler2D, MAX_SPOT_SHADOWS>;
 
 		// Direct Lighting
 		@param var cameraPosition : Vec3;
@@ -101,65 +105,62 @@ class DefaultForward extends hxsl.Shader {
 			NdV = transformedNormal.dot(view).max(0.);
 		}
 
-		function evaluateDirLight( index : Int ) : Vec3 {
+		function evaluateDirShadow( index : Int ) : Float {
 			var i = index * 5;
-			var lightColor = lightInfos[i].rgb;
-			var lightDir = lightInfos[i+1].xyz;
-			var hasShadowMap = lightInfos[i].a > 0;
 
-			// Shadow
 			var shadow = 1.0;
-			/*if( hasShadowMap ) {
+			if (lightInfos[i].a > 0) {
 				var shadowBias = lightInfos[i+1].a;
 				var shadowProj = mat3x4(lightInfos[i+2], lightInfos[i+3], lightInfos[i+4]);
 				var shadowPos = transformedPosition * shadowProj;
 				var shadowUv = screenToUv(shadowPos.xy);
 				var depth = dirShadowMaps[index].get(shadowUv.xy).r;
 				shadow = (shadowPos.z - shadowBias > depth) ? 0.0 : 1.0;
-			}*/
+			}
+			return shadow;
+		}
 
-			return directLighting(lightColor, lightDir) * shadow;
+		function evaluateDirLight( index : Int ) : Vec3 {
+			var i = index * 5;
+			var lightColor = lightInfos[i].rgb;
+			var lightDir = lightInfos[i+1].xyz;
+
+			return directLighting(lightColor, lightDir);
 		}
 
-		function evaluatePointLight( index : Int ) : Vec3 {
+		function evaluatePointShadow( index : Int ) : Float {
 			var i = index * 3 + dirLightStride;
-			var lightColor = lightInfos[i].rgb;
-			var size = lightInfos[i].a;
-			var lightPos = lightInfos[i+1].rgb;
-			var invRange4 = lightInfos[i+1].a;
-			var range = lightInfos[i+2].r;
-			var hasShadowMap = lightInfos[i+2].g > 0;
-			var delta = lightPos - transformedPosition;
 
-			// Shadow
 			var shadow = 1.0;
-			/*if( hasShadowMap ) {
+			if (lightInfos[i+2].g > 0) {
+				var lightPos = lightInfos[i+1].rgb;
+				var range = lightInfos[i+2].r;
 				var shadowBias = lightInfos[i+2].b;
 				var posToLight = transformedPosition.xyz - lightPos;
 				var dir = normalize(posToLight.xyz);
 				var depth = pointShadowMaps[index].getLod(dir, 0).r * range;
 				var zMax = length(posToLight);
 				shadow = (zMax - shadowBias > depth) ? 0.0 : 1.0;
-			}*/
-
-			return directLighting(pointLightIntensity(delta, size, invRange4) * lightColor, delta.normalize()) * shadow;
+			}
+			return shadow;
 		}
 
-		function evaluateSpotLight( index : Int ) : Vec3 {
-			var i = index * 8 + dirLightStride + pointLightStride;
+		function evaluatePointLight( index : Int ) : Vec3 {
+			var i = index * 3 + dirLightStride;
 			var lightColor = lightInfos[i].rgb;
-			var range = lightInfos[i].a;
-			var lightPos = lightInfos[i+1].xyz;
+			var size = lightInfos[i].a;
+			var lightPos = lightInfos[i+1].rgb;
 			var invRange4 = lightInfos[i+1].a;
-			var lightDir = lightInfos[i+2].xyz;
-			var angle = lightInfos[i+3].r;
-			var fallOff = lightInfos[i+3].g;
-			var hasShadowMap = lightInfos[i+3].b > 0;
 			var delta = lightPos - transformedPosition;
 
-			// Shadow
+			return directLighting(pointLightIntensity(delta, size, invRange4) * lightColor, delta.normalize());
+		}
+
+		function evaluateSpotShadow( index : Int ) : Float {
+			var i = index * 8 + dirLightStride + pointLightStride;
+
 			var shadow = 1.0;
-			/*if( hasShadowMap ) {
+			if (lightInfos[i+3].b > 0) {
 				var shadowBias = lightInfos[i+3].a;
 				var shadowProj = mat4(lightInfos[i+4], lightInfos[i+5], lightInfos[i+6], lightInfos[i+7]);
 				var shadowPos = vec4(transformedPosition, 1.0) * shadowProj;
@@ -167,13 +168,26 @@ class DefaultForward extends hxsl.Shader {
 				var shadowUv = screenToUv(shadowPos.xy);
 				var depth = spotShadowMaps[index].get(shadowUv.xy).r;
 				shadow = (shadowPos.z - shadowBias > depth) ? 0.0 : 1.0;
-			}*/
+			}
+			return shadow;
+		}
+
+		function evaluateSpotLight( index : Int ) : Vec3 {
+			var i = index * 8 + dirLightStride + pointLightStride;
+			var lightColor = lightInfos[i].rgb;
+			var range = lightInfos[i].a;
+			var lightPos = lightInfos[i+1].xyz;
+			var invRange4 = lightInfos[i+1].a;
+			var lightDir = lightInfos[i+2].xyz;
+			var angle = lightInfos[i+3].r;
+			var fallOff = lightInfos[i+3].g;
+			var delta = lightPos - transformedPosition;
 
 			var fallOffInfo = spotLightIntensity(delta, lightDir, range, invRange4, fallOff, angle);
 			var fallOff = fallOffInfo.x;
 			var fallOffInfoAngle = fallOffInfo.y;
 
-			return directLighting(fallOff * lightColor * fallOffInfoAngle, delta.normalize()) * shadow;
+			return directLighting(fallOff * lightColor * fallOffInfoAngle, delta.normalize());
 		}
 
 		function evaluateLighting() : Vec3 {
@@ -182,16 +196,25 @@ class DefaultForward extends hxsl.Shader {
 
 			F0 = mix(pbrSpecularColor, albedoGamma, metalness);
 
+			// Dir Light With Shadow
+			@unroll for( l in 0 ... MAX_DIR_SHADOWS )
+				lightAccumulation += evaluateDirLight(l) * evaluateDirShadow(l);
 			// Dir Light
-			@unroll for( l in 0 ... dirLightCount )
+			@unroll for( l in MAX_DIR_SHADOWS ... dirLightCount + MAX_DIR_SHADOWS )
 				lightAccumulation += evaluateDirLight(l);
 
+			// Point Light With Shadow
+			@unroll for( l in 0 ... MAX_POINT_SHADOWS )
+				lightAccumulation += evaluatePointLight(l) * evaluatePointShadow(l);
 			// Point Light
-			@unroll for( l in 0 ... pointLightCount )
+			@unroll for( l in MAX_POINT_SHADOWS ... pointLightCount + MAX_POINT_SHADOWS )
 				lightAccumulation += evaluatePointLight(l);
 
+			// Spot Light With Shadow
+			@unroll for( l in 0 ... MAX_SPOT_SHADOWS )
+				lightAccumulation += evaluateSpotLight(l) * evaluateSpotShadow(l);
 			// Spot Light
-			@unroll for( l in 0 ... spotLightCount )
+			@unroll for( l in MAX_SPOT_SHADOWS ... spotLightCount + MAX_SPOT_SHADOWS )
 				lightAccumulation += evaluateSpotLight(l);
 
 			// Indirect only support the main env from the scene at the moment