Просмотр исходного кода

Added code & shaders for generating a view-space light grid for use in forward shading, primarily meant for transparency (WIP)
OpenGL: GLSL code uint/int images & samplers are now properly parsed

BearishSun 9 лет назад
Родитель
Сommit
565cbef7dc

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

@@ -197,6 +197,14 @@
         {
         {
             "Path": "TiledDeferredLighting.bsl",
             "Path": "TiledDeferredLighting.bsl",
             "UUID": "787d7293-f335-4eda-a897-c706e6b5c818"
             "UUID": "787d7293-f335-4eda-a897-c706e6b5c818"
+        },
+        {
+            "Path": "LightGridLLCreation.bsl",
+            "UUID": "097acd4f-9eeb-4a25-bbd8-e3ee31118a0f"
+        },
+        {
+            "Path": "LightGridLLReduction.bsl",
+            "UUID": "d190e4e1-5ea9-4961-9ee3-b5df218cb5e6"
         }
         }
     ],
     ],
     "Skin": [
     "Skin": [

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

@@ -16,6 +16,9 @@ Parameters =
 	// Converts device Z to world Z using this formula: worldZ = (1 / (deviceZ + y)) * x
 	// Converts device Z to world Z using this formula: worldZ = (1 / (deviceZ + y)) * x
 	float2		gDeviceZToWorldZ : auto("DeviceToWorldZ");
 	float2		gDeviceZToWorldZ : auto("DeviceToWorldZ");
 	
 	
+	// x - near plane distance, y - far plane distance
+	float2		gNearFar : auto("NearFar");
+	
 	// xy - Viewport offset in pixels
 	// xy - Viewport offset in pixels
 	// zw - Viewport width & height in pixels
 	// zw - Viewport width & height in pixels
 	int4 		gViewportRectangle : auto("ViewportRect");
 	int4 		gViewportRectangle : auto("ViewportRect");
@@ -49,6 +52,7 @@ Technique : base("PerCameraData") =
 				float4x4 gMatInvViewProj;
 				float4x4 gMatInvViewProj;
 				float4x4 gMatScreenToWorld;
 				float4x4 gMatScreenToWorld;
 				float2 	 gDeviceZToWorldZ;
 				float2 	 gDeviceZToWorldZ;
+				float2	 gNearFar;
 				int4 	 gViewportRectangle;
 				int4 	 gViewportRectangle;
 				float4 	 gClipToUVScaleOffset;				
 				float4 	 gClipToUVScaleOffset;				
 			}
 			}
@@ -75,6 +79,7 @@ Technique : base("PerCameraData") =
 				mat4 gMatInvViewProj;
 				mat4 gMatInvViewProj;
 				mat4 gMatScreenToWorld;
 				mat4 gMatScreenToWorld;
 				vec2 gDeviceZToWorldZ;
 				vec2 gDeviceZToWorldZ;
+				vec2 gNearFar;
 				ivec4 gViewportRectangle;
 				ivec4 gViewportRectangle;
 				vec4 gClipToUVScaleOffset;				
 				vec4 gClipToUVScaleOffset;				
 			};
 			};

+ 256 - 0
Data/Raw/Engine/Shaders/LightGridLLCreation.bsl

@@ -0,0 +1,256 @@
+#include "$ENGINE$\GBuffer.bslinc"
+#include "$ENGINE$\LightingCommon.bslinc"
+
+Blocks =
+{
+	Block PerCamera : auto("PerCamera");
+};
+
+Technique
+ : inherits("GBuffer")
+ : inherits("LightingCommon") =
+{
+	Language = "HLSL11";
+	
+	Pass =
+	{
+		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;
+			}
+		
+			void calcCellAABB(uint3 cellIdx, out float3 center, out float3 extent)
+			{
+				// Convert grid XY coordinates to clip coordinates
+				float2 a = 2.0f / gGridSize.xy;
+			
+				float3 ndcMin;
+				float3 ndcMax;
+			
+				ndcMin.xy = cellIdx.xy * a - float2(1.0f, 1.0f);
+				ndcMax.xy = (cellIdx.xy + 1) * a - float2(1.0f, 1.0f);
+			
+				// Flip Y depending on render API, depending if Y in NDC is facing up or down
+				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);
+			
+				float4 clipMin = mul(gMatInvProj, float4(ndcMin, 1.0f));
+				float4 clipMax = mul(gMatInvProj, float4(ndcMax, 1.0f));
+				
+				float3 viewMin = float3(clipMin.xy / clipMin.w, viewZMin);
+				float3 viewMax = float3(clipMax.xy / clipMax.w, viewZMax);				
+				
+				extent = (viewMax - viewMin) * 0.5f;
+				center = viewMin + extent;
+			}
+		
+			[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, THREADGROUP_SIZE)]
+			void main(
+				uint3 groupId : SV_GroupID,
+				uint3 groupThreadId : SV_GroupThreadID,
+				uint3 dispatchThreadId : SV_DispatchThreadID)
+			{
+				uint2 viewportMax = gViewportRectangle.xy + gViewportRectangle.zw;
+
+				// Ignore pixels out of valid range
+				if (all(dispatchThreadId.xy >= viewportMax))
+					return;
+					
+				uint maxNumLinks = gNumCells * gMaxNumLightsPerCell;	
+				uint cellIdx = (dispatchThreadId.z * gGridSize.y + dispatchThreadId.y) * gGridSize.x + dispatchThreadId.x;
+				
+				float3 cellCenter;
+				float3 cellExtent;
+				calcCellAABB(dispatchThreadId, cellCenter, cellExtent);
+				
+				for(uint type = 1; type < 3; ++type)
+				{
+					uint lightOffset = gLightOffsets[type - 1];
+					uint lightEnd = gLightOffsets[type];
+					for(uint i = lightOffset; i < lightEnd; ++i)
+					{
+						float4 lightPosition = mul(gMatView, float4(gLights[i].position, 1.0f));
+						float lightRadius = gLights[i].radius;
+						
+						// Calculate distance from box to light
+						float3 distances = max(abs(lightPosition - cellCenter) - cellExtent, 0);
+						float distSqrd = dot(distances, distances);
+						
+						if(distSqrd <= (lightRadius * lightRadius))
+						{
+							uint nextLink;
+							InterlockedAdd(gLinkedListCounter[0], 1U, nextLink);
+							
+							if(nextLink < maxNumLinks)
+							{
+								uint prevLink;
+								InterlockedExchange(gLinkedListHeads[cellIdx], nextLink, prevLink);
+								
+								gLinkedList[nextLink] = uint2(i, prevLink);
+							}
+						}
+					}
+				}
+			}
+		};
+	};
+};
+
+Technique
+ : inherits("GBuffer")
+ : inherits("LightingCommon") =
+{
+	Language = "GLSL";
+	
+	Pass =
+	{
+		Compute = 
+		{
+			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
+			{
+				LightData[] gLightsData;
+			};
+			
+			layout(binding = 2, r32ui) uniform uimageBuffer gLinkedListCounter;
+			layout(binding = 3, r32ui) uniform uimageBuffer gLinkedListHeads;
+			layout(binding = 4, rg32ui) 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
+				vec2 a = 2.0f / gGridSize.xy;
+			
+				vec3 ndcMin;
+				vec3 ndcMax;
+			
+				ndcMin.xy = cellIdx.xy * a - vec2(1.0f, 1.0f);
+				ndcMax.xy = (cellIdx.xy + 1) * a - vec2(1.0f, 1.0f);
+			
+				// Flip Y depending on render API, depending if Y in NDC is facing up or down
+				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 clipMin = gMatInvProj * vec4(ndcMin, 1.0f);
+				vec4 clipMax = gMatInvProj * vec4(ndcMax, 1.0f);
+				
+				vec3 viewMin = vec3(clipMin.xy / clipMin.w, viewZMin);
+				vec3 viewMax = vec3(clipMax.xy / clipMax.w, viewZMax);				
+				
+				extent = (viewMax - viewMin) * 0.5f;
+				center = viewMin + extent;
+			}
+
+			void main()
+			{
+				uvec2 viewportMax = gViewportRectangle.xy + gViewportRectangle.zw;
+
+				// Ignore pixels out of valid range
+				if (all(greaterThanEqual(gl_GlobalInvocationID.xy, viewportMax)))
+					return;
+					
+				uint maxNumLinks = gNumCells * gMaxNumLightsPerCell;	
+				uint cellIdx = (gl_GlobalInvocationID.z * gGridSize.y + gl_GlobalInvocationID.y) * gGridSize.x + gl_GlobalInvocationID.x;
+				
+				vec3 cellCenter;
+				vec3 cellExtent;
+				calcCellAABB(gl_GlobalInvocationID, cellCenter, cellExtent);
+				
+				for(uint type = 1; type < 3; ++type)
+				{
+					uint lightOffset = gLightOffsets[type - 1];
+					uint lightEnd = gLightOffsets[type];
+					for(uint i = lightOffset; i < lightEnd; ++i)
+					{
+						vec4 lightPosition = gMatView * vec4(gLightsData[i].position, 1.0f);
+						float lightRadius = gLightsData[i].radius;
+						
+						// Calculate distance from box to light
+						vec3 distances = max(abs(lightPosition.xyz - cellCenter) - cellExtent, 0);
+						float distSqrd = dot(distances, distances);
+						
+						if(distSqrd <= (lightRadius * lightRadius))
+						{
+							uint nextLink = imageAtomicAdd(gLinkedListCounter, 0, 1U);
+							if(nextLink < maxNumLinks)
+							{
+								uint prevLink = imageAtomicExchange(gLinkedListHeads, int(cellIdx), nextLink);
+								
+								imageStore(gLinkedList, int(nextLink), uvec4(i, prevLink, 0, 0));
+							}
+						}
+					}
+				}
+			}
+		};
+	};
+};

+ 142 - 0
Data/Raw/Engine/Shaders/LightGridLLReduction.bsl

@@ -0,0 +1,142 @@
+Technique
+{
+	Language = "HLSL11";
+	
+	Pass =
+	{
+		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);
+			
+			RWBuffer<uint> gGridDataCounter : register(u0);
+			RWBuffer<uint2> gGridLightOffsetAndSize : register(u1);
+			RWBuffer<uint> gGridLightIndices : register(u2);
+			
+			[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, THREADGROUP_SIZE)]
+			void main(
+				uint3 groupId : SV_GroupID,
+				uint3 groupThreadId : SV_GroupThreadID,
+				uint3 dispatchThreadId : SV_DispatchThreadID)
+			{
+				uint2 viewportMax = gViewportRectangle.xy + gViewportRectangle.zw;
+
+				// Ignore pixels out of valid range
+				if (all(dispatchThreadId.xy >= viewportMax))
+					return;
+					
+				uint maxNumLinks = gNumCells * gMaxNumLightsPerCell;	
+				uint cellIdx = (dispatchThreadId.z * gGridSize.y + dispatchThreadId.y) * gGridSize.x + dispatchThreadId.x;
+				
+				// First count total number of lights affecting the tile
+				uint currentIdx = gLinkedListHeads[cellIdx];
+				uint numLights = 0;
+				while(currentIdx != 0xFFFFFFFF)
+				{
+					numLights++;
+					currentIdx = gLinkedList[currentIdx].y;
+				}
+				
+				// Allocate enough room and remember the offset to indices
+				uint indicesStart;
+				InterlockedAdd(gGridDataCounter[0], numLights, indicesStart);
+				gGridLightOffsetAndSize[cellIdx] = uint2(indicesStart, numLights);
+				
+				// Actually write light indices
+				// Note: Values are written in the reverse order than they were found in
+				currentIdx = gLinkedListHeads[cellIdx];
+				uint lightIdx = 0;
+				while(currentIdx != 0xFFFFFFFF)
+				{
+					uint2 entry = gLinkedList[currentIdx];
+				
+					gGridLightIndices[indicesStart + lightIdx] = entry.x;
+					
+					currentIdx = entry.y;
+					lightIdx++;
+				}
+			}
+		};
+	};
+};
+
+Technique
+{
+	Language = "GLSL";
+	
+	Pass =
+	{
+		Compute = 
+		{
+			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;
+			
+			void main()
+			{
+				uvec2 viewportMax = gViewportRectangle.xy + gViewportRectangle.zw;
+
+				// Ignore pixels out of valid range
+				if (all(greaterThanEqual(gl_GlobalInvocationID.xy, viewportMax)))
+					return;
+					
+				uint maxNumLinks = gNumCells * gMaxNumLightsPerCell;	
+				uint cellIdx = (gl_GlobalInvocationID.z * gGridSize.y + gl_GlobalInvocationID.y) * gGridSize.x + gl_GlobalInvocationID.x;
+				
+				// First count total number of lights affecting the tile
+				uint currentIdx = texelFetch(gLinkedListHeads, cellIdx).x;
+				uint numLights = 0;
+				while(currentIdx != 0xFFFFFFFF)
+				{
+					numLights++;
+					currentIdx = texelFetch(gLinkedList, currentIdx).y;
+				}
+				
+				// Allocate enough room and remember the offset to indices
+				uint indicesStart = imageAtomicAdd(gGridDataCounter, 0, numLights);
+				imageStore(gGridLightOffsetAndSize, cellIdx, uvec4(indicesStart, numLights, 0, 0));
+
+				// Actually write light indices
+				// Note: Values are written in the reverse order than they were found in
+				currentIdx = texelFetch(gLinkedListHeads, cellIdx).x;
+				uint lightIdx = 0;
+				while(currentIdx != 0xFFFFFFFF)
+				{
+					uvec2 entry = texelFetch(gLinkedList, currentIdx).xy;
+				
+					imageStore(gGridLightIndices, indicesStart + lightIdx, uvec4(entry.x, 0, 0, 0));
+					
+					currentIdx = entry.y;
+					lightIdx++;
+				}
+			}
+		};
+	};
+};

+ 32 - 0
Source/BansheeGLRenderAPI/Source/BsGLSLParamParser.cpp

@@ -316,6 +316,10 @@ namespace bs { namespace ct
 			case GL_SAMPLER_1D_SHADOW:
 			case GL_SAMPLER_1D_SHADOW:
 			case GL_SAMPLER_1D_ARRAY:
 			case GL_SAMPLER_1D_ARRAY:
 			case GL_SAMPLER_1D_ARRAY_SHADOW:
 			case GL_SAMPLER_1D_ARRAY_SHADOW:
+			case GL_UNSIGNED_INT_SAMPLER_1D:
+			case GL_INT_SAMPLER_1D:
+			case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+			case GL_INT_SAMPLER_1D_ARRAY:
 				samplerType = GPOT_SAMPLER1D;
 				samplerType = GPOT_SAMPLER1D;
 				textureType = GPOT_TEXTURE1D;
 				textureType = GPOT_TEXTURE1D;
 				isSampler = true;
 				isSampler = true;
@@ -324,17 +328,27 @@ namespace bs { namespace ct
 			case GL_SAMPLER_2D_SHADOW:
 			case GL_SAMPLER_2D_SHADOW:
 			case GL_SAMPLER_2D_ARRAY:
 			case GL_SAMPLER_2D_ARRAY:
 			case GL_SAMPLER_2D_ARRAY_SHADOW:
 			case GL_SAMPLER_2D_ARRAY_SHADOW:
+			case GL_UNSIGNED_INT_SAMPLER_2D:
+			case GL_INT_SAMPLER_2D:
+			case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+			case GL_INT_SAMPLER_2D_ARRAY:
 				samplerType = GPOT_SAMPLER2D;
 				samplerType = GPOT_SAMPLER2D;
 				textureType = GPOT_TEXTURE2D;
 				textureType = GPOT_TEXTURE2D;
 				isSampler = true;
 				isSampler = true;
 				break;
 				break;
 			case GL_SAMPLER_2D_MULTISAMPLE:
 			case GL_SAMPLER_2D_MULTISAMPLE:
 			case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
 			case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
+			case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+			case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
+			case GL_INT_SAMPLER_2D_MULTISAMPLE:
+			case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
 				samplerType = GPOT_SAMPLER2DMS;
 				samplerType = GPOT_SAMPLER2DMS;
 				textureType = GPOT_TEXTURE2DMS;
 				textureType = GPOT_TEXTURE2DMS;
 				isSampler = true;
 				isSampler = true;
 				break;
 				break;
 			case GL_SAMPLER_3D:
 			case GL_SAMPLER_3D:
+			case GL_UNSIGNED_INT_SAMPLER_3D:
+			case GL_INT_SAMPLER_3D:
 				samplerType = GPOT_SAMPLER3D;
 				samplerType = GPOT_SAMPLER3D;
 				textureType = GPOT_TEXTURE3D;
 				textureType = GPOT_TEXTURE3D;
 				isSampler = true;
 				isSampler = true;
@@ -343,31 +357,49 @@ namespace bs { namespace ct
 			case GL_SAMPLER_CUBE_MAP_ARRAY:
 			case GL_SAMPLER_CUBE_MAP_ARRAY:
 			case GL_SAMPLER_CUBE_SHADOW:
 			case GL_SAMPLER_CUBE_SHADOW:
 			case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
 			case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
+			case GL_UNSIGNED_INT_SAMPLER_CUBE:
+			case GL_INT_SAMPLER_CUBE:
+			case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY:
+			case GL_INT_SAMPLER_CUBE_MAP_ARRAY:
 				samplerType = GPOT_SAMPLERCUBE;
 				samplerType = GPOT_SAMPLERCUBE;
 				textureType = GPOT_TEXTURECUBE;
 				textureType = GPOT_TEXTURECUBE;
 				isSampler = true;
 				isSampler = true;
 				break;
 				break;
 			case GL_IMAGE_1D:
 			case GL_IMAGE_1D:
+			case GL_UNSIGNED_INT_IMAGE_1D:
+			case GL_INT_IMAGE_1D:
 			case GL_IMAGE_1D_ARRAY:
 			case GL_IMAGE_1D_ARRAY:
+			case GL_UNSIGNED_INT_IMAGE_1D_ARRAY:
+			case GL_INT_IMAGE_1D_ARRAY:
 				textureType = GPOT_RWTEXTURE1D;
 				textureType = GPOT_RWTEXTURE1D;
 				isImage = true;
 				isImage = true;
 				break;
 				break;
 			case GL_IMAGE_2D:
 			case GL_IMAGE_2D:
+			case GL_UNSIGNED_INT_IMAGE_2D:
+			case GL_INT_IMAGE_2D:
 			case GL_IMAGE_2D_ARRAY:
 			case GL_IMAGE_2D_ARRAY:
+			case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+			case GL_INT_IMAGE_2D_ARRAY:
 				textureType = GPOT_RWTEXTURE2D;
 				textureType = GPOT_RWTEXTURE2D;
 				isImage = true;
 				isImage = true;
 				break;
 				break;
 			case GL_IMAGE_2D_MULTISAMPLE:
 			case GL_IMAGE_2D_MULTISAMPLE:
 			case GL_IMAGE_2D_MULTISAMPLE_ARRAY:
 			case GL_IMAGE_2D_MULTISAMPLE_ARRAY:
+			case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE:
+			case GL_INT_IMAGE_2D_MULTISAMPLE:
 				textureType = GPOT_RWTEXTURE2DMS;
 				textureType = GPOT_RWTEXTURE2DMS;
 				isImage = true;
 				isImage = true;
 				break;
 				break;
 			case GL_IMAGE_3D:
 			case GL_IMAGE_3D:
+			case GL_UNSIGNED_INT_IMAGE_3D:
+			case GL_INT_IMAGE_3D:
 				textureType = GPOT_RWTEXTURE3D;
 				textureType = GPOT_RWTEXTURE3D;
 				isImage = true;
 				isImage = true;
 				break;
 				break;
 			case GL_SAMPLER_BUFFER:
 			case GL_SAMPLER_BUFFER:
 			case GL_IMAGE_BUFFER:
 			case GL_IMAGE_BUFFER:
+			case GL_UNSIGNED_INT_IMAGE_BUFFER:
+			case GL_INT_IMAGE_BUFFER:
 				isBuffer = true;
 				isBuffer = true;
 				break;
 				break;
 			}
 			}

+ 2 - 0
Source/RenderBeast/CMakeSources.cmake

@@ -12,6 +12,7 @@ set(BS_RENDERBEAST_INC_NOFILTER
 	"Include/BsRendererCamera.h"
 	"Include/BsRendererCamera.h"
 	"Include/BsRendererObject.h"
 	"Include/BsRendererObject.h"
 	"Include/BsReflectionCubemap.h"
 	"Include/BsReflectionCubemap.h"
+	"Include/BsLightGrid.h"
 )
 )
 
 
 set(BS_RENDERBEAST_SRC_NOFILTER
 set(BS_RENDERBEAST_SRC_NOFILTER
@@ -27,6 +28,7 @@ set(BS_RENDERBEAST_SRC_NOFILTER
 	"Source/BsRendererCamera.cpp"
 	"Source/BsRendererCamera.cpp"
 	"Source/BsRendererObject.cpp"
 	"Source/BsRendererObject.cpp"
 	"Source/BsReflectionCubemap.cpp"
 	"Source/BsReflectionCubemap.cpp"
+	"Source/BsLightGrid.cpp"
 )
 )
 
 
 source_group("Header Files" FILES ${BS_RENDERBEAST_INC_NOFILTER})
 source_group("Header Files" FILES ${BS_RENDERBEAST_INC_NOFILTER})

+ 121 - 0
Source/RenderBeast/Include/BsLightGrid.h

@@ -0,0 +1,121 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsRenderBeastPrerequisites.h"
+#include "BsRendererMaterial.h"
+#include "BsParamBlocks.h"
+#include "BsVectorNI.h"
+
+namespace bs { namespace ct
+{
+	/** @addtogroup RenderBeast
+	 *  @{
+	 */
+
+	BS_PARAM_BLOCK_BEGIN(LightGridParamDef)
+		BS_PARAM_BLOCK_ENTRY(Vector3I, gLightOffsets)
+		BS_PARAM_BLOCK_ENTRY(INT32, gNumCells)
+		BS_PARAM_BLOCK_ENTRY(Vector3I, gGridSize)
+		BS_PARAM_BLOCK_ENTRY(INT32, gMaxNumLightsPerCell)
+	BS_PARAM_BLOCK_END
+
+	extern LightGridParamDef gLightGridParamDefDef;
+
+	/** Shader that creates a linked list for each light grid cell, containing which light affects each cell. */
+	class LightGridLLCreationMat : public RendererMaterial<LightGridLLCreationMat>
+	{
+		RMAT_DEF("LightGridLLCreation.bsl");
+
+	public:
+		LightGridLLCreationMat();
+
+		/** Binds parameter buffers and prepares any internal buffers. Must be called before execute(). */
+		void setParams(const Vector3I& gridSize, const SPtr<GpuParamBlockBuffer>& gridParams, 
+					   const SPtr<GpuBuffer>& lightsBuffer);
+
+		/** Binds the material for rendering, sets up per-camera parameters and executes it. */
+		void execute(const RendererCamera& view);
+
+		/** Returns the buffers generated by execute(). */
+		void getOutputs(SPtr<GpuBuffer>& linkedListHeads, SPtr<GpuBuffer>& linkedList) const;
+	private:
+		GpuParamBuffer mLightBufferParam;
+		GpuParamBuffer mLinkedListCounterParam;
+		GpuParamBuffer mLinkedListHeadsParam;
+		GpuParamBuffer mLinkedListParam;
+
+		SPtr<GpuBuffer> mLinkedListCounter;
+		SPtr<GpuBuffer> mLinkedListHeads;
+		SPtr<GpuBuffer> mLinkedList;
+
+		UINT32 mBufferNumCells;
+		Vector3I mGridSize;
+	};
+
+	/** Shader that reduces the linked list created by LightGridLLCreationMat into a sequential array. */
+	class LightGridLLReductionMat : public RendererMaterial<LightGridLLReductionMat>
+	{
+		RMAT_DEF("LightGridLLReduction.bsl");
+
+	public:
+		LightGridLLReductionMat();
+
+		/** Binds parameter buffers and prepares any internal buffers. Must be called before execute(). */
+		void setParams(const Vector3I& gridSize, const SPtr<GpuParamBlockBuffer>& gridParams, 
+					   SPtr<GpuBuffer>& linkedListHeads, SPtr<GpuBuffer>& linkedList);
+
+		/** Binds the material for renderingand executes it. */
+		void execute(const RendererCamera& view);
+
+		/** Returns the buffers generated by execute(). */
+		void getOutputs(SPtr<GpuBuffer>& gridOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices) const;
+	private:
+		GpuParamBuffer mLinkedListHeadsParam;
+		GpuParamBuffer mLinkedListParam;
+
+		GpuParamBuffer mGridDataCounterParam;
+		GpuParamBuffer mGridLightOffsetAndSizeParam;
+		GpuParamBuffer mGridLightIndicesParam;
+
+		SPtr<GpuBuffer> mGridDataCounter;
+		SPtr<GpuBuffer> mGridLightOffsetAndSize;
+		SPtr<GpuBuffer> mGridLightIndices;
+
+		UINT32 mBufferNumCells;
+		Vector3I mGridSize;
+	};
+
+	/**	
+	 * Helper class that is used for generating a grid in view space, whose cells contain information about lights 
+	 * affecting them. Used for forward rendering. 
+	 */
+	class LightGrid
+	{
+	public:
+		LightGrid();
+
+		/** Updates the light grid from the provided view. */
+		void updateGrid(const RendererCamera& view, const SPtr<GpuBuffer>& lightsBuffer, UINT32 numDirLights, 
+						UINT32 numRadialLights, UINT32 numSpotLights);
+
+		/** 
+		 * Returns the buffers containing light indices per grid cell. 
+		 *
+		 * @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. 
+		 */
+		void getOutputs(SPtr<GpuBuffer>& gridOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices) const;
+
+	private:
+		LightGridLLCreationMat mLLCreationMat;
+		LightGridLLReductionMat mLLReductionMat;
+
+		SPtr<GpuParamBlockBuffer> mGridParamBuffer;
+	};
+
+	/** @} */
+}}

+ 1 - 0
Source/RenderBeast/Include/BsRenderBeastPrerequisites.h

@@ -37,4 +37,5 @@ namespace bs { namespace ct
 	struct PooledRenderTexture;
 	struct PooledRenderTexture;
 	class RenderTargets;
 	class RenderTargets;
 	class RendererCamera;
 	class RendererCamera;
+	struct LightData;
 }}
 }}

+ 9 - 0
Source/RenderBeast/Include/BsRenderTargets.h

@@ -70,6 +70,12 @@ namespace bs { namespace ct
 		/**	Returns the number of samples per pixel supported by the targets. */
 		/**	Returns the number of samples per pixel supported by the targets. */
 		UINT32 getNumSamples() const { return mViewTarget.numSamples; }
 		UINT32 getNumSamples() const { return mViewTarget.numSamples; }
 
 
+		/** Gets the width of the targets, in pixels. */
+		UINT32 getWidth() const { return mWidth; }
+
+		/** Gets the height of the targets, in pixels. */
+		UINT32 getHeight() const { return mHeight; }
+
 	private:
 	private:
 		RenderTargets(const RENDERER_VIEW_TARGET_DESC& view, bool hdr);
 		RenderTargets(const RENDERER_VIEW_TARGET_DESC& view, bool hdr);
 
 
@@ -87,6 +93,9 @@ namespace bs { namespace ct
 		PixelFormat mAlbedoFormat;
 		PixelFormat mAlbedoFormat;
 		PixelFormat mNormalFormat;
 		PixelFormat mNormalFormat;
 		bool mHDR;
 		bool mHDR;
+
+		UINT32 mWidth;
+		UINT32 mHeight;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 5 - 0
Source/RenderBeast/Include/BsRendererCamera.h

@@ -26,6 +26,7 @@ namespace bs { namespace ct
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatInvViewProj)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatInvViewProj)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatScreenToWorld)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatScreenToWorld)
 		BS_PARAM_BLOCK_ENTRY(Vector2, gDeviceZToWorldZ)
 		BS_PARAM_BLOCK_ENTRY(Vector2, gDeviceZToWorldZ)
+		BS_PARAM_BLOCK_ENTRY(Vector2, gNearFar)
 		BS_PARAM_BLOCK_ENTRY(Vector4I, gViewportRectangle)
 		BS_PARAM_BLOCK_ENTRY(Vector4I, gViewportRectangle)
 		BS_PARAM_BLOCK_ENTRY(Vector4, gClipToUVScaleOffset)
 		BS_PARAM_BLOCK_ENTRY(Vector4, gClipToUVScaleOffset)
 	BS_PARAM_BLOCK_END
 	BS_PARAM_BLOCK_END
@@ -77,6 +78,7 @@ namespace bs { namespace ct
 		Vector3 viewOrigin;
 		Vector3 viewOrigin;
 		bool flipView;
 		bool flipView;
 		float nearPlane;
 		float nearPlane;
+		float farPlane;
 
 
 		bool isOverlay : 1;
 		bool isOverlay : 1;
 		bool isHDR : 1;
 		bool isHDR : 1;
@@ -138,6 +140,9 @@ namespace bs { namespace ct
 		/** Returns the distance to the near clipping plane. */
 		/** Returns the distance to the near clipping plane. */
 		float getNearPlane() const { return mViewDesc.nearPlane; }
 		float getNearPlane() const { return mViewDesc.nearPlane; }
 
 
+		/** Returns the distance to the far clipping plane. */
+		float getFarPlane() const { return mViewDesc.farPlane; }
+
 		/** Returns true if the view requires high dynamic range rendering. */
 		/** Returns true if the view requires high dynamic range rendering. */
 		bool isHDR() const { return mViewDesc.isHDR; }
 		bool isHDR() const { return mViewDesc.isHDR; }
 
 

+ 232 - 0
Source/RenderBeast/Source/BsLightGrid.cpp

@@ -0,0 +1,232 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsLightGrid.h"
+#include "BsGpuBuffer.h"
+#include "BsGpuParamsSet.h"
+#include "BsRendererUtility.h"
+#include "BsRendererCamera.h"
+#include "BsRenderTargets.h"
+
+namespace bs { namespace ct
+{
+	static const UINT32 CELL_XY_SIZE = 64;
+	static const UINT32 NUM_Z_SUBDIVIDES = 32;
+	static const UINT32 MAX_LIGHTS_PER_CELL = 32;
+	static const UINT32 THREADGROUP_SIZE = 4;
+
+	LightGridParamDef gLightGridParamDefDef;
+
+	LightGridLLCreationMat::LightGridLLCreationMat()
+		:mBufferNumCells(0)
+	{
+		SPtr<GpuParams> params = mParamsSet->getGpuParams();
+
+		params->getBufferParam(GPT_COMPUTE_PROGRAM, "gLights", mLightBufferParam);
+		params->getBufferParam(GPT_COMPUTE_PROGRAM, "gLinkedListCounter", mLinkedListCounterParam);
+		params->getBufferParam(GPT_COMPUTE_PROGRAM, "gLinkedListHeads", mLinkedListHeadsParam);
+		params->getBufferParam(GPT_COMPUTE_PROGRAM, "gLinkedList", mLinkedListParam);
+
+		GPU_BUFFER_DESC desc;
+		desc.elementCount = 1;
+		desc.format = BF_32X1U;
+		desc.randomGpuWrite = true;
+		desc.type = GBT_STANDARD;
+
+		mLinkedListCounter = GpuBuffer::create(desc);
+		mLinkedListCounterParam.set(mLinkedListCounter);
+	}
+
+	void LightGridLLCreationMat::_initDefines(ShaderDefines& defines)
+	{
+		defines.set("THREADGROUP_SIZE", THREADGROUP_SIZE);
+	}
+
+	void LightGridLLCreationMat::setParams(const Vector3I& gridSize, const SPtr<GpuParamBlockBuffer>& gridParams,
+											   const SPtr<GpuBuffer>& lightsBuffer)
+	{
+		mGridSize = gridSize;
+		UINT32 numCells = gridSize[0] * gridSize[1] * gridSize[2];
+
+		if(numCells > mBufferNumCells || mBufferNumCells == 0)
+		{
+			GPU_BUFFER_DESC desc;
+			desc.elementCount = numCells;
+			desc.format = BF_32X1U;
+			desc.randomGpuWrite = true;
+			desc.type = GBT_STANDARD;
+
+			mLinkedListHeads = GpuBuffer::create(desc);
+			mLinkedListHeadsParam.set(mLinkedListHeads);
+
+			desc.format = BF_32X2U;
+			desc.elementCount = numCells * MAX_LIGHTS_PER_CELL;
+
+			mLinkedList = GpuBuffer::create(desc);
+			mLinkedListParam.set(mLinkedList);
+
+			mBufferNumCells = numCells;
+		}
+
+		UINT32 zero = 0;
+		mLinkedListCounter->writeData(0, sizeof(UINT32), &zero, BWT_DISCARD);
+
+		// Note: Add a method to clear buffer data directly? e.g. GpuBuffer->clear(value);
+		UINT32* headsClearData = (UINT32*)bs_stack_alloc(mLinkedListHeads->getSize());
+		memset(headsClearData, 0xFFFFFFFF, mLinkedListHeads->getSize());
+
+		mLinkedListHeads->writeData(0, mLinkedListHeads->getSize(), headsClearData, BWT_DISCARD);
+
+		bs_stack_free(headsClearData);
+
+		mParamsSet->setParamBlockBuffer("Params", gridParams, true);
+		mLightBufferParam.set(lightsBuffer);
+	}
+
+	void LightGridLLCreationMat::execute(const RendererCamera& view)
+	{
+		mParamsSet->setParamBlockBuffer("PerCamera", view.getPerViewBuffer(), true);
+
+		UINT32 width = view.getRenderTargets()->getWidth();
+		UINT32 height = view.getRenderTargets()->getHeight();
+
+		UINT32 numGroupsX = (mGridSize[0] + THREADGROUP_SIZE - 1) / THREADGROUP_SIZE;
+		UINT32 numGroupsY = (mGridSize[1] + THREADGROUP_SIZE - 1) / THREADGROUP_SIZE;
+		UINT32 numGroupsZ = (mGridSize[2] + THREADGROUP_SIZE - 1) / THREADGROUP_SIZE;
+
+		gRendererUtility().setComputePass(mMaterial, 0);
+		gRendererUtility().setPassParams(mParamsSet);
+
+		RenderAPI::instance().dispatchCompute(numGroupsX, numGroupsY, numGroupsZ);
+	}
+
+	void LightGridLLCreationMat::getOutputs(SPtr<GpuBuffer>& linkedListHeads, SPtr<GpuBuffer>& linkedList) const
+	{
+		linkedListHeads = mLinkedListHeads;
+		linkedList = mLinkedList;
+	}
+
+	LightGridLLReductionMat::LightGridLLReductionMat()
+		:mBufferNumCells(0)
+	{
+		SPtr<GpuParams> params = mParamsSet->getGpuParams();
+
+		params->getBufferParam(GPT_COMPUTE_PROGRAM, "gLinkedListHeads", mLinkedListHeadsParam);
+		params->getBufferParam(GPT_COMPUTE_PROGRAM, "gLinkedList", mLinkedListParam);
+
+		params->getBufferParam(GPT_COMPUTE_PROGRAM, "gGridDataCounter", mGridDataCounterParam);
+		params->getBufferParam(GPT_COMPUTE_PROGRAM, "gGridLightOffsetAndSize", mGridLightOffsetAndSizeParam);
+		params->getBufferParam(GPT_COMPUTE_PROGRAM, "gGridLightIndices", mGridLightIndicesParam);
+
+		GPU_BUFFER_DESC desc;
+		desc.elementCount = 1;
+		desc.format = BF_32X1U;
+		desc.randomGpuWrite = true;
+		desc.type = GBT_STANDARD;
+
+		mGridDataCounter = GpuBuffer::create(desc);
+		mGridDataCounterParam.set(mGridDataCounter);
+	}
+
+	void LightGridLLReductionMat::_initDefines(ShaderDefines& defines)
+	{
+		defines.set("THREADGROUP_SIZE", THREADGROUP_SIZE);
+	}
+
+	void LightGridLLReductionMat::setParams(const Vector3I& gridSize, const SPtr<GpuParamBlockBuffer>& gridParams,
+											SPtr<GpuBuffer>& linkedListHeads, SPtr<GpuBuffer>& linkedList)
+	{
+		mGridSize = gridSize;
+		UINT32 numCells = gridSize[0] * gridSize[1] * gridSize[2];
+
+		if (numCells > mBufferNumCells || mBufferNumCells == 0)
+		{
+			GPU_BUFFER_DESC desc;
+			desc.elementCount = numCells;
+			desc.format = BF_32X1U;
+			desc.randomGpuWrite = true;
+			desc.type = GBT_STANDARD;
+
+			mGridLightOffsetAndSize = GpuBuffer::create(desc);
+			mGridLightOffsetAndSizeParam.set(mGridLightOffsetAndSize);
+
+			desc.format = BF_32X2U;
+			desc.elementCount = numCells * MAX_LIGHTS_PER_CELL;
+			mGridLightIndices = GpuBuffer::create(desc);
+			mGridLightIndicesParam.set(mGridLightIndices);
+
+			mBufferNumCells = numCells;
+		}
+
+		// Note: Add a method to clear buffer data directly? e.g. GpuBuffer->clear(value);
+		UINT32 zero = 0;
+		mGridDataCounter->writeData(0, sizeof(UINT32), &zero, BWT_DISCARD);
+
+		mParamsSet->setParamBlockBuffer("Params", gridParams, true);
+		mLinkedListHeadsParam.set(linkedListHeads);
+		mLinkedListParam.set(linkedList);
+	}
+
+	void LightGridLLReductionMat::execute(const RendererCamera& view)
+	{
+		mParamsSet->setParamBlockBuffer("PerCamera", view.getPerViewBuffer(), true);
+
+		UINT32 numGroupsX = (mGridSize[0] + THREADGROUP_SIZE - 1) / THREADGROUP_SIZE;
+		UINT32 numGroupsY = (mGridSize[1] + THREADGROUP_SIZE - 1) / THREADGROUP_SIZE;
+		UINT32 numGroupsZ = (mGridSize[2] + THREADGROUP_SIZE - 1) / THREADGROUP_SIZE;
+
+		gRendererUtility().setComputePass(mMaterial, 0);
+		gRendererUtility().setPassParams(mParamsSet);
+
+		RenderAPI::instance().dispatchCompute(numGroupsX, numGroupsY, numGroupsZ);
+	}
+
+	void LightGridLLReductionMat::getOutputs(SPtr<GpuBuffer>& gridOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices) const
+	{
+		gridOffsetsAndSize = mGridLightOffsetAndSize;
+		gridLightIndices = mGridLightIndices;
+	}
+
+	LightGrid::LightGrid()
+	{
+		mGridParamBuffer = gLightGridParamDefDef.createBuffer();
+	}
+
+	void LightGrid::updateGrid(const RendererCamera& view, const SPtr<GpuBuffer>& lightsBuffer, UINT32 numDirLights,
+					UINT32 numRadialLights, UINT32 numSpotLights)
+	{
+		UINT32 width = view.getRenderTargets()->getWidth();
+		UINT32 height = view.getRenderTargets()->getHeight();
+
+		Vector3I gridSize;
+		gridSize[0] = (width + CELL_XY_SIZE - 1) / CELL_XY_SIZE;
+		gridSize[1] = (height + CELL_XY_SIZE - 1) / CELL_XY_SIZE;
+		gridSize[2] = NUM_Z_SUBDIVIDES;
+
+		Vector3I lightOffsets;
+		lightOffsets[0] = numDirLights;
+		lightOffsets[1] = lightOffsets[0] + numRadialLights;
+		lightOffsets[2] = lightOffsets[1] + numSpotLights;
+
+		UINT32 numCells = gridSize[0] * gridSize[1] * gridSize[2];
+
+		gLightGridParamDefDef.gLightOffsets.set(mGridParamBuffer, lightOffsets);
+		gLightGridParamDefDef.gNumCells.set(mGridParamBuffer, numCells);
+		gLightGridParamDefDef.gGridSize.set(mGridParamBuffer, gridSize);
+		gLightGridParamDefDef.gMaxNumLightsPerCell.set(mGridParamBuffer, MAX_LIGHTS_PER_CELL);
+
+		mLLCreationMat.setParams(gridSize, mGridParamBuffer, lightsBuffer);
+		mLLCreationMat.execute(view);
+
+		SPtr<GpuBuffer> linkedListHeads;
+		SPtr<GpuBuffer> linkedList;
+		mLLCreationMat.getOutputs(linkedListHeads, linkedList);
+
+		mLLReductionMat.setParams(gridSize, mGridParamBuffer, linkedListHeads, linkedList);
+		mLLReductionMat.execute(view);
+	}
+
+	void LightGrid::getOutputs(SPtr<GpuBuffer>& gridOffsetsAndSize, SPtr<GpuBuffer>& gridLightIndices) const
+	{
+		mLLReductionMat.getOutputs(gridOffsetsAndSize, gridLightIndices);
+	}
+}}

+ 2 - 0
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -500,6 +500,7 @@ namespace bs { namespace ct
 			viewDesc.cullFrustum = camera->getWorldFrustum();
 			viewDesc.cullFrustum = camera->getWorldFrustum();
 			viewDesc.visibleLayers = camera->getLayers();
 			viewDesc.visibleLayers = camera->getLayers();
 			viewDesc.nearPlane = camera->getNearClipDistance();
 			viewDesc.nearPlane = camera->getNearClipDistance();
+			viewDesc.farPlane = camera->getFarClipDistance();
 			viewDesc.flipView = false;
 			viewDesc.flipView = false;
 
 
 			viewDesc.viewOrigin = camera->getPosition();
 			viewDesc.viewOrigin = camera->getPosition();
@@ -1032,6 +1033,7 @@ namespace bs { namespace ct
 
 
 		viewDesc.visibleLayers = 0xFFFFFFFFFFFFFFFF;
 		viewDesc.visibleLayers = 0xFFFFFFFFFFFFFFFF;
 		viewDesc.nearPlane = 0.5f;
 		viewDesc.nearPlane = 0.5f;
+		viewDesc.farPlane = 1000.0f;
 		viewDesc.flipView = RenderAPI::instance().getAPIInfo().getUVYAxisUp();
 		viewDesc.flipView = RenderAPI::instance().getAPIInfo().getUVYAxisUp();
 
 
 		viewDesc.viewOrigin = position;
 		viewDesc.viewOrigin = position;

+ 1 - 1
Source/RenderBeast/Source/BsRenderTargets.cpp

@@ -10,7 +10,7 @@
 namespace bs { namespace ct
 namespace bs { namespace ct
 {
 {
 	RenderTargets::RenderTargets(const RENDERER_VIEW_TARGET_DESC& view, bool hdr)
 	RenderTargets::RenderTargets(const RENDERER_VIEW_TARGET_DESC& view, bool hdr)
-		:mViewTarget(view), mHDR(hdr)
+		:mViewTarget(view), mHDR(hdr), mWidth(view.targetWidth), mHeight(view.targetHeight)
 	{
 	{
 		// Note: Consider customizable HDR format via options? e.g. smaller PF_FLOAT_R11G11B10 or larger 32-bit format
 		// Note: Consider customizable HDR format via options? e.g. smaller PF_FLOAT_R11G11B10 or larger 32-bit format
 		mSceneColorFormat = hdr ? PF_FLOAT16_RGBA : PF_R8G8B8A8;
 		mSceneColorFormat = hdr ? PF_FLOAT16_RGBA : PF_R8G8B8A8;

+ 3 - 0
Source/RenderBeast/Source/BsRendererCamera.cpp

@@ -271,6 +271,9 @@ namespace bs { namespace ct
 		gPerCameraParamDef.gViewOrigin.set(mParamBuffer, mViewDesc.viewOrigin);
 		gPerCameraParamDef.gViewOrigin.set(mParamBuffer, mViewDesc.viewOrigin);
 		gPerCameraParamDef.gDeviceZToWorldZ.set(mParamBuffer, getDeviceZTransform(mViewDesc.projTransform));
 		gPerCameraParamDef.gDeviceZToWorldZ.set(mParamBuffer, getDeviceZTransform(mViewDesc.projTransform));
 
 
+		Vector2 nearFar(mViewDesc.nearPlane, mViewDesc.farPlane);
+		gPerCameraParamDef.gNearFar.set(mParamBuffer, nearFar);
+
 		const Rect2I& viewRect = mViewDesc.target.viewRect;
 		const Rect2I& viewRect = mViewDesc.target.viewRect;
 
 
 		Vector4I viewportRect;
 		Vector4I viewportRect;