Browse Source

Work on transparent rendering path - DirectX functional

BearishSun 9 years ago
parent
commit
42c00ad036

+ 4 - 0
Data/Raw/Engine/DataList.json

@@ -111,6 +111,10 @@
         {
             "Path": "BasePass.bslinc",
             "UUID": "ec1d3d37-5509-4bcb-9be2-93208d6ae224"
+        },
+        {
+            "Path": "LightGridCommon.bslinc",
+            "UUID": "26caf86e-433c-4ade-9bc5-55c136d82912"
         }
     ],
     "Shaders": [

+ 115 - 0
Data/Raw/Engine/Includes/LightGridCommon.bslinc

@@ -0,0 +1,115 @@
+Technique : base("LightGridCommon") =
+{
+	Language = "HLSL11";
+	
+	Pass =
+	{
+		Common = 
+		{
+			cbuffer GridParams : register(b4)
+			{
+				// Offsets at which specific light types begin in gLights buffer
+				// Assumed directional lights start at 0
+				// x - offset to point lights, y - offset to spot lights, z - total number of lights
+				uint3 gLightOffsets;			
+				uint gNumCells;
+				uint3 gGridSize;
+				uint gMaxNumLightsPerCell;
+				uint2 gGridPixelSize;
+			}
+			
+			float convertToDeviceZ(float viewZ)
+			{
+				return -gDeviceZToWorldZ.y + (gDeviceZToWorldZ.x / viewZ);
+			}
+			
+			float calcViewZFromCellZ(uint cellZ)
+			{
+				// TODO - Need better Z distribution. Currently I uniformly distribute in view space, but this
+				// results in very elongated cells along Z
+				float viewZ = gNearFar.x + (gNearFar.y - gNearFar.x) * cellZ / (float)gGridSize.z;
+				
+				return -viewZ;
+			}
+			
+			uint calcCellZFromViewZ(float viewZ)
+			{
+				// TODO - Need better Z distribution. Currently I uniformly distribute in view space, but this
+				// results in very elongated cells along Z
+				uint cellZ = min((gGridSize.z * (-viewZ - gNearFar.x))/(gNearFar.y - gNearFar.x), gGridSize.z);
+				
+				return cellZ;
+			}
+			
+			uint calcCellIdx(uint2 pixelPos, float deviceZ)
+			{
+				// MARK: API_SPECIFIC
+				// Grid origin is bottom left but DirectX pixel position origin is top left
+				pixelPos.y = gViewportRectangle.w - pixelPos.y;
+			
+				// Note: Use bitshift to divide since gGridPixelSize will be a power of 2
+				uint2 cellXY = pixelPos / gGridPixelSize;
+				uint cellZ = calcCellZFromViewZ(convertFromDeviceZ(deviceZ));
+				
+				uint cellIdx = (cellZ * gGridSize.y + cellXY.y) * gGridSize.x + cellXY.x;
+				return cellIdx;
+			}
+		};
+	};
+};
+
+Technique : base("LightGridCommon") = 
+{
+	Language = "GLSL";
+	
+	Pass =
+	{
+		Common = 
+		{
+			layout(binding = 4, std140) uniform GridParams
+			{
+				// Offsets at which specific light types begin in gLights buffer
+				// Assumed directional lights start at 0
+				// x - offset to point lights, y - offset to spot lights, z - total number of lights
+				uvec3 gLightOffsets;			
+				uint gNumCells;
+				uvec3 gGridSize;
+				uint gMaxNumLightsPerCell;
+				uvec2 gGridPixelSize;
+			};
+			
+			float convertToDeviceZ(float viewZ)
+			{
+				return -gDeviceZToWorldZ.y + (gDeviceZToWorldZ.x / viewZ);
+			}
+			
+			float calcViewZFromCellZ(uint cellZ)
+			{
+				// TODO - Need better Z distribution. Currently I uniformly distribute in view space, but this
+				// results in very elongated cells along Z
+				float viewZ = gNearFar.x + (gNearFar.y - gNearFar.x) * cellZ / float(gGridSize.z);
+				
+				return -viewZ;
+			}
+						
+			uint calcCellZFromViewZ(float viewZ)
+			{
+				// TODO - Need better Z distribution. Currently I uniformly distribute in view space, but this
+				// results in very elongated cells along Z
+				uint cellZ = min(uint((gGridSize.z * (-viewZ - gNearFar.x))/(gNearFar.y - gNearFar.x)), gGridSize.z);
+				
+				return cellZ;
+			}
+			
+			int calcCellIdx(uvec2 pixelPos, float deviceZ)
+			{
+				// Note: Use bitshift to divide since gGridPixelSize will be a power of 2
+				uvec2 cellXY = pixelPos / gGridPixelSize;
+				uint cellZ = calcCellZFromViewZ(convertFromDeviceZ(deviceZ));
+								
+				int cellIdx = int((cellZ * gGridSize.y + cellXY.y) * gGridSize.x + cellXY.x);
+				return cellIdx;
+			}
+		};
+	};
+};

+ 2 - 12
Data/Raw/Engine/Includes/LightingCommon.bslinc

@@ -20,12 +20,7 @@ Technique
 				float radiusSqrdInv;
 				float3 color;
 			};
-			
-			float convertFromDeviceZ(float deviceZ)
-			{
-				return (1.0f / (deviceZ + gDeviceZToWorldZ.y)) * gDeviceZToWorldZ.x;
-			}
-			
+						
 			float getSpotAttenuation(float3 worldPosToLight, float3 direction, float3 angles)
 			{
 				float output = saturate((dot(-worldPosToLight, direction) - angles.y) * angles.z);
@@ -101,12 +96,7 @@ Technique
 				float radiusSqrdInv;
 				vec3 color;
 			};
-			
-			float convertFromDeviceZ(float deviceZ)
-			{
-				return (1.0f / (deviceZ + gDeviceZToWorldZ.y)) * gDeviceZToWorldZ.x;	
-			}
-						
+									
 			float getSpotAttenuation(vec3 worldPosToLight, vec3 direction, vec3 angles)
 			{
 				float atten = clamp((dot(-worldPosToLight, direction) - angles.y) * angles.z, 0.0, 1.0);

+ 10 - 0
Data/Raw/Engine/Includes/PerCameraData.bslinc

@@ -56,6 +56,11 @@ Technique : base("PerCameraData") =
 				int4 	 gViewportRectangle;
 				float4 	 gClipToUVScaleOffset;				
 			}
+			
+			float convertFromDeviceZ(float deviceZ)
+			{
+				return (1.0f / (deviceZ + gDeviceZToWorldZ.y)) * gDeviceZToWorldZ.x;
+			}
 		};
 	};
 };
@@ -83,6 +88,11 @@ Technique : base("PerCameraData") =
 				ivec4 gViewportRectangle;
 				vec4 gClipToUVScaleOffset;				
 			};
+			
+			float convertFromDeviceZ(float deviceZ)
+			{
+				return (1.0f / (deviceZ + gDeviceZToWorldZ.y)) * gDeviceZToWorldZ.x;	
+			}
 		};
 	};
 };

+ 67 - 70
Data/Raw/Engine/Shaders/LightGridLLCreation.bsl

@@ -1,16 +1,19 @@
 #include "$ENGINE$\GBuffer.bslinc"
 #include "$ENGINE$\PerCameraData.bslinc"
 #include "$ENGINE$\LightingCommon.bslinc"
+#include "$ENGINE$\LightGridCommon.bslinc"
 
 Blocks =
 {
 	Block PerCamera : auto("PerCamera");
+	Block GridParams : auto("GridParams");
 };
 
 Technique
  : inherits("GBuffer")
  : inherits("PerCameraData")
- : inherits("LightingCommon") =
+ : inherits("LightingCommon")
+ : inherits("LightGridCommon") =
 {
 	Language = "HLSL11";
 	
@@ -18,36 +21,14 @@ Technique
 	{
 		Compute = 
 		{
-			cbuffer Params : register(b0)
-			{
-				// Offsets at which specific light types begin in gLights buffer
-				// Assumed directional lights start at 0
-				// x - offset to point lights, y - offset to spot lights, z - total number of lights
-				uint3 gLightOffsets;			
-				uint gNumCells;
-				uint3 gGridSize;
-				uint gMaxNumLightsPerCell;
-			}
-			
 			StructuredBuffer<LightData> gLights : register(t0);
 			RWBuffer<uint> gLinkedListCounter : register(u0);
 			RWBuffer<uint> gLinkedListHeads : register(u1);
-			RWBuffer<uint2> gLinkedList : register(u2);
-			
-			float convertToDeviceZ(float viewZ)
-			{
-				return (gDeviceZToWorldZ.x - viewZ * gDeviceZToWorldZ.y) / viewZ;
-			}
-			
-			float calcViewZFromCellZ(uint cellZ)
-			{
-				// TODO - Need better Z distribution. Currently I uniformly distribute in view space, but this
-				// results in very elongated cells along Z
-				float viewZ = gNearFar.x + (gNearFar.y - gNearFar.x) * cellZ / (float)gGridSize.z;
-				
-				return viewZ;
-			}
-		
+			RWBuffer<uint4> gLinkedList : register(u2);
+					
+			// Generates a an axis aligned bounding box in NDC and transforms it to view space.
+			// Note: This will overlap other cells, so it might be better to use frustum planes
+			// instead of AABB, although frustum testing procedure could result in more false positive
 			void calcCellAABB(uint3 cellIdx, out float3 center, out float3 extent)
 			{
 				// Convert grid XY coordinates to clip coordinates
@@ -63,19 +44,40 @@ Technique
 				float flipY = sign(gMatProj[1][1]);
 				ndcMin.y *= flipY;
 				ndcMax.y *= flipY;
-				
+			
 				// Because we're viewing along negative Z, farther end is the minimum
 				float viewZMin = calcViewZFromCellZ(cellIdx.z + 1);
 				float viewZMax = calcViewZFromCellZ(cellIdx.z);
 			
-				ndcMin.z = convertToDeviceZ(viewZMin);
-				ndcMax.z = convertToDeviceZ(viewZMax);
+				ndcMin.z = convertToDeviceZ(viewZMax);
+				ndcMax.z = convertToDeviceZ(viewZMin);
+			
+				float4 corner[8];
+				// Near
+				corner[0] = mul(gMatInvProj, float4(ndcMin.x, ndcMin.y, ndcMin.z, 1.0f));
+				corner[1] = mul(gMatInvProj, float4(ndcMax.x, ndcMin.y, ndcMin.z, 1.0f));
+				corner[2] = mul(gMatInvProj, float4(ndcMax.x, ndcMax.y, ndcMin.z, 1.0f));
+				corner[3] = mul(gMatInvProj, float4(ndcMin.x, ndcMax.y, ndcMin.z, 1.0f));
+			
+				// Far
+				corner[4] = mul(gMatInvProj, float4(ndcMin.x, ndcMin.y, ndcMax.z, 1.0f));
+				corner[5] = mul(gMatInvProj, float4(ndcMax.x, ndcMin.y, ndcMax.z, 1.0f));
+				corner[6] = mul(gMatInvProj, float4(ndcMax.x, ndcMax.y, ndcMax.z, 1.0f));
+				corner[7] = mul(gMatInvProj, float4(ndcMin.x, ndcMax.y, ndcMax.z, 1.0f));
+			
+				[unroll]
+				for(uint i = 0; i < 8; ++i)
+					corner[i].xy /= corner[i].w;
 			
-				float4 clipMin = mul(gMatInvProj, float4(ndcMin, 1.0f));
-				float4 clipMax = mul(gMatInvProj, float4(ndcMax, 1.0f));
+				float3 viewMin = float3(corner[0].xy, viewZMin);
+				float3 viewMax = float3(corner[0].xy, viewZMax);
 				
-				float3 viewMin = float3(clipMin.xy / clipMin.w, viewZMin);
-				float3 viewMax = float3(clipMax.xy / clipMax.w, viewZMax);				
+				[unroll]
+				for(uint i = 1; i < 8; ++i)
+				{
+					viewMin.xy = min(viewMin.xy, corner[i].xy);
+					viewMax.xy = max(viewMax.xy, corner[i].xy);
+				}
 				
 				extent = (viewMax - viewMin) * 0.5f;
 				center = viewMin + extent;
@@ -123,7 +125,7 @@ Technique
 								uint prevLink;
 								InterlockedExchange(gLinkedListHeads[cellIdx], nextLink, prevLink);
 								
-								gLinkedList[nextLink] = uint2(i, prevLink);
+								gLinkedList[nextLink] = uint4(i, type, prevLink, 0);
 							}
 						}
 					}
@@ -136,7 +138,8 @@ Technique
 Technique
  : inherits("GBuffer")
  : inherits("PerCameraData")
- : inherits("LightingCommon") =
+ : inherits("LightingCommon")
+ : inherits("LightGridCommon") =
 {
 	Language = "GLSL";
 	
@@ -146,40 +149,15 @@ Technique
 		{
 			layout (local_size_x = THREADGROUP_SIZE, local_size_y = THREADGROUP_SIZE, local_size_z = THREADGROUP_SIZE) in;
 		
-			layout(binding = 0, std140) uniform Params
-			{
-				// Offsets at which specific light types begin in gLights buffer
-				// Assumed directional lights start at 0
-				// x - offset to point lights, y - offset to spot lights, z - total number of lights
-				uvec3 gLightOffsets;			
-				uint gNumCells;
-				uvec3 gGridSize;
-				uint gMaxNumLightsPerCell;
-			};
-			
-			layout(std430, binding = 1) buffer gLights
+			layout(std430, binding = 1) readonly buffer gLights
 			{
 				LightData[] gLightsData;
 			};
 			
 			layout(binding = 2, r32ui) uniform uimageBuffer gLinkedListCounter;
 			layout(binding = 3, r32ui) uniform uimageBuffer gLinkedListHeads;
-			layout(binding = 4, rg32ui) uniform uimageBuffer gLinkedList;
+			layout(binding = 5, rgba32ui) uniform uimageBuffer gLinkedList;
 			
-			float convertToDeviceZ(float viewZ)
-			{
-				return (gDeviceZToWorldZ.x - viewZ * gDeviceZToWorldZ.y) / viewZ;
-			}
-			
-			float calcViewZFromCellZ(uint cellZ)
-			{
-				// TODO - Need better Z distribution. Currently I uniformly distribute in view space, but this
-				// results in very elongated cells along Z
-				float viewZ = gNearFar.x + (gNearFar.y - gNearFar.x) * cellZ / float(gGridSize.z);
-				
-				return viewZ;
-			}
-		
 			void calcCellAABB(uvec3 cellIdx, out vec3 center, out vec3 extent)
 			{
 				// Convert grid XY coordinates to clip coordinates
@@ -195,19 +173,38 @@ Technique
 				float flipY = sign(gMatProj[1][1]);
 				ndcMin.y *= flipY;
 				ndcMax.y *= flipY;
-				
+			
 				// Because we're viewing along negative Z, farther end is the minimum
 				float viewZMin = calcViewZFromCellZ(cellIdx.z + 1);
 				float viewZMax = calcViewZFromCellZ(cellIdx.z);
 			
 				ndcMin.z = convertToDeviceZ(viewZMin);
 				ndcMax.z = convertToDeviceZ(viewZMax);
+							
+				vec4 corner[8];
+				// Near
+				corner[0] = gMatInvProj * vec4(ndcMin.x, ndcMin.y, ndcMin.z, 1.0f);
+				corner[1] = gMatInvProj * vec4(ndcMax.x, ndcMin.y, ndcMin.z, 1.0f);
+				corner[2] = gMatInvProj * vec4(ndcMax.x, ndcMax.y, ndcMin.z, 1.0f);
+				corner[3] = gMatInvProj * vec4(ndcMin.x, ndcMax.y, ndcMin.z, 1.0f);
+			
+				// Far
+				corner[4] = gMatInvProj * vec4(ndcMin.x, ndcMin.y, ndcMax.z, 1.0f);
+				corner[5] = gMatInvProj * vec4(ndcMax.x, ndcMin.y, ndcMax.z, 1.0f);
+				corner[6] = gMatInvProj * vec4(ndcMax.x, ndcMax.y, ndcMax.z, 1.0f);
+				corner[7] = gMatInvProj * vec4(ndcMin.x, ndcMax.y, ndcMax.z, 1.0f);
 			
-				vec4 clipMin = gMatInvProj * vec4(ndcMin, 1.0f);
-				vec4 clipMax = gMatInvProj * vec4(ndcMax, 1.0f);
+				for(uint i = 0; i < 8; ++i)
+					corner[i].xy /= corner[i].w;
+			
+				vec3 viewMin = vec3(corner[0].xy, viewZMin);
+				vec3 viewMax = vec3(corner[0].xy, viewZMax);
 				
-				vec3 viewMin = vec3(clipMin.xy / clipMin.w, viewZMin);
-				vec3 viewMax = vec3(clipMax.xy / clipMax.w, viewZMax);				
+				for(uint i = 1; i < 8; ++i)
+				{
+					viewMin.xy = min(viewMin.xy, corner[i].xy);
+					viewMax.xy = max(viewMax.xy, corner[i].xy);
+				}		
 				
 				extent = (viewMax - viewMin) * 0.5f;
 				center = viewMin + extent;
@@ -248,7 +245,7 @@ Technique
 							{
 								uint prevLink = imageAtomicExchange(gLinkedListHeads, int(cellIdx), nextLink);
 								
-								imageStore(gLinkedList, int(nextLink), uvec4(i, prevLink, 0, 0));
+								imageStore(gLinkedList, int(nextLink), uvec4(i, type, prevLink, 0));
 							}
 						}
 					}

+ 43 - 46
Data/Raw/Engine/Shaders/LightGridLLReduction.bsl

@@ -1,6 +1,9 @@
 #include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\LightGridCommon.bslinc"
 
-Technique : inherits("PerCameraData") = 
+Technique 
+ : inherits("PerCameraData")
+ : inherits("LightGridCommon") = 
 {
 	Language = "HLSL11";
 	
@@ -8,22 +11,11 @@ Technique : inherits("PerCameraData") =
 	{
 		Compute = 
 		{
-			cbuffer Params : register(b0)
-			{
-				// Offsets at which specific light types begin in gLights buffer
-				// Assumed directional lights start at 0
-				// x - offset to point lights, y - offset to spot lights, z - total number of lights
-				uint3 gLightOffsets;			
-				uint gNumCells;
-				uint3 gGridSize;
-				uint gMaxNumLightsPerCell;
-			}
-			
 			Buffer<uint> gLinkedListHeads : register(t0);
-			Buffer<uint2> gLinkedList : register(t1);
+			Buffer<uint4> gLinkedList : register(t1);
 			
 			RWBuffer<uint> gGridDataCounter : register(u0);
-			RWBuffer<uint2> gGridLightOffsetAndSize : register(u1);
+			RWBuffer<uint4> gGridLightOffsetAndSize : register(u1);
 			RWBuffer<uint> gGridLightIndices : register(u2);
 			
 			[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, THREADGROUP_SIZE)]
@@ -43,29 +35,36 @@ Technique : inherits("PerCameraData") =
 				
 				// First count total number of lights affecting the tile
 				uint currentIdx = gLinkedListHeads[cellIdx];
-				uint numLights = 0;
+				uint numRadialLights = 0;
+				uint numSpotLights = 0;
 				while(currentIdx != 0xFFFFFFFF)
 				{
-					numLights++;
-					currentIdx = gLinkedList[currentIdx].y;
+					uint4 entry = gLinkedList[currentIdx];
+				
+					if(entry.y == 1) // Radial
+						numRadialLights++;
+					else // Spot
+						numSpotLights++;
+
+					currentIdx = entry.z;
 				}
 				
 				// Allocate enough room and remember the offset to indices
+				uint numLights = numRadialLights + numSpotLights;
 				uint indicesStart;
 				InterlockedAdd(gGridDataCounter[0], numLights, indicesStart);
-				gGridLightOffsetAndSize[cellIdx] = uint2(indicesStart, numLights);
+				gGridLightOffsetAndSize[cellIdx] = uint4(indicesStart, numRadialLights, numSpotLights, 0);
 				
-				// Actually write light indices
-				// Note: Values are written in the reverse order than they were found in
+				// Actually write light indices (reverse order, so that radial lights come first, as is the convention)
 				currentIdx = gLinkedListHeads[cellIdx];
 				uint lightIdx = 0;
 				while(currentIdx != 0xFFFFFFFF)
 				{
-					uint2 entry = gLinkedList[currentIdx];
+					uint4 entry = gLinkedList[currentIdx];
 				
-					gGridLightIndices[indicesStart + lightIdx] = entry.x;
+					gGridLightIndices[indicesStart + numLights - 1 - lightIdx] = entry.x;
 					
-					currentIdx = entry.y;
+					currentIdx = entry.z;
 					lightIdx++;
 				}
 			}
@@ -73,7 +72,9 @@ Technique : inherits("PerCameraData") =
 	};
 };
 
-Technique : inherits("PerCameraData") = 
+Technique 
+ : inherits("PerCameraData")
+ : inherits("LightGridCommon") = 
 {
 	Language = "GLSL";
 	
@@ -83,23 +84,12 @@ Technique : inherits("PerCameraData") =
 		{
 			layout (local_size_x = THREADGROUP_SIZE, local_size_y = THREADGROUP_SIZE, local_size_z = THREADGROUP_SIZE) in;
 		
-			layout(binding = 0, std140) uniform Params
-			{
-				// Offsets at which specific light types begin in gLights buffer
-				// Assumed directional lights start at 0
-				// x - offset to point lights, y - offset to spot lights, z - total number of lights
-				uvec3 gLightOffsets;			
-				uint gNumCells;
-				uvec3 gGridSize;
-				uint gMaxNumLightsPerCell;
-			};
-			
 			layout(binding = 1) uniform usamplerBuffer gLinkedListHeads;
 			layout(binding = 2) uniform usamplerBuffer gLinkedList;
 			
 			layout(binding = 3, r32ui) uniform uimageBuffer gGridDataCounter;
-			layout(binding = 4, rg32ui) uniform uimageBuffer gGridLightOffsetAndSize;
-			layout(binding = 5, r32ui) uniform uimageBuffer gGridLightIndices;
+			layout(binding = 5, rgba32ui) uniform uimageBuffer gGridLightOffsetAndSize;
+			layout(binding = 6, r32ui) uniform uimageBuffer gGridLightIndices;
 			
 			void main()
 			{
@@ -114,28 +104,35 @@ Technique : inherits("PerCameraData") =
 				
 				// First count total number of lights affecting the tile
 				int currentIdx = int(texelFetch(gLinkedListHeads, cellIdx).x);
-				uint numLights = 0;
+				uint numRadialLights = 0;
+				uint numSpotLights = 0;
 				while(currentIdx != 0xFFFFFFFF)
 				{
-					numLights++;
-					currentIdx = int(texelFetch(gLinkedList, currentIdx).y);
+					uvec3 entry = texelFetch(gLinkedList, currentIdx).xyz;
+				
+					if(entry.y == 1) // Radial
+						numRadialLights++;
+					else // Spot
+						numSpotLights++;
+
+					currentIdx = int(entry.z);
 				}
 				
 				// Allocate enough room and remember the offset to indices
+				uint numLights = numRadialLights + numSpotLights;
 				uint indicesStart = imageAtomicAdd(gGridDataCounter, 0, numLights);
-				imageStore(gGridLightOffsetAndSize, cellIdx, uvec4(indicesStart, numLights, 0, 0));
+				imageStore(gGridLightOffsetAndSize, cellIdx, uvec4(indicesStart, numRadialLights, numSpotLights, 0));
 
-				// Actually write light indices
-				// Note: Values are written in the reverse order than they were found in
+				// Actually write light indices (reverse order, so that radial lights come first, as is the convention)
 				currentIdx = int(texelFetch(gLinkedListHeads, cellIdx).x);
 				uint lightIdx = 0;
 				while(currentIdx != 0xFFFFFFFF)
 				{
-					uvec2 entry = texelFetch(gLinkedList, currentIdx).xy;
+					uvec3 entry = texelFetch(gLinkedList, currentIdx).xyz;
 				
-					imageStore(gGridLightIndices, int(indicesStart + lightIdx), uvec4(entry.x, 0, 0, 0));
+					imageStore(gGridLightIndices, int(indicesStart + numLights - 1 - lightIdx), uvec4(entry.x, 0, 0, 0));
 					
-					currentIdx = int(entry.y);
+					currentIdx = int(entry.z);
 					lightIdx++;
 				}
 			}

+ 1 - 1
Data/Raw/Engine/Shaders/TiledDeferredLighting.bsl

@@ -329,7 +329,7 @@ Technique
 				return decodeGBuffer(GBufferAData, GBufferBData, deviceZ);
 			}	
 			
-			layout(std430, binding = 4) buffer gLights
+			layout(std430, binding = 4) readonly buffer gLights
 			{
 				LightData[] gLightsData;
 			};

+ 61 - 35
Data/Raw/Engine/Shaders/Transparent.bsl

@@ -1,5 +1,6 @@
 #include "$ENGINE$\BasePass.bslinc"
 #include "$ENGINE$\LightingCommon.bslinc"
+#include "$ENGINE$\LightGridCommon.bslinc"
 
 Parameters =
 {
@@ -21,6 +22,7 @@ Transparent = true;
 
 Technique 
  : inherits("LightingCommon")
+ : inherits("LightGridCommon")
  : base("Surface") =
 {
 	Language = "HLSL11";
@@ -43,16 +45,10 @@ Technique
 			Texture2D gAlbedoTex : register(t0);
 			Texture2D gNormalTex : register(t1);
 			
-			StructuredBuffer<LightData> gLights : register(t2);
-		
-			cbuffer LightParams : register(b4)
-			{
-				// Offsets at which specific light types begin in gLights buffer
-				// Assumed directional lights start at 0
-				// x - offset to point lights, y - offset to spot lights, z - total number of lights
-				uint3 gLightOffsets;
-			}
-			
+			Buffer<uint3> gGridOffsetsAndSize : register(t2);
+			Buffer<uint> gGridLightIndices : register(t3);
+			StructuredBuffer<LightData> gLights : register(t4);
+
 			cbuffer MaterialParams : register(b5)
 			{
 				float gOpacity;
@@ -60,7 +56,7 @@ Technique
 			
 			float4 main(in VStoFS input) : SV_Target0
 			{
-				float3 normal = normalize(gNormalTex.Sample(gNormalSamp, input.uv0) * 2.0f - float3(1, 1, 1));
+				float3 normal = normalize(gNormalTex.Sample(gNormalSamp, input.uv0).xyz * 2.0f - float3(1, 1, 1));
 				float3 worldNormal = calcWorldNormal(input, normal);
 			
 				SurfaceData surfaceData;
@@ -68,14 +64,35 @@ Technique
 				surfaceData.worldNormal.xyz = worldNormal;
 				
 				float3 lightAccumulator = 0;
-				for(uint i = 0; i < gLightOffsets[0]; ++i)
-					lightAccumulator += getDirLightContibution(surfaceData, gLights[i]);
 				
-				for (uint i = gLightOffsets[0]; i < gLightOffsets[1]; ++i)
-					lightAccumulator += getPointLightContribution(input.worldPosition, surfaceData, gLights[i]);
-
-				for(uint i = gLightOffsets[1]; i < gLightOffsets[2]; ++i)
-					lightAccumulator += getSpotLightContribution(input.worldPosition, surfaceData, gLights[i]);
+				// Directional lights
+				for(uint lightIdx = 0; lightIdx < gLightOffsets[0]; ++lightIdx)
+					lightAccumulator += getDirLightContibution(surfaceData, gLights[lightIdx]);
+				
+				uint2 pixelPos = (uint2)input.position.xy;
+				uint cellIdx = calcCellIdx(pixelPos, input.position.z);
+				uint3 offsetAndSize = gGridOffsetsAndSize[cellIdx];
+				
+				// Radial lights
+				uint i = offsetAndSize.x;
+				uint end = offsetAndSize.x + offsetAndSize.y;
+				for(; i < end; i++)
+				{
+					uint lightIndex = gGridLightIndices[i];
+					LightData lightData = gLights[lightIndex];
+					
+					lightAccumulator += getPointLightContribution(input.worldPosition, surfaceData, lightData);
+				}
+				
+				// Spot lights
+				end += offsetAndSize.z;
+				for(; i < end; i++)
+				{
+					uint lightIndex = gGridLightIndices[i];
+					LightData lightData = gLights[lightIndex];
+					
+					lightAccumulator += getSpotLightContribution(input.worldPosition, surfaceData, lightData);
+				}
 				
 				float3 diffuse = surfaceData.albedo.xyz / PI;
 				return float4(diffuse * lightAccumulator, gOpacity); // TODO - Add better lighting model later
@@ -86,6 +103,7 @@ Technique
 
 Technique 
  : inherits("LightingCommon")
+ : inherits("LightGridCommon")
  : base("Surface") =
 {
 	Language = "GLSL";
@@ -107,23 +125,17 @@ Technique
 			layout(location = 2) in vec3 tangentToWorldZ;
 			layout(location = 3) in vec4 tangentToWorldX;			
 		
-			layout(binding = 4) uniform sampler2D gAlbedoTex;
-			layout(binding = 5) uniform sampler2D gNormalTex;
+			layout(binding = 5) uniform sampler2D gAlbedoTex;
+			layout(binding = 6) uniform sampler2D gNormalTex;
 
-			layout(std430, binding = 6) buffer gLights
+			layout(binding = 7) uniform usamplerBuffer gGridOffsetsAndSize;
+			layout(binding = 8) uniform usamplerBuffer gGridLightIndices;
+			layout(std430, binding = 9) readonly buffer gLights
 			{
 				LightData[] gLightsData;
 			};
 						
-			layout(binding = 7, std140) uniform LightParams
-			{
-				// Offsets at which specific light types begin in gLights buffer
-				// Assumed directional lights start at 0
-				// x - offset to point lights, y - offset to spot lights, z - total number of lights
-				uvec3 gLightOffsets;
-			};
-			
-			layout(binding = 8, std140) uniform MaterialParams
+			layout(binding = 10, std140) uniform MaterialParams
 			{
 				float gOpacity;
 			};
@@ -139,6 +151,7 @@ Technique
 				surfaceData.albedo = texture(gAlbedoTex, uv0);
 				surfaceData.worldNormal.xyz = worldNormal;
 				
+				// Directional lights
 				vec3 lightAccumulator = vec3(0, 0, 0);
 				for(uint i = 0; i < gLightOffsets[0]; ++i)
 				{
@@ -146,15 +159,28 @@ Technique
 					lightAccumulator += getDirLightContibution(surfaceData, lightData);
 				}
 				
-				for (uint i = gLightOffsets[0]; i < gLightOffsets[1]; ++i)
+				uvec2 pixelPos = uvec2(gl_FragCoord.xy);
+				int cellIdx = calcCellIdx(pixelPos, gl_FragCoord.z);
+				uvec3 offsetAndSize = texelFetch(gGridOffsetsAndSize, cellIdx).xyz;
+				
+				// Radial lights
+				int i = int(offsetAndSize.x);
+				uint end = offsetAndSize.x + offsetAndSize.y;
+				for(; i < end; i++)
 				{
-					LightData lightData = gLightsData[i];
+					uint lightIndex = texelFetch(gGridLightIndices, i).x;
+					LightData lightData = gLightsData[lightIndex];
+					
 					lightAccumulator += getPointLightContribution(worldPosition, surfaceData, lightData);
 				}
-
-				for(uint i = gLightOffsets[1]; i < gLightOffsets[2]; ++i)
+				
+				// Spot lights
+				end += offsetAndSize.z;
+				for(; i < end; i++)
 				{
-					LightData lightData = gLightsData[i];
+					uint lightIndex = texelFetch(gGridLightIndices, i).x;
+					LightData lightData = gLightsData[lightIndex];
+					
 					lightAccumulator += getSpotLightContribution(worldPosition, surfaceData, lightData);
 				}
 				

+ 22 - 3
Source/BansheeD3D11RenderAPI/Source/BsD3D11GpuBufferView.cpp

@@ -6,6 +6,7 @@
 #include "BsD3D11Device.h"
 #include "BsRenderStats.h"
 #include "BsException.h"
+#include "BsD3D11Mappings.h"
 
 namespace bs { namespace ct
 {
@@ -74,7 +75,14 @@ namespace bs { namespace ct
 		D3D11_SHADER_RESOURCE_VIEW_DESC desc;
 		ZeroMemory(&desc, sizeof(desc));
 
-		if (props.getType() == GBT_STRUCTURED || props.getType() == GBT_STANDARD)
+		if (props.getType() == GBT_STANDARD)
+		{
+			desc.Format = D3D11Mappings::getBF(props.getFormat());
+			desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
+			desc.Buffer.FirstElement = firstElement;
+			desc.Buffer.NumElements = numElements;
+		}
+		else if (props.getType() == GBT_STRUCTURED)
 		{
 			desc.Format = DXGI_FORMAT_UNKNOWN;
 			desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
@@ -121,7 +129,18 @@ namespace bs { namespace ct
 
 		desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
 
-		if (props.getType() == GBT_STRUCTURED || props.getType() == GBT_STANDARD)
+		if (props.getType() == GBT_STANDARD)
+		{
+			desc.Format = D3D11Mappings::getBF(props.getFormat());
+			desc.Buffer.FirstElement = firstElement;
+			desc.Buffer.NumElements = numElements;
+
+			if (useCounter)
+				desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_COUNTER;
+			else
+				desc.Buffer.Flags = 0;
+		} 
+		else if (props.getType() == GBT_STRUCTURED)
 		{
 			desc.Format = DXGI_FORMAT_UNKNOWN;
 			desc.Buffer.FirstElement = firstElement;
@@ -167,4 +186,4 @@ namespace bs { namespace ct
 
 		return uav;
 	}
-}}
+}}

+ 8 - 4
Source/RenderBeast/Include/BsLightGrid.h

@@ -9,6 +9,8 @@
 
 namespace bs { namespace ct
 {
+	class GPULightData;
+
 	/** @addtogroup RenderBeast
 	 *  @{
 	 */
@@ -18,6 +20,7 @@ namespace bs { namespace ct
 		BS_PARAM_BLOCK_ENTRY(INT32, gNumCells)
 		BS_PARAM_BLOCK_ENTRY(Vector3I, gGridSize)
 		BS_PARAM_BLOCK_ENTRY(INT32, gMaxNumLightsPerCell)
+		BS_PARAM_BLOCK_ENTRY(Vector2I, gGridPixelSize)
 	BS_PARAM_BLOCK_END
 
 	extern LightGridParamDef gLightGridParamDefDef;
@@ -96,19 +99,20 @@ namespace bs { namespace ct
 		LightGrid();
 
 		/** Updates the light grid from the provided view. */
-		void updateGrid(const RendererCamera& view, const SPtr<GpuBuffer>& lightsBuffer, UINT32 numDirLights, 
-						UINT32 numRadialLights, UINT32 numSpotLights);
+		void updateGrid(const RendererCamera& view, const GPULightData& lightData);
 
 		/** 
-		 * Returns the buffers containing light indices per grid cell. 
+		 * Returns the buffers containing light indices per grid cell and global grid parameters. 
 		 *
 		 * @param[out]	gridOffsetsAndSize	Flattened array of grid cells, where each entry contains the number of lights
 		 *									affecting that cell, and a index into the @p gridLightIndices buffer.
 		 * @param[out]	gridLightIndices	A list of light indices. Each cell's indices start at specific position and
 		 *									are placed sequentially one after another. Lookup into this array is done
 		 *									through offset & size provided by @p gridOffsetsAndSize. 
+		 * @param[out]	gridParams			Global grid parameter block.
 		 */
-		void getOutputs(SPtr<GpuBuffer>& gridOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices) const;
+		void getOutputs(SPtr<GpuBuffer>& gridOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices, 
+						SPtr<GpuParamBlockBuffer>& gridParams) const;
 
 	private:
 		LightGridLLCreationMat mLLCreationMat;

+ 11 - 0
Source/RenderBeast/Include/BsLightRendering.h

@@ -56,9 +56,20 @@ namespace bs { namespace ct
 		/** Returns a GPU bindable param buffer containing meta-data about light in the ligth buffer. */
 		SPtr<GpuParamBlockBuffer> getParamBuffer() const { return mParamBuffer; }
 
+		/** Returns the number of directional lights in the lights buffer. */
+		UINT32 getNumDirLights() const { return mNumLights[0]; }
+
+		/** Returns the number of radial point lights in the lights buffer. */
+		UINT32 getNumRadialLights() const { return mNumLights[1]; }
+
+		/** Returns the number of spot point lights in the lights buffer. */
+		UINT32 getNumSpotLights() const { return mNumLights[2]; }
+
 	private:
 		SPtr<GpuParamBlockBuffer> mParamBuffer;
 		SPtr<GpuBuffer> mLightBuffer;
+
+		UINT32 mNumLights[3];
 	};
 
 	BS_PARAM_BLOCK_BEGIN(TiledLightingParamDef)

+ 3 - 0
Source/RenderBeast/Include/BsRenderBeast.h

@@ -19,6 +19,8 @@ namespace bs
 
 	namespace ct
 	{
+	class LightGrid;
+
 	/** @addtogroup RenderBeast
 	 *  @{
 	 */
@@ -220,6 +222,7 @@ namespace bs
 		SkyboxMat* mSkyboxMat;
 
 		GPULightData* mGPULightData;
+		LightGrid* mLightGrid;
 
 		ObjectRenderer* mObjectRenderer;
 

+ 10 - 1
Source/RenderBeast/Include/BsRendererObject.h

@@ -65,7 +65,16 @@ namespace bs { namespace ct
 		UINT32 perCameraBindingIdx;
 
 		/** Index to which should the lights param block buffer be bound to. */
-		UINT32 lightParamsBindingIdx;
+		UINT32 gridParamsBindingIdx;
+
+		/** 
+		 * Parameter to which to bind a buffer containing light grid offsets and size, per grid cell. Used for forward
+		 * rendering. 
+		 */
+		GpuParamBuffer gridOffsetsAndSizeParam;
+
+		/** Parameter to which to bind a buffer containing all light indices, as mapped by grid offsets & size. */
+		GpuParamBuffer gridLightIndicesParam;
 
 		/** Parameter to which to bind light buffer used for forward rendering. */
 		GpuParamBuffer lightsBufferParam;

+ 19 - 12
Source/RenderBeast/Source/BsLightGrid.cpp

@@ -6,6 +6,7 @@
 #include "BsRendererUtility.h"
 #include "BsRendererCamera.h"
 #include "BsRenderTargets.h"
+#include "BsLightRendering.h"
 
 namespace bs { namespace ct
 {
@@ -31,6 +32,7 @@ namespace bs { namespace ct
 		desc.format = BF_32X1U;
 		desc.randomGpuWrite = true;
 		desc.type = GBT_STANDARD;
+		desc.elementSize = 0;
 
 		mLinkedListCounter = GpuBuffer::create(desc);
 		mLinkedListCounterParam.set(mLinkedListCounter);
@@ -54,11 +56,12 @@ namespace bs { namespace ct
 			desc.format = BF_32X1U;
 			desc.randomGpuWrite = true;
 			desc.type = GBT_STANDARD;
+			desc.elementSize = 0;
 
 			mLinkedListHeads = GpuBuffer::create(desc);
 			mLinkedListHeadsParam.set(mLinkedListHeads);
 
-			desc.format = BF_32X2U;
+			desc.format = BF_32X4U;
 			desc.elementCount = numCells * MAX_LIGHTS_PER_CELL;
 
 			mLinkedList = GpuBuffer::create(desc);
@@ -78,7 +81,7 @@ namespace bs { namespace ct
 
 		bs_stack_free(headsClearData);
 
-		mParamsSet->setParamBlockBuffer("Params", gridParams, true);
+		mParamsSet->setParamBlockBuffer("GridParams", gridParams, true);
 		mLightBufferParam.set(lightsBuffer);
 	}
 
@@ -122,6 +125,7 @@ namespace bs { namespace ct
 		desc.format = BF_32X1U;
 		desc.randomGpuWrite = true;
 		desc.type = GBT_STANDARD;
+		desc.elementSize = 0;
 
 		mGridDataCounter = GpuBuffer::create(desc);
 		mGridDataCounterParam.set(mGridDataCounter);
@@ -142,14 +146,15 @@ namespace bs { namespace ct
 		{
 			GPU_BUFFER_DESC desc;
 			desc.elementCount = numCells;
-			desc.format = BF_32X1U;
+			desc.format = BF_32X4U;
 			desc.randomGpuWrite = true;
 			desc.type = GBT_STANDARD;
+			desc.elementSize = 0;
 
 			mGridLightOffsetAndSize = GpuBuffer::create(desc);
 			mGridLightOffsetAndSizeParam.set(mGridLightOffsetAndSize);
 
-			desc.format = BF_32X2U;
+			desc.format = BF_32X1U;
 			desc.elementCount = numCells * MAX_LIGHTS_PER_CELL;
 			mGridLightIndices = GpuBuffer::create(desc);
 			mGridLightIndicesParam.set(mGridLightIndices);
@@ -161,7 +166,7 @@ namespace bs { namespace ct
 		UINT32 zero = 0;
 		mGridDataCounter->writeData(0, sizeof(UINT32), &zero, BWT_DISCARD);
 
-		mParamsSet->setParamBlockBuffer("Params", gridParams, true);
+		mParamsSet->setParamBlockBuffer("GridParams", gridParams, true);
 		mLinkedListHeadsParam.set(linkedListHeads);
 		mLinkedListParam.set(linkedList);
 	}
@@ -191,8 +196,7 @@ namespace bs { namespace ct
 		mGridParamBuffer = gLightGridParamDefDef.createBuffer();
 	}
 
-	void LightGrid::updateGrid(const RendererCamera& view, const SPtr<GpuBuffer>& lightsBuffer, UINT32 numDirLights,
-					UINT32 numRadialLights, UINT32 numSpotLights)
+	void LightGrid::updateGrid(const RendererCamera& view, const GPULightData& lightData)
 	{
 		UINT32 width = view.getRenderTargets()->getWidth();
 		UINT32 height = view.getRenderTargets()->getHeight();
@@ -203,9 +207,9 @@ namespace bs { namespace ct
 		gridSize[2] = NUM_Z_SUBDIVIDES;
 
 		Vector3I lightOffsets;
-		lightOffsets[0] = numDirLights;
-		lightOffsets[1] = lightOffsets[0] + numRadialLights;
-		lightOffsets[2] = lightOffsets[1] + numSpotLights;
+		lightOffsets[0] = lightData.getNumDirLights();
+		lightOffsets[1] = lightOffsets[0] + lightData.getNumRadialLights();
+		lightOffsets[2] = lightOffsets[1] + lightData.getNumSpotLights();
 
 		UINT32 numCells = gridSize[0] * gridSize[1] * gridSize[2];
 
@@ -213,8 +217,9 @@ namespace bs { namespace ct
 		gLightGridParamDefDef.gNumCells.set(mGridParamBuffer, numCells);
 		gLightGridParamDefDef.gGridSize.set(mGridParamBuffer, gridSize);
 		gLightGridParamDefDef.gMaxNumLightsPerCell.set(mGridParamBuffer, MAX_LIGHTS_PER_CELL);
+		gLightGridParamDefDef.gGridPixelSize.set(mGridParamBuffer, Vector2I(CELL_XY_SIZE, CELL_XY_SIZE));
 
-		mLLCreationMat.setParams(gridSize, mGridParamBuffer, lightsBuffer);
+		mLLCreationMat.setParams(gridSize, mGridParamBuffer, lightData.getLightBuffer());
 		mLLCreationMat.execute(view);
 
 		SPtr<GpuBuffer> linkedListHeads;
@@ -225,8 +230,10 @@ namespace bs { namespace ct
 		mLLReductionMat.execute(view);
 	}
 
-	void LightGrid::getOutputs(SPtr<GpuBuffer>& gridOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices) const
+	void LightGrid::getOutputs(SPtr<GpuBuffer>& gridOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices, 
+							   SPtr<GpuParamBlockBuffer>& gridParams) const
 	{
 		mLLReductionMat.getOutputs(gridOffsetsAndSize, gridLightIndices);
+		gridParams = mGridParamBuffer;
 	}
 }}

+ 5 - 0
Source/RenderBeast/Source/BsLightRendering.cpp

@@ -39,6 +39,7 @@ namespace bs { namespace ct
 	}
 
 	GPULightData::GPULightData()
+		:mNumLights {}
 	{
 		mParamBuffer = gTiledLightingParamDef.createBuffer();
 	}
@@ -46,6 +47,10 @@ namespace bs { namespace ct
 	void GPULightData::setLights(const Vector<LightData>& lightData, UINT32 numDirLights, UINT32 numRadialLights,
 				   UINT32 numSpotLights)
 	{
+		mNumLights[0] = numDirLights;
+		mNumLights[1] = numRadialLights;
+		mNumLights[2] = numSpotLights;
+
 		Vector3I lightOffsets;
 		lightOffsets[0] = numDirLights;
 		lightOffsets[1] = lightOffsets[0] + numRadialLights;

+ 9 - 3
Source/RenderBeast/Source/BsObjectRendering.cpp

@@ -27,7 +27,7 @@ namespace bs { namespace ct
 		if (shader == nullptr)
 		{
 			element.perCameraBindingIdx = -1;
-			element.lightParamsBindingIdx = -1;
+			element.gridParamsBindingIdx = -1;
 
 			LOGWRN("Missing shader on material.");
 			return;
@@ -57,12 +57,18 @@ namespace bs { namespace ct
 			}
 		}
 
-		element.lightParamsBindingIdx = element.params->getParamBlockBufferIndex("LightParams");
+		element.gridParamsBindingIdx = element.params->getParamBlockBufferIndex("GridParams");
 
 		SPtr<GpuParams> gpuParams = element.params->getGpuParams();
-		if(gpuParams->hasBuffer(GPT_FRAGMENT_PROGRAM, "gLights"))
+		if (gpuParams->hasBuffer(GPT_FRAGMENT_PROGRAM, "gLights"))
 			gpuParams->getBufferParam(GPT_FRAGMENT_PROGRAM, "gLights", element.lightsBufferParam);
 
+		if(gpuParams->hasBuffer(GPT_FRAGMENT_PROGRAM, "gGridOffsetsAndSize"))
+			gpuParams->getBufferParam(GPT_FRAGMENT_PROGRAM, "gGridOffsetsAndSize", element.gridOffsetsAndSizeParam);
+
+		if (gpuParams->hasBuffer(GPT_FRAGMENT_PROGRAM, "gGridLightIndices"))
+			gpuParams->getBufferParam(GPT_FRAGMENT_PROGRAM, "gGridLightIndices", element.gridLightIndicesParam);
+
 		const Map<String, SHADER_OBJECT_PARAM_DESC>& bufferDescs = shader->getBufferParams();
 		String boneMatricesParamName;
 

+ 18 - 13
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -33,6 +33,7 @@
 #include "BsRendererExtension.h"
 #include "BsReflectionCubemap.h"
 #include "BsMeshData.h"
+#include "BsLightGrid.h"
 
 using namespace std::placeholders;
 
@@ -40,7 +41,8 @@ namespace bs { namespace ct
 {
 	RenderBeast::RenderBeast()
 		: mDefaultMaterial(nullptr), mTiledDeferredLightingMat(nullptr), mSkyboxMat(nullptr), mGPULightData(nullptr)
-		, mObjectRenderer(nullptr), mOptions(bs_shared_ptr_new<RenderBeastOptions>()), mOptionsDirty(true)
+		, mLightGrid(nullptr), mObjectRenderer(nullptr), mOptions(bs_shared_ptr_new<RenderBeastOptions>())
+		, mOptionsDirty(true)
 	{ }
 
 	const StringID& RenderBeast::getName() const
@@ -76,6 +78,7 @@ namespace bs { namespace ct
 		mSkyboxMat = bs_new<SkyboxMat>();
 
 		mGPULightData = bs_new<GPULightData>();
+		mLightGrid = bs_new<LightGrid>();
 
 		RenderTexturePool::startUp();
 		PostProcessing::startUp();
@@ -104,6 +107,7 @@ namespace bs { namespace ct
 		bs_delete(mTiledDeferredLightingMat);
 		bs_delete(mSkyboxMat);
 		bs_delete(mGPULightData);
+		bs_delete(mLightGrid);
 
 		RendererUtility::shutDown();
 
@@ -756,15 +760,7 @@ namespace bs { namespace ct
 			// Note: Could this step be moved in notifyRenderableUpdated, so it only triggers when material actually gets
 			// changed? Although it shouldn't matter much because if the internal versions keeping track of dirty params.
 			for (auto& element : mRenderables[i]->elements)
-			{
-				bool isTransparent = (element.material->getShader()->getFlags() & (UINT32)ShaderFlags::Transparent) != 0;
-				if(isTransparent)
-				{
-					
-				}
-
 				element.material->updateParamsSet(element.params);
-			}
 
 			mRenderables[i]->perObjectParamBuffer->flushToGPU();
 		}
@@ -789,6 +785,15 @@ namespace bs { namespace ct
 
 		Matrix4 viewProj = viewInfo->getViewProjMatrix();
 
+		viewInfo->beginRendering(true);
+
+		// Prepare light grid required for transparent object rendering
+		mLightGrid->updateGrid(*viewInfo, *mGPULightData);
+
+		SPtr<GpuParamBlockBuffer> gridParams;
+		SPtr<GpuBuffer> gridOffsetsAndSize, gridLightIndices;
+		mLightGrid->getOutputs(gridOffsetsAndSize, gridLightIndices, gridParams);
+
 		// Assign camera and per-call data to all relevant renderables
 		const VisibilityInfo& visibility = viewInfo->getVisibilityMasks();
 		UINT32 numRenderables = (UINT32)mRenderables.size();
@@ -805,15 +810,15 @@ namespace bs { namespace ct
 				if (element.perCameraBindingIdx != -1)
 					element.params->setParamBlockBuffer(element.perCameraBindingIdx, perCameraBuffer, true);
 
-				if (element.lightParamsBindingIdx != -1)
-					element.params->setParamBlockBuffer(element.lightParamsBindingIdx, mGPULightData->getParamBuffer(), true);
+				if (element.gridParamsBindingIdx != -1)
+					element.params->setParamBlockBuffer(element.gridParamsBindingIdx, gridParams, true);
 
+				element.gridOffsetsAndSizeParam.set(gridOffsetsAndSize);
+				element.gridLightIndicesParam.set(gridLightIndices);
 				element.lightsBufferParam.set(mGPULightData->getLightBuffer());
 			}
 		}
 
-		viewInfo->beginRendering(true);
-
 		SPtr<RenderTargets> renderTargets = viewInfo->getRenderTargets();
 		renderTargets->bindGBuffer();