Browse Source

Bulk of code for shadow depth rendering & shadow projection done

BearishSun 8 years ago
parent
commit
2f3745e7be

+ 27 - 4
Data/Raw/Engine/Includes/ShadowDepthBase.bslinc

@@ -17,22 +17,41 @@ mixin ShadowDepthBase
 			
 			
 			#ifdef USES_GS
 			#ifdef USES_GS
 			float4 worldPos : TEXCOORD0;
 			float4 worldPos : TEXCOORD0;
+			#else
+			#ifndef LINEAR_DEPTH_RANGE
+				float shadowPos : TEXCOORD0;
+			#endif
 			#endif
 			#endif
 		};
 		};
 		
 		
+		[internal]
 		cbuffer ShadowParams
 		cbuffer ShadowParams
 		{
 		{
 			float4x4 gMatViewProj;
 			float4x4 gMatViewProj;
+			float2 gNDCZToViewZ;
+			float2 gNDCZToDeviceZ;
 			float gDepthBias;
 			float gDepthBias;
 			float gDepthRange;
 			float gDepthRange;
 		};
 		};
+		
+		/** Converts Z value from view space to NDC space. */
+		float convertToNDCZ(float viewZ)
+		{
+			return -gNDCZToViewZ.y + (gNDCZToViewZ.x / viewZ);
+		}
+		
+		/** Converts Z value from NDC space to device Z value in range [0, 1]. */
+		float NDCZToDeviceZ(float ndcZ)
+		{
+			return (ndcZ + gNDCZToDeviceZ.y) * gNDCZToDeviceZ.x;
+		}		
 
 
 		void linearizeDepth(inout float4 clipPos)
 		void linearizeDepth(inout float4 clipPos)
-		{
+		{	
+			#ifdef CLAMP_TO_NEAR_PLANE
 			float ndcZ = clipPos.z / clipPos.w;
 			float ndcZ = clipPos.z / clipPos.w;
 			float deviceZ = NDCZToDeviceZ(ndcZ);
 			float deviceZ = NDCZToDeviceZ(ndcZ);
 			
 			
-			#ifdef CLAMP_TO_NEAR_PLANE
 			// Clamp to near plane if behind it
 			// Clamp to near plane if behind it
 			if (deviceZ < 0)
 			if (deviceZ < 0)
 			{
 			{
@@ -43,8 +62,8 @@ mixin ShadowDepthBase
 
 
 			// Output linear depth
 			// Output linear depth
 			#ifdef LINEAR_DEPTH_RANGE
 			#ifdef LINEAR_DEPTH_RANGE
-				float linearDepth = deviceZ * gDepthRange + gDepthBias;
-				clipPos.z = DeviceZToNDCZ(linearDepth) * clipPos.w;
+				float linearDepth = -clipPos.z * gInvDepthRange + gDepthBias;
+				clipPos.z = linearDepth * clipPos.w;
 			#endif
 			#endif
 		}		
 		}		
 	
 	
@@ -61,6 +80,10 @@ mixin ShadowDepthBase
 			float4 clipPos = mul(gMatViewProj, worldPosition);
 			float4 clipPos = mul(gMatViewProj, worldPosition);
 			linearizeDepth(clipPos);
 			linearizeDepth(clipPos);
 			
 			
+			#ifndef LINEAR_DEPTH_RANGE
+			output.shadowPos = clipPos.z;
+			#endif
+			
 			output.position = clipPos;
 			output.position = clipPos;
 			#endif
 			#endif
 			
 			

+ 1 - 0
Data/Raw/Engine/Includes/ShadowProjectionCommon.bslinc

@@ -17,6 +17,7 @@ mixin ShadowProjectionCommon
 		};
 		};
 		
 		
 		#if NEEDS_TRANSFORM
 		#if NEEDS_TRANSFORM
+		[internal]
 		cbuffer VertParams
 		cbuffer VertParams
 		{
 		{
 			float4 gPositionAndScale;
 			float4 gPositionAndScale;

+ 2 - 0
Data/Raw/Engine/Shaders/ShadowDepthCube.bsl

@@ -11,11 +11,13 @@ mixin ShadowDepth
 			uint targetIdx : SV_RenderTargetArrayIndex;
 			uint targetIdx : SV_RenderTargetArrayIndex;
 		};
 		};
 
 
+		[internal]
 		cbuffer ShadowCubeMatrices
 		cbuffer ShadowCubeMatrices
 		{
 		{
 			float4x4 gFaceVPMatrices[6];
 			float4x4 gFaceVPMatrices[6];
 		};
 		};
 		
 		
+		[internal]
 		cbuffer ShadowCubeMasks
 		cbuffer ShadowCubeMasks
 		{
 		{
 			uint gFaceMasks[6];
 			uint gFaceMasks[6];

+ 10 - 1
Data/Raw/Engine/Shaders/ShadowDepthNormal.bsl

@@ -1,4 +1,13 @@
 #include "$ENGINE$\ShadowDepthBase.bslinc"
 #include "$ENGINE$\ShadowDepthBase.bslinc"
 
 
 mixin ShadowDepth
 mixin ShadowDepth
-{ };
+{ 
+	code
+	{
+		float4 fsmain(ShadowVStoFS input, float outDepth : SV_Depth)
+		{
+			outDepth = saturate(-input.shadowPos * gInvDepthRange + gDepthBias);
+			return 0;
+		}
+	};
+};

+ 40 - 1
Data/Raw/Engine/Shaders/ShadowProject.bsl

@@ -6,11 +6,50 @@ technique ShadowProject
 	mixin GBufferInput;
 	mixin GBufferInput;
 	mixin ShadowProjectionCommon;
 	mixin ShadowProjectionCommon;
 
 
+	depth
+	{
+		read = false;
+		write = false;
+	};
+	
+	stencil
+	{
+		enabled = true;
+		
+		// This clears the stencil at the same time as performing the test
+		// Note: Need to test performance clearing the stencil this way vs. clearing it separately,
+		//   as this disables HiStencil optimization.
+		front = { zero, zero, zero, neq };
+	};
+	
+	#ifdef FADE_PLANE
+	blend
+	{
+		target
+		{
+			enabled = true;
+			writemask = R;
+			color = { srcA, srcIA, add };
+		};
+	};
+	#else
+	blend
+	{
+		target
+		{
+			enabled = true;
+			writemask = R;
+			color = { one, one, min };
+		};
+	};	
+	#endif
+	
 	code
 	code
 	{
 	{
 		Texture2D gShadowTex;
 		Texture2D gShadowTex;
 		SamplerState gShadowSampler;
 		SamplerState gShadowSampler;
 	
 	
+		[internal]
 		cbuffer Params
 		cbuffer Params
 		{
 		{
 			// Transform a point in mixed space (xy - clip space, z - view space) to a point
 			// Transform a point in mixed space (xy - clip space, z - view space) to a point
@@ -207,7 +246,7 @@ technique ShadowProject
 			#endif
 			#endif
 			
 			
 			float alpha = 1.0f;
 			float alpha = 1.0f;
-			#if FADE_PLANE
+			#ifdef FADE_PLANE
 				alpha = 1.0f - saturate((depth - gFadePlaneDepth) * gInvFadePlaneRange);
 				alpha = 1.0f - saturate((depth - gFadePlaneDepth) * gInvFadePlaneRange);
 			#endif
 			#endif
 
 

+ 34 - 0
Data/Raw/Engine/Shaders/ShadowProjectOmni.bsl

@@ -6,6 +6,39 @@ technique ShadowProjectOmni
 	mixin GBufferInput;
 	mixin GBufferInput;
 	mixin ShadowProjectionCommon;
 	mixin ShadowProjectionCommon;
 
 
+	blend
+	{
+		target
+		{
+			enabled = true;
+			writemask = R;
+			color = { one, one, min };
+		};
+	};
+	
+	#ifdef VIEWER_INSIDE_VOLUME
+	depth
+	{
+		read = false;
+		write = false;
+	};
+	
+	raster
+	{
+		cull = cw;
+	};
+	#else
+	depth
+	{
+		write = false;
+	};
+	
+	raster
+	{
+		cull = ccw;
+	};
+	#endif
+	
 	code
 	code
 	{
 	{
 		// Random samples on a disc of radius 2.5. Random values generated using low discrepancy
 		// Random samples on a disc of radius 2.5. Random values generated using low discrepancy
@@ -73,6 +106,7 @@ technique ShadowProjectOmni
 		TextureCube gShadowCubeTex;
 		TextureCube gShadowCubeTex;
 		SamplerComparisonState gShadowCubeSampler;
 		SamplerComparisonState gShadowCubeSampler;
 		
 		
+		[internal]
 		cbuffer Params
 		cbuffer Params
 		{
 		{
 			float4x4 gFaceVPMatrices[6];
 			float4x4 gFaceVPMatrices[6];

+ 2 - 2
Source/BansheeSL/BsLexerFX.l

@@ -132,8 +132,8 @@ inverse			{ yylval->intValue = OV_Invert; return TOKEN_OPVALUE; }
 one				{ yylval->intValue = OV_One; return TOKEN_OPVALUE; }
 one				{ yylval->intValue = OV_One; return TOKEN_OPVALUE; }
 dstRGB			{ yylval->intValue = OV_DestColor; return TOKEN_OPVALUE; }
 dstRGB			{ yylval->intValue = OV_DestColor; return TOKEN_OPVALUE; }
 srcRGB			{ yylval->intValue = OV_SrcColor; return TOKEN_OPVALUE; }
 srcRGB			{ yylval->intValue = OV_SrcColor; return TOKEN_OPVALUE; }
-dstiRGB			{ yylval->intValue = OV_InvDestColor; return TOKEN_OPVALUE; }
-srciRGB			{ yylval->intValue = OV_InvSrcColor; return TOKEN_OPVALUE; }
+dstIRGB			{ yylval->intValue = OV_InvDestColor; return TOKEN_OPVALUE; }
+srcIRGB			{ yylval->intValue = OV_InvSrcColor; return TOKEN_OPVALUE; }
 dstA			{ yylval->intValue = OV_DestAlpha; return TOKEN_OPVALUE; }
 dstA			{ yylval->intValue = OV_DestAlpha; return TOKEN_OPVALUE; }
 srcA			{ yylval->intValue = OV_SrcAlpha; return TOKEN_OPVALUE; }
 srcA			{ yylval->intValue = OV_SrcAlpha; return TOKEN_OPVALUE; }
 dstIA			{ yylval->intValue = OV_InvDestAlpha; return TOKEN_OPVALUE; }
 dstIA			{ yylval->intValue = OV_InvDestAlpha; return TOKEN_OPVALUE; }

+ 4 - 4
Source/RenderBeast/Include/BsLightRendering.h

@@ -67,10 +67,10 @@ namespace bs { namespace ct
 		void bind(const SPtr<RenderTargets>& renderTargets);
 		void bind(const SPtr<RenderTargets>& renderTargets);
 
 
 	private:
 	private:
-		GpuParamTexture mGBufferA;
-		GpuParamTexture mGBufferB;
-		GpuParamTexture mGBufferC;
-		GpuParamTexture mGBufferDepth;
+		MaterialParamTexture mGBufferA;
+		MaterialParamTexture mGBufferB;
+		MaterialParamTexture mGBufferC;
+		MaterialParamTexture mGBufferDepth;
 	};
 	};
 
 
 	/** Shader that renders directional light sources during deferred rendering light pass. */
 	/** Shader that renders directional light sources during deferred rendering light pass. */

+ 6 - 0
Source/RenderBeast/Include/BsRenderBeastOptions.h

@@ -45,6 +45,12 @@ namespace bs { namespace ct
 		 * shadows far away, but will never increase the resolution past the provided value.
 		 * shadows far away, but will never increase the resolution past the provided value.
 		 */
 		 */
 		UINT32 shadowMapSize = 2048;
 		UINT32 shadowMapSize = 2048;
+
+		/**
+		 * Determines the number of samples used for percentage closer shadow map filtering. Higher values yield higher
+		 * quality shadows. Valid range is [1, 4].
+		 */
+		UINT32 shadowFilteringQuality = 4;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 8 - 3
Source/RenderBeast/Include/BsRendererView.h

@@ -253,7 +253,6 @@ namespace bs { namespace ct
 		/** Returns a buffer that stores per-view parameters. */
 		/** Returns a buffer that stores per-view parameters. */
 		SPtr<GpuParamBlockBuffer> getPerViewBuffer() const { return mParamBuffer; }
 		SPtr<GpuParamBlockBuffer> getPerViewBuffer() const { return mParamBuffer; }
 
 
-	private:
 		/**
 		/**
 		 * Extracts the necessary values from the projection matrix that allow you to transform device Z value (range [0, 1]
 		 * Extracts the necessary values from the projection matrix that allow you to transform device Z value (range [0, 1]
 		 * into view Z value.
 		 * into view Z value.
@@ -262,7 +261,7 @@ namespace bs { namespace ct
 		 * @return					Returns two values that can be used to transform device z to view z using this formula:
 		 * @return					Returns two values that can be used to transform device z to view z using this formula:
 		 * 							z = (deviceZ + y) * x.
 		 * 							z = (deviceZ + y) * x.
 		 */
 		 */
-		Vector2 getDeviceZTransform(const Matrix4& projMatrix) const;
+		static Vector2 getDeviceZToViewZ(const Matrix4& projMatrix);
 
 
 		/**
 		/**
 		 * Extracts the necessary values from the projection matrix that allow you to transform NDC Z value (range depending
 		 * Extracts the necessary values from the projection matrix that allow you to transform NDC Z value (range depending
@@ -272,8 +271,14 @@ namespace bs { namespace ct
 		 * @return					Returns two values that can be used to transform NDC z to view z using this formula:
 		 * @return					Returns two values that can be used to transform NDC z to view z using this formula:
 		 * 							z = (NDCZ + y) * x.
 		 * 							z = (NDCZ + y) * x.
 		 */
 		 */
-		Vector2 getNDCZTransform(const Matrix4& projMatrix) const;
+		static Vector2 getNDCZToViewZ(const Matrix4& projMatrix);
 
 
+		/** 
+		 * Returns a value that can be used for tranforming a depth value in NDC, to a depth value in device Z ([0, 1] 
+		 * range using this formula: (NDCZ + y) * x. 
+		 */
+		static Vector2 getNDCZToDeviceZ();
+	private:
 		RendererViewProperties mProperties;
 		RendererViewProperties mProperties;
 		RENDERER_VIEW_TARGET_DESC mTargetDesc;
 		RENDERER_VIEW_TARGET_DESC mTargetDesc;
 		Camera* mCamera;
 		Camera* mCamera;

+ 103 - 8
Source/RenderBeast/Include/BsShadowRendering.h

@@ -28,8 +28,10 @@ namespace bs { namespace ct
 
 
 	BS_PARAM_BLOCK_BEGIN(ShadowParamsDef)
 	BS_PARAM_BLOCK_BEGIN(ShadowParamsDef)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatViewProj)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatViewProj)
+		BS_PARAM_BLOCK_ENTRY(Vector2, gNDCZToViewZ)
+		BS_PARAM_BLOCK_ENTRY(Vector2, gNDCZToDeviceZ)
 		BS_PARAM_BLOCK_ENTRY(float, gDepthBias)
 		BS_PARAM_BLOCK_ENTRY(float, gDepthBias)
-		BS_PARAM_BLOCK_ENTRY(float, gDepthRange)
+		BS_PARAM_BLOCK_ENTRY(float, gInvDepthRange)
 	BS_PARAM_BLOCK_END
 	BS_PARAM_BLOCK_END
 
 
 	extern ShadowParamsDef gShadowParamsDef;
 	extern ShadowParamsDef gShadowParamsDef;
@@ -135,6 +137,35 @@ namespace bs { namespace ct
 		ShadowProjectStencilMat<false, false> mFF;
 		ShadowProjectStencilMat<false, false> mFF;
 	};
 	};
 
 
+	/** Common parameters used by the shadow projection materials. */
+	struct ShadowProjectParams
+	{
+		ShadowProjectParams(const Light& light, const SPtr<Texture>& shadowMap, UINT32 shadowMapFace,
+			const SPtr<GpuParamBlockBuffer>& shadowParams, const SPtr<GpuParamBlockBuffer>& perCameraParams,
+			const SPtr<RenderTargets>& renderTargets)
+			: light{light}, shadowMap{shadowMap}, shadowParams{shadowParams}, perCamera{perCameraParams}
+			, renderTargets{renderTargets}
+		{ }
+
+		/** Light which is casting the shadow. */
+		const Light& light;
+
+		/** Texture containing the shadow map. */
+		const SPtr<Texture>& shadowMap;
+
+		/** Face of the shadow map to bind, if it has multiple faces. */
+		UINT32 shadowMapFace;
+
+		/** Parameter block containing parameters specific for shadow projection. */
+		const SPtr<GpuParamBlockBuffer> shadowParams;
+
+		/** Parameter block containing parameters specific to this view. */
+		const SPtr<GpuParamBlockBuffer>& perCamera;
+
+		/** Contains the GBuffer textures. */
+		SPtr<RenderTargets> renderTargets;
+	};
+
 	BS_PARAM_BLOCK_BEGIN(ShadowProjectParamsDef)
 	BS_PARAM_BLOCK_BEGIN(ShadowProjectParamsDef)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMixedToShadowSpace)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMixedToShadowSpace)
 		BS_PARAM_BLOCK_ENTRY(Vector2, gShadowMapSize)
 		BS_PARAM_BLOCK_ENTRY(Vector2, gShadowMapSize)
@@ -157,8 +188,7 @@ namespace bs { namespace ct
 		ShadowProjectMat();
 		ShadowProjectMat();
 
 
 		/** Binds the material and its parameters to the pipeline. */
 		/** Binds the material and its parameters to the pipeline. */
-		void bind(const Light& light, const SPtr<Texture>& shadowMap, const SPtr<GpuParamBlockBuffer> shadowParams, 
-			const SPtr<GpuParamBlockBuffer>& perCamera);
+		void bind(const ShadowProjectParams& params);
 
 
 	private:
 	private:
 		SPtr<SamplerState> mSamplerState;
 		SPtr<SamplerState> mSamplerState;
@@ -170,6 +200,35 @@ namespace bs { namespace ct
 		GpuParamSampState mShadowSamplerParam;
 		GpuParamSampState mShadowSamplerParam;
 	};
 	};
 
 
+	/** Contains all variations of the ShadowProjectMat material. */
+	class ShadowProjectMaterials
+	{
+	public:
+		/** 
+		 * Binds the appropriate variation of the ShadowProjectMat based on the provided parameters. 
+		 * 
+		 * @param[in]	quality			Quality of the shadow filtering to use. In range [1, 4].
+		 * @param[in]	directional		True if rendering a shadow from a directional light.
+		 * @param[in]	MSAA			True if the GBuffer contains per-sample data.
+		 * @param[in]	params			Parameters to be passed along to the material.
+		 */
+		void bind(UINT32 quality, bool directional, bool MSAA, const ShadowProjectParams& params);
+	private:
+#define MAT_MEMBERS(QUALITY)											\
+		ShadowProjectMat<QUALITY, true, true> mMat##QUALITY##TT;		\
+		ShadowProjectMat<QUALITY, true, false> mMat##QUALITY##TF;		\
+		ShadowProjectMat<QUALITY, false, true> mMat##QUALITY##FT;		\
+		ShadowProjectMat<QUALITY, false, false> mMat##QUALITY##FF;
+	
+		MAT_MEMBERS(1)
+		MAT_MEMBERS(2)
+		MAT_MEMBERS(3)
+		MAT_MEMBERS(4)
+
+#undef MAT_MEMBERS
+
+	};
+
 	BS_PARAM_BLOCK_BEGIN(ShadowProjectOmniParamsDef)
 	BS_PARAM_BLOCK_BEGIN(ShadowProjectOmniParamsDef)
 		BS_PARAM_BLOCK_ENTRY_ARRAY(Matrix4, gFaceVPMatrices, 6)
 		BS_PARAM_BLOCK_ENTRY_ARRAY(Matrix4, gFaceVPMatrices, 6)
 		BS_PARAM_BLOCK_ENTRY(Vector4, gLightPosAndRadius)
 		BS_PARAM_BLOCK_ENTRY(Vector4, gLightPosAndRadius)
@@ -181,8 +240,8 @@ namespace bs { namespace ct
 	extern ShadowProjectOmniParamsDef gShadowProjectOmniParamsDef;
 	extern ShadowProjectOmniParamsDef gShadowProjectOmniParamsDef;
 
 
 	/** Material used for projecting depth into a shadow accumulation buffer for omnidirectional shadow maps. */
 	/** Material used for projecting depth into a shadow accumulation buffer for omnidirectional shadow maps. */
-	template<int ShadowQuality, bool MSAA>
-	class ShadowProjectOmniMat : public RendererMaterial<ShadowProjectOmniMat<ShadowQuality, MSAA>>
+	template<int ShadowQuality, bool Inside, bool MSAA>
+	class ShadowProjectOmniMat : public RendererMaterial<ShadowProjectOmniMat<ShadowQuality, Inside, MSAA>>
 	{
 	{
 		RMAT_DEF("ShadowProjectOmni.bsl");
 		RMAT_DEF("ShadowProjectOmni.bsl");
 
 
@@ -190,8 +249,7 @@ namespace bs { namespace ct
 		ShadowProjectOmniMat();
 		ShadowProjectOmniMat();
 
 
 		/** Binds the material and its parameters to the pipeline. */
 		/** Binds the material and its parameters to the pipeline. */
-		void bind(const Light& light, const SPtr<Texture>& shadowMap, const SPtr<GpuParamBlockBuffer> shadowParams, 
-			const SPtr<GpuParamBlockBuffer>& perCamera);
+		void bind(const ShadowProjectParams& params);
 
 
 	private:
 	private:
 		SPtr<SamplerState> mSamplerState;
 		SPtr<SamplerState> mSamplerState;
@@ -203,6 +261,35 @@ namespace bs { namespace ct
 		GpuParamSampState mShadowSamplerParam;
 		GpuParamSampState mShadowSamplerParam;
 	};
 	};
 
 
+	/** Contains all variations of the ShadowProjectOmniMat material. */
+	class ShadowProjectOmniMaterials
+	{
+	public:
+		/** 
+		 * Binds the appropriate variation of the ShadowProjectOmniMat based on the provided parameters. 
+		 * 
+		 * @param[in]	quality			Quality of the shadow filtering to use. In range [1, 4].
+		 * @param[in]	inside			True if the viewer is inside the light volume.
+		 * @param[in]	MSAA			True if the GBuffer contains per-sample data.
+		 * @param[in]	params			Parameters to be passed along to the material.
+		 */
+		void bind(UINT32 quality, bool inside, bool MSAA, const ShadowProjectParams& params);
+	private:
+#define MAT_MEMBERS(QUALITY)												\
+		ShadowProjectOmniMat<QUALITY, true, true> mMat##QUALITY##TT;		\
+		ShadowProjectOmniMat<QUALITY, true, false> mMat##QUALITY##TF;		\
+		ShadowProjectOmniMat<QUALITY, false, true> mMat##QUALITY##FT;		\
+		ShadowProjectOmniMat<QUALITY, false, false> mMat##QUALITY##FF;
+	
+		MAT_MEMBERS(1)
+		MAT_MEMBERS(2)
+		MAT_MEMBERS(3)
+		MAT_MEMBERS(4)
+
+#undef MAT_MEMBERS
+
+	};
+
 	/** Information about a shadow cast from a single light. */
 	/** Information about a shadow cast from a single light. */
 	struct ShadowInfo
 	struct ShadowInfo
 	{
 	{
@@ -369,7 +456,8 @@ namespace bs { namespace ct
 		 * Renders shadow occlusion values for the specified light, into the currently bound render target. 
 		 * Renders shadow occlusion values for the specified light, into the currently bound render target. 
 		 * The system uses shadow maps rendered by renderShadowMaps().
 		 * The system uses shadow maps rendered by renderShadowMaps().
 		 */
 		 */
-		void renderShadowOcclusion(const RendererScene& scene, const RendererLight& light, UINT32 viewIdx);
+		void renderShadowOcclusion(const RendererScene& scene, UINT32 shadowQuality, const RendererLight& light,
+			UINT32 viewIdx);
 
 
 		/** Changes the default shadow map size. Will cause all shadow maps to be rebuilt. */
 		/** Changes the default shadow map size. Will cause all shadow maps to be rebuilt. */
 		void setShadowMapSize(UINT32 size);
 		void setShadowMapSize(UINT32 size);
@@ -416,6 +504,11 @@ namespace bs { namespace ct
 		 */
 		 */
 		void drawFrustum(const std::array<Vector3, 8>& corners);
 		void drawFrustum(const std::array<Vector3, 8>& corners);
 
 
+		/**
+		 * Calculates optimal shadow quality based on the quality set in the options and the actual shadow map resolution.
+		 */
+		static UINT32 getShadowQuality(UINT32 requestedQuality, UINT32 shadowMapResolution, UINT32 minAllowedQuality);
+
 		/**
 		/**
 		 * Generates a frustum for a single cascade of a cascaded shadow map. Also outputs spherical bounds of the
 		 * Generates a frustum for a single cascade of a cascaded shadow map. Also outputs spherical bounds of the
 		 * split view frustum.
 		 * split view frustum.
@@ -486,6 +579,8 @@ namespace bs { namespace ct
 		ShadowDepthDirectionalMat mDepthDirectionalMat;
 		ShadowDepthDirectionalMat mDepthDirectionalMat;
 
 
 		ShadowProjectStencilMaterials mProjectStencilMaterials;
 		ShadowProjectStencilMaterials mProjectStencilMaterials;
+		ShadowProjectMaterials mProjectMaterials;
+		ShadowProjectOmniMaterials mProjectOmniMaterials;
 
 
 		UINT32 mShadowMapSize;
 		UINT32 mShadowMapSize;
 
 

+ 4 - 6
Source/RenderBeast/Source/BsLightRendering.cpp

@@ -91,12 +91,10 @@ namespace bs { namespace ct
 
 
 	GBufferParams::GBufferParams(const SPtr<Material>& material, const SPtr<GpuParamsSet>& paramsSet)
 	GBufferParams::GBufferParams(const SPtr<Material>& material, const SPtr<GpuParamsSet>& paramsSet)
 	{
 	{
-		SPtr<GpuParams> params = paramsSet->getGpuParams();
-
-		params->getTextureParam(GPT_COMPUTE_PROGRAM, "gGBufferATex", mGBufferA);
-		params->getTextureParam(GPT_COMPUTE_PROGRAM, "gGBufferBTex", mGBufferB);
-		params->getTextureParam(GPT_COMPUTE_PROGRAM, "gGBufferCTex", mGBufferC);
-		params->getTextureParam(GPT_COMPUTE_PROGRAM, "gDepthBufferTex", mGBufferDepth);
+		mGBufferA = material->getParamTexture("gGBufferATex");
+		mGBufferB = material->getParamTexture("gGBufferBTex");
+		mGBufferC = material->getParamTexture("gGBufferCTex");
+		mGBufferDepth = material->getParamTexture("gDepthBufferTex");
 	}
 	}
 
 
 	void GBufferParams::bind(const SPtr<RenderTargets>& renderTargets)
 	void GBufferParams::bind(const SPtr<RenderTargets>& renderTargets)

+ 17 - 9
Source/RenderBeast/Source/BsRendererView.cpp

@@ -245,7 +245,7 @@ namespace bs { namespace ct
 		}
 		}
 	}
 	}
 
 
-	Vector2 RendererView::getDeviceZTransform(const Matrix4& projMatrix) const
+	Vector2 RendererView::getDeviceZToViewZ(const Matrix4& projMatrix)
 	{
 	{
 		// Returns a set of values that will transform depth buffer values (in range [0, 1]) to a distance
 		// Returns a set of values that will transform depth buffer values (in range [0, 1]) to a distance
 		// in view space. This involes applying the inverse projection transform to the depth value. When you multiply
 		// in view space. This involes applying the inverse projection transform to the depth value. When you multiply
@@ -292,7 +292,7 @@ namespace bs { namespace ct
 		return output;
 		return output;
 	}
 	}
 
 
-	Vector2 RendererView::getNDCZTransform(const Matrix4& projMatrix) const
+	Vector2 RendererView::getNDCZToViewZ(const Matrix4& projMatrix)
 	{
 	{
 		// Returns a set of values that will transform depth buffer values (e.g. [0, 1] in DX, [-1, 1] in GL) to a distance
 		// Returns a set of values that will transform depth buffer values (e.g. [0, 1] in DX, [-1, 1] in GL) to a distance
 		// in view space. This involes applying the inverse projection transform to the depth value. When you multiply
 		// in view space. This involes applying the inverse projection transform to the depth value. When you multiply
@@ -331,6 +331,18 @@ namespace bs { namespace ct
 		return output;
 		return output;
 	}
 	}
 
 
+	Vector2 RendererView::getNDCZToDeviceZ()
+	{
+		RenderAPI& rapi = RenderAPI::instance();
+		const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
+
+		Vector2 ndcZToDeviceZ;
+		ndcZToDeviceZ.x = 1.0f / (rapiInfo.getMaximumDepthInputValue() - rapiInfo.getMinimumDepthInputValue());
+		ndcZToDeviceZ.y = -rapiInfo.getMinimumDepthInputValue();
+
+		return ndcZToDeviceZ;
+	}
+
 	void RendererView::updatePerViewBuffer()
 	void RendererView::updatePerViewBuffer()
 	{
 	{
 		RenderAPI& rapi = RenderAPI::instance();
 		RenderAPI& rapi = RenderAPI::instance();
@@ -359,13 +371,9 @@ namespace bs { namespace ct
 		gPerCameraParamDef.gMatScreenToWorld.set(mParamBuffer, invViewProj * projZ);
 		gPerCameraParamDef.gMatScreenToWorld.set(mParamBuffer, invViewProj * projZ);
 		gPerCameraParamDef.gViewDir.set(mParamBuffer, mProperties.viewDirection);
 		gPerCameraParamDef.gViewDir.set(mParamBuffer, mProperties.viewDirection);
 		gPerCameraParamDef.gViewOrigin.set(mParamBuffer, mProperties.viewOrigin);
 		gPerCameraParamDef.gViewOrigin.set(mParamBuffer, mProperties.viewOrigin);
-		gPerCameraParamDef.gDeviceZToWorldZ.set(mParamBuffer, getDeviceZTransform(mProperties.projTransform));
-		gPerCameraParamDef.gNDCZToWorldZ.set(mParamBuffer, getNDCZTransform(mProperties.projTransform));
-
-		Vector2 ndcZToDeviceZ;
-		ndcZToDeviceZ.x = 1.0f / (rapiInfo.getMaximumDepthInputValue() - rapiInfo.getMinimumDepthInputValue());
-		ndcZToDeviceZ.y = -rapiInfo.getMinimumDepthInputValue();
-		gPerCameraParamDef.gNDCZToDeviceZ.set(mParamBuffer, ndcZToDeviceZ);
+		gPerCameraParamDef.gDeviceZToWorldZ.set(mParamBuffer, getDeviceZToViewZ(mProperties.projTransform));
+		gPerCameraParamDef.gNDCZToWorldZ.set(mParamBuffer, getNDCZToViewZ(mProperties.projTransform));
+		gPerCameraParamDef.gNDCZToDeviceZ.set(mParamBuffer, getNDCZToDeviceZ());
 
 
 		Vector2 nearFar(mProperties.nearPlane, mProperties.farPlane);
 		Vector2 nearFar(mProperties.nearPlane, mProperties.farPlane);
 		gPerCameraParamDef.gNearFar.set(mParamBuffer, nearFar);
 		gPerCameraParamDef.gNearFar.set(mParamBuffer, nearFar);

+ 159 - 70
Source/RenderBeast/Source/BsShadowRendering.cpp

@@ -181,9 +181,10 @@ namespace bs { namespace ct
 			break;
 			break;
 		}
 		}
 
 
-		defines.set("FADE_PLANE", Directional ? 1 : 0);
-		defines.set("NEEDS_TRANSFORM", Directional ? 0 : 1);
+		if(Directional)
+			defines.set("FADE_PLANE", 1);
 
 
+		defines.set("NEEDS_TRANSFORM", Directional ? 0 : 1);
 		defines.set("MSAA_COUNT", MSAA ? 2 : 1); // Actual count doesn't matter, as long as its >1 if enabled
 		defines.set("MSAA_COUNT", MSAA ? 2 : 1); // Actual count doesn't matter, as long as its >1 if enabled
 	}
 	}
 
 
@@ -229,39 +230,58 @@ namespace bs { namespace ct
 	}
 	}
 
 
 	template <int ShadowQuality, bool Directional, bool MSAA>
 	template <int ShadowQuality, bool Directional, bool MSAA>
-	void ShadowProjectMat<ShadowQuality, Directional, MSAA>::bind(const Light& light, const SPtr<Texture>& shadowMap, 
-		const SPtr<GpuParamBlockBuffer> shadowParams, const SPtr<GpuParamBlockBuffer>& perCamera)
+	void ShadowProjectMat<ShadowQuality, Directional, MSAA>::bind(const ShadowProjectParams& params)
 	{
 	{
-		Vector4 lightPosAndScale(light.getPosition(), light.getAttenuationRadius());
+		Vector4 lightPosAndScale(params.light.getPosition(), params.light.getAttenuationRadius());
 		gShadowProjectVertParamsDef.gPositionAndScale.set(mVertParams, lightPosAndScale);
 		gShadowProjectVertParamsDef.gPositionAndScale.set(mVertParams, lightPosAndScale);
 
 
-		mShadowMapParam.set(shadowMap);
+		TextureSurface surface;
+		surface.arraySlice = params.shadowMapFace;
+
+		mShadowMapParam.set(params.shadowMap, surface);
 		mShadowSamplerParam.set(mSamplerState);
 		mShadowSamplerParam.set(mSamplerState);
 
 
-		mParamsSet->setParamBlockBuffer("Params", shadowParams);
-		mParamsSet->setParamBlockBuffer("PerCamera", perCamera);
+		mGBufferParams.bind(params.renderTargets);
+
+		mParamsSet->setParamBlockBuffer("Params", params.shadowParams);
+		mParamsSet->setParamBlockBuffer("PerCamera", params.perCamera);
 
 
 		gRendererUtility().setPass(mMaterial);
 		gRendererUtility().setPass(mMaterial);
 		gRendererUtility().setPassParams(mParamsSet);
 		gRendererUtility().setPassParams(mParamsSet);
 	}
 	}
 
 
-#define TEMPL_INSTANTIATE(QUALITY)								\
-	template class ShadowProjectMat<QUALITY, true, true>;		\
-	template class ShadowProjectMat<QUALITY, true, false>;		\
-	template class ShadowProjectMat<QUALITY, false, true>;		\
-	template class ShadowProjectMat<QUALITY, false, false>;
-	
-TEMPL_INSTANTIATE(0)
-TEMPL_INSTANTIATE(1)
-TEMPL_INSTANTIATE(2)
-TEMPL_INSTANTIATE(3)
+	void ShadowProjectMaterials::bind(UINT32 quality, bool directional, bool MSAA, const ShadowProjectParams& params)
+	{
+#define BIND_MAT(QUALITY)						\
+	{											\
+		if(directional)							\
+			if (MSAA)							\
+				mMat##QUALITY##TT.bind(params);	\
+			else								\
+				mMat##QUALITY##TF.bind(params);	\
+		else									\
+			if (MSAA)							\
+				mMat##QUALITY##FT.bind(params);	\
+			else								\
+				mMat##QUALITY##FF.bind(params);	\
+	}
 
 
-#undef TEMPL_INSTANTIATE
+		if(quality <= 1)
+			BIND_MAT(1)
+		else if(quality == 2)
+			BIND_MAT(2)
+		else if(quality == 3)
+			BIND_MAT(3)
+		else // 4 or higher
+			BIND_MAT(4)
+
+#undef BIND_MAT
+	}
 
 
 	ShadowProjectOmniParamsDef gShadowProjectOmniParamsDef;
 	ShadowProjectOmniParamsDef gShadowProjectOmniParamsDef;
 
 
-	template<int ShadowQuality, bool MSAA>
-	ShadowProjectOmniMat<ShadowQuality, MSAA>::ShadowProjectOmniMat()
+	template<int ShadowQuality, bool Inside, bool MSAA>
+	ShadowProjectOmniMat<ShadowQuality, Inside, MSAA>::ShadowProjectOmniMat()
 		: mGBufferParams(mMaterial, mParamsSet)
 		: mGBufferParams(mMaterial, mParamsSet)
 	{
 	{
 		SPtr<GpuParams> params = mParamsSet->getGpuParams();
 		SPtr<GpuParams> params = mParamsSet->getGpuParams();
@@ -285,8 +305,8 @@ TEMPL_INSTANTIATE(3)
 			params->setParamBlockBuffer(GPT_VERTEX_PROGRAM, "VertParams", mVertParams);
 			params->setParamBlockBuffer(GPT_VERTEX_PROGRAM, "VertParams", mVertParams);
 	}
 	}
 
 
-	template<int ShadowQuality, bool MSAA>
-	void ShadowProjectOmniMat<ShadowQuality, MSAA>::_initDefines(ShaderDefines& defines)
+	template<int ShadowQuality, bool Inside, bool MSAA>
+	void ShadowProjectOmniMat<ShadowQuality, Inside, MSAA>::_initDefines(ShaderDefines& defines)
 	{
 	{
 		switch(ShadowQuality)
 		switch(ShadowQuality)
 		{
 		{
@@ -307,35 +327,56 @@ TEMPL_INSTANTIATE(3)
 
 
 		defines.set("NEEDS_TRANSFORM", 1);
 		defines.set("NEEDS_TRANSFORM", 1);
 		defines.set("MSAA_COUNT", MSAA ? 2 : 1); // Actual count doesn't matter, as long as its >1 if enabled
 		defines.set("MSAA_COUNT", MSAA ? 2 : 1); // Actual count doesn't matter, as long as its >1 if enabled
+		
+		if (Inside)
+			defines.set("VIEWER_INSIDE_VOLUME", 1);
 	}
 	}
 
 
-	template <int ShadowQuality, bool MSAA>
-	void ShadowProjectOmniMat<ShadowQuality, MSAA>::bind(const Light& light, const SPtr<Texture>& shadowMap, 
-		const SPtr<GpuParamBlockBuffer> shadowParams, const SPtr<GpuParamBlockBuffer>& perCamera)
+	template<int ShadowQuality, bool Inside, bool MSAA>
+	void ShadowProjectOmniMat<ShadowQuality, Inside, MSAA>::bind(const ShadowProjectParams& params)
 	{
 	{
-		Vector4 lightPosAndScale(light.getPosition(), light.getAttenuationRadius());
+		Vector4 lightPosAndScale(params.light.getPosition(), params.light.getAttenuationRadius());
 		gShadowProjectVertParamsDef.gPositionAndScale.set(mVertParams, lightPosAndScale);
 		gShadowProjectVertParamsDef.gPositionAndScale.set(mVertParams, lightPosAndScale);
 
 
-		mShadowMapParam.set(shadowMap);
+		mShadowMapParam.set(params.shadowMap);
 		mShadowSamplerParam.set(mSamplerState);
 		mShadowSamplerParam.set(mSamplerState);
 
 
-		mParamsSet->setParamBlockBuffer("Params", shadowParams);
-		mParamsSet->setParamBlockBuffer("PerCamera", perCamera);
+		mGBufferParams.bind(params.renderTargets);
+
+		mParamsSet->setParamBlockBuffer("Params", params.shadowParams);
+		mParamsSet->setParamBlockBuffer("PerCamera", params.perCamera);
 
 
 		gRendererUtility().setPass(mMaterial);
 		gRendererUtility().setPass(mMaterial);
 		gRendererUtility().setPassParams(mParamsSet);
 		gRendererUtility().setPassParams(mParamsSet);
 	}
 	}
 
 
-#define TEMPL_INSTANTIATE(QUALITY)								\
-	template class ShadowProjectOmniMat<QUALITY, true>;			\
-	template class ShadowProjectOmniMat<QUALITY, false>;		\
-	
-TEMPL_INSTANTIATE(0)
-TEMPL_INSTANTIATE(1)
-TEMPL_INSTANTIATE(2)
-TEMPL_INSTANTIATE(3)
+	void ShadowProjectOmniMaterials::bind(UINT32 quality, bool directional, bool MSAA, const ShadowProjectParams& params)
+	{
+#define BIND_MAT(QUALITY)						\
+	{											\
+		if(directional)							\
+			if (MSAA)							\
+				mMat##QUALITY##TT.bind(params);	\
+			else								\
+				mMat##QUALITY##TF.bind(params);	\
+		else									\
+			if (MSAA)							\
+				mMat##QUALITY##FT.bind(params);	\
+			else								\
+				mMat##QUALITY##FF.bind(params);	\
+	}
+
+		if(quality <= 1)
+			BIND_MAT(1)
+		else if(quality == 2)
+			BIND_MAT(2)
+		else if(quality == 3)
+			BIND_MAT(3)
+		else // 4 or higher
+			BIND_MAT(4)
 
 
-#undef TEMPL_INSTANTIATE
+#undef BIND_MAT
+	}
 
 
 	void ShadowInfo::updateNormArea(UINT32 atlasSize)
 	void ShadowInfo::updateNormArea(UINT32 atlasSize)
 	{
 	{
@@ -706,8 +747,8 @@ TEMPL_INSTANTIATE(3)
 		return output;
 		return output;
 	}
 	}
 
 
-	void ShadowRendering::renderShadowOcclusion(const RendererScene& scene, const RendererLight& rendererLight, 
-		UINT32 viewIdx)
+	void ShadowRendering::renderShadowOcclusion(const RendererScene& scene, UINT32 shadowQuality, 
+		const RendererLight& rendererLight, UINT32 viewIdx)
 	{
 	{
 		const Light* light = rendererLight.internal;
 		const Light* light = rendererLight.internal;
 		UINT32 lightIdx = light->getRendererId();
 		UINT32 lightIdx = light->getRendererId();
@@ -723,8 +764,8 @@ TEMPL_INSTANTIATE(3)
 		RenderAPI& rapi = RenderAPI::instance();
 		RenderAPI& rapi = RenderAPI::instance();
 		// TODO - Calculate and set a scissor rectangle for the light
 		// TODO - Calculate and set a scissor rectangle for the light
 
 
-		SPtr<GpuParamBlockBuffer> shadowParams = gShadowProjectParamsDef.createBuffer();
-		SPtr<GpuParamBlockBuffer> shadowOmniParams = gShadowProjectOmniParamsDef.createBuffer();
+		SPtr<GpuParamBlockBuffer> shadowParamBuffer = gShadowProjectParamsDef.createBuffer();
+		SPtr<GpuParamBlockBuffer> shadowOmniParamBuffer = gShadowProjectOmniParamsDef.createBuffer();
 
 
 		Vector<const ShadowInfo*> shadowInfos;
 		Vector<const ShadowInfo*> shadowInfos;
 
 
@@ -741,16 +782,28 @@ TEMPL_INSTANTIATE(3)
 					continue;
 					continue;
 
 
 				for(UINT32 j = 0; j < 6; j++)
 				for(UINT32 j = 0; j < 6; j++)
-					gShadowProjectOmniParamsDef.gFaceVPMatrices.set(shadowOmniParams, shadowInfo.shadowVPTransforms[j], j);
+					gShadowProjectOmniParamsDef.gFaceVPMatrices.set(shadowOmniParamBuffer, shadowInfo.shadowVPTransforms[j], j);
 
 
-				gShadowProjectOmniParamsDef.gDepthBias.set(shadowOmniParams, shadowInfo.depthBias);
-				gShadowProjectOmniParamsDef.gFadePercent.set(shadowOmniParams, shadowInfo.fadePerView[viewIdx]);
-				gShadowProjectOmniParamsDef.gInvResolution.set(shadowOmniParams, 1.0f / shadowInfo.area.width);
+				gShadowProjectOmniParamsDef.gDepthBias.set(shadowOmniParamBuffer, shadowInfo.depthBias);
+				gShadowProjectOmniParamsDef.gFadePercent.set(shadowOmniParamBuffer, shadowInfo.fadePerView[viewIdx]);
+				gShadowProjectOmniParamsDef.gInvResolution.set(shadowOmniParamBuffer, 1.0f / shadowInfo.area.width);
 
 
 				Vector4 lightPosAndRadius(light->getPosition(), light->getAttenuationRadius());
 				Vector4 lightPosAndRadius(light->getPosition(), light->getAttenuationRadius());
-				gShadowProjectOmniParamsDef.gLightPosAndRadius.set(shadowOmniParams, lightPosAndRadius);
+				gShadowProjectOmniParamsDef.gLightPosAndRadius.set(shadowOmniParamBuffer, lightPosAndRadius);
+
+				// Reduce shadow quality based on shadow map resolution for spot lights
+				UINT32 effectiveShadowQuality = getShadowQuality(shadowQuality, shadowInfo.area.width, 2);
+
+				// Check if viewer is inside the light bounds
+				//// Expand the light bounds slightly to handle the case when the near plane is intersecting the light volume
+				float lightRadius = light->getAttenuationRadius() + viewProps.nearPlane * 3.0f;
+				bool viewerInsideVolume = (light->getPosition() - viewProps.viewOrigin).length() < lightRadius;
 
 
-				// TODO - Bind material & render stencil geometry (also check if inside or outside of the volume?)
+				SPtr<Texture> shadowMap = mShadowCubemaps[shadowInfo.textureIdx].getTexture();
+				SPtr<RenderTargets> renderTargets = view->getRenderTargets();
+
+				ShadowProjectParams shadowParams(*light, shadowMap, 0, shadowParamBuffer, perViewBuffer, renderTargets);
+				mProjectOmniMaterials.bind(effectiveShadowQuality, viewerInsideVolume, viewProps.numSamples > 1, shadowParams);
 
 
 				gRendererUtility().draw(gRendererUtility().getRadialLightStencil());
 				gRendererUtility().draw(gRendererUtility().getRadialLightStencil());
 			}
 			}
@@ -759,7 +812,8 @@ TEMPL_INSTANTIATE(3)
 		{
 		{
 			shadowInfos.clear();
 			shadowInfos.clear();
 
 
-			if(light->getType() == LightType::Spot)
+			bool isCSM = light->getType() == LightType::Directional;
+			if(!isCSM)
 			{
 			{
 				const LightShadows& shadows = mSpotLightShadows[lightIdx];
 				const LightShadows& shadows = mSpotLightShadows[lightIdx];
 				for (UINT32 i = 0; i < shadows.numShadows; ++i)
 				for (UINT32 i = 0; i < shadows.numShadows; ++i)
@@ -778,7 +832,9 @@ TEMPL_INSTANTIATE(3)
 				UINT32 mapIdx = mDirectionalLightShadows[lightIdx];
 				UINT32 mapIdx = mDirectionalLightShadows[lightIdx];
 				ShadowCascadedMap& cascadedMap = mCascadedShadowMaps[mapIdx];
 				ShadowCascadedMap& cascadedMap = mCascadedShadowMaps[mapIdx];
 
 
-				for (UINT32 i = 0; i < NUM_CASCADE_SPLITS; i++)
+				// Render cascades in far to near order.
+				// Note: If rendering other non-cascade maps they should be rendered after cascades.
+				for (UINT32 i = NUM_CASCADE_SPLITS; i >= 0; i--)
 					shadowInfos.push_back(&cascadedMap.getShadowInfo(i));
 					shadowInfos.push_back(&cascadedMap.getShadowInfo(i));
 			}
 			}
 
 
@@ -790,17 +846,18 @@ TEMPL_INSTANTIATE(3)
 				Vector2 shadowMapSize((float)shadowInfo->area.width, (float)shadowInfo->area.height);
 				Vector2 shadowMapSize((float)shadowInfo->area.width, (float)shadowInfo->area.height);
 				float transitionScale = getFadeTransition(*light, shadowInfo->depthRange, shadowInfo->area.width);
 				float transitionScale = getFadeTransition(*light, shadowInfo->depthRange, shadowInfo->area.width);
 
 
-				gShadowProjectParamsDef.gFadePercent.set(shadowParams, shadowInfo->fadePerView[viewIdx]);
-				gShadowProjectParamsDef.gFadePlaneDepth.set(shadowParams, shadowInfo->depthFade);
-				gShadowProjectParamsDef.gInvFadePlaneRange.set(shadowParams, 1.0f / shadowInfo->fadeRange);
-				gShadowProjectParamsDef.gMixedToShadowSpace.set(shadowParams, mixedToShadowUV);
-				gShadowProjectParamsDef.gShadowMapSize.set(shadowParams, shadowMapSize);
-				gShadowProjectParamsDef.gShadowMapSizeInv.set(shadowParams, 1.0f / shadowMapSize);
-				gShadowProjectParamsDef.gSoftTransitionScale.set(shadowParams, transitionScale);
+				gShadowProjectParamsDef.gFadePercent.set(shadowParamBuffer, shadowInfo->fadePerView[viewIdx]);
+				gShadowProjectParamsDef.gFadePlaneDepth.set(shadowParamBuffer, shadowInfo->depthFade);
+				gShadowProjectParamsDef.gInvFadePlaneRange.set(shadowParamBuffer, 1.0f / shadowInfo->fadeRange);
+				gShadowProjectParamsDef.gMixedToShadowSpace.set(shadowParamBuffer, mixedToShadowUV);
+				gShadowProjectParamsDef.gShadowMapSize.set(shadowParamBuffer, shadowMapSize);
+				gShadowProjectParamsDef.gShadowMapSizeInv.set(shadowParamBuffer, 1.0f / shadowMapSize);
+				gShadowProjectParamsDef.gSoftTransitionScale.set(shadowParamBuffer, transitionScale);
 
 
 				// Generate a stencil buffer to avoid evaluating pixels without any receiver geometry in the shadow area
 				// Generate a stencil buffer to avoid evaluating pixels without any receiver geometry in the shadow area
 				std::array<Vector3, 8> frustumVertices;
 				std::array<Vector3, 8> frustumVertices;
-				if(light->getType() == LightType::Spot)
+				UINT32 effectiveShadowQuality = shadowQuality;
+				if(!isCSM)
 				{
 				{
 					ConvexVolume shadowFrustum;
 					ConvexVolume shadowFrustum;
 					frustumVertices = getFrustum(shadowInfo->shadowVPTransform.inverse(), shadowFrustum);
 					frustumVertices = getFrustum(shadowInfo->shadowVPTransform.inverse(), shadowFrustum);
@@ -812,6 +869,9 @@ TEMPL_INSTANTIATE(3)
 
 
 					mProjectStencilMaterials.bind(false, viewerInsideFrustum, perViewBuffer);
 					mProjectStencilMaterials.bind(false, viewerInsideFrustum, perViewBuffer);
 					drawFrustum(frustumVertices);
 					drawFrustum(frustumVertices);
+
+					// Reduce shadow quality based on shadow map resolution for spot lights
+					effectiveShadowQuality = getShadowQuality(shadowQuality, shadowInfo->area.width, 2);
 				}
 				}
 				else
 				else
 				{
 				{
@@ -825,14 +885,22 @@ TEMPL_INSTANTIATE(3)
 					drawNearFarPlanes(near.z, far.z, shadowInfo->cascadeIdx != 0);
 					drawNearFarPlanes(near.z, far.z, shadowInfo->cascadeIdx != 0);
 				}
 				}
 
 
-				// TODO - Update projection shaders so they test against the stencil buffer, and clear it to zero
-
-				// TODO - Update shaders so they set relevant blend states
+				SPtr<Texture> shadowMap;
+				UINT32 shadowMapFace = 0;
+				if(!isCSM)
+					shadowMap = mDynamicShadowMaps[shadowInfo->textureIdx].getTexture();
+				else
+				{
+					shadowMap = mCascadedShadowMaps[shadowInfo->textureIdx].getTexture();
+					shadowMapFace = shadowInfo->cascadeIdx;
+				}
 
 
-				// TODO - Bind material & render. Pick material depending on selected shadow quality, MSAA setting, light type
-				//      - Reduce shadow quality based on distance for CSM shadows
+				SPtr<RenderTargets> renderTargets = view->getRenderTargets();
+				ShadowProjectParams shadowParams(*light, shadowMap, shadowMapFace, shadowParamBuffer, perViewBuffer, 
+					renderTargets);
+				mProjectMaterials.bind(effectiveShadowQuality, isCSM, viewProps.numSamples > 1, shadowParams);
 
 
-				if(light->getType() == LightType::Spot)
+				if(!isCSM)
 					drawFrustum(frustumVertices);
 					drawFrustum(frustumVertices);
 				else
 				else
 					gRendererUtility().drawScreenQuad();
 					gRendererUtility().drawScreenQuad();
@@ -918,8 +986,10 @@ TEMPL_INSTANTIATE(3)
 			shadowInfo.depthBias = getDepthBias(*light, shadowInfo.depthRange, mapSize);
 			shadowInfo.depthBias = getDepthBias(*light, shadowInfo.depthRange, mapSize);
 
 
 			gShadowParamsDef.gDepthBias.set(shadowParamsBuffer, shadowInfo.depthBias);
 			gShadowParamsDef.gDepthBias.set(shadowParamsBuffer, shadowInfo.depthBias);
-			gShadowParamsDef.gDepthRange.set(shadowParamsBuffer, shadowInfo.depthRange);
+			gShadowParamsDef.gInvDepthRange.set(shadowParamsBuffer, 1.0f / shadowInfo.depthRange);
 			gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, shadowInfo.shadowVPTransform);
 			gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, shadowInfo.shadowVPTransform);
+			gShadowParamsDef.gNDCZToDeviceZ.set(shadowParamsBuffer, RendererView::getNDCZToDeviceZ());
+			gShadowParamsDef.gNDCZToViewZ.set(shadowParamsBuffer, RendererView::getNDCZToViewZ(proj));
 
 
 			rapi.setRenderTarget(shadowMap.getTarget(i));
 			rapi.setRenderTarget(shadowMap.getTarget(i));
 			rapi.clearRenderTarget(FBT_DEPTH);
 			rapi.clearRenderTarget(FBT_DEPTH);
@@ -1010,8 +1080,10 @@ TEMPL_INSTANTIATE(3)
 		mapInfo.shadowVPTransform = proj * view;
 		mapInfo.shadowVPTransform = proj * view;
 
 
 		gShadowParamsDef.gDepthBias.set(shadowParamsBuffer, mapInfo.depthBias);
 		gShadowParamsDef.gDepthBias.set(shadowParamsBuffer, mapInfo.depthBias);
-		gShadowParamsDef.gDepthRange.set(shadowParamsBuffer, mapInfo.depthRange);
+		gShadowParamsDef.gInvDepthRange.set(shadowParamsBuffer, 1.0f / mapInfo.depthRange);
 		gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, mapInfo.shadowVPTransform);
 		gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, mapInfo.shadowVPTransform);
+		gShadowParamsDef.gNDCZToDeviceZ.set(shadowParamsBuffer, RendererView::getNDCZToDeviceZ());
+		gShadowParamsDef.gNDCZToViewZ.set(shadowParamsBuffer, RendererView::getNDCZToViewZ(proj));
 
 
 		mDepthNormalMat.bind(shadowParamsBuffer);
 		mDepthNormalMat.bind(shadowParamsBuffer);
 
 
@@ -1111,8 +1183,10 @@ TEMPL_INSTANTIATE(3)
 		ConvexVolume localFrustum(proj);
 		ConvexVolume localFrustum(proj);
 
 
 		gShadowParamsDef.gDepthBias.set(shadowParamsBuffer, mapInfo.depthBias);
 		gShadowParamsDef.gDepthBias.set(shadowParamsBuffer, mapInfo.depthBias);
-		gShadowParamsDef.gDepthRange.set(shadowParamsBuffer, mapInfo.depthRange);
+		gShadowParamsDef.gInvDepthRange.set(shadowParamsBuffer, 1.0f / mapInfo.depthRange);
 		gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, Matrix4::IDENTITY);
 		gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, Matrix4::IDENTITY);
+		gShadowParamsDef.gNDCZToDeviceZ.set(shadowParamsBuffer, RendererView::getNDCZToDeviceZ());
+		gShadowParamsDef.gNDCZToViewZ.set(shadowParamsBuffer, RendererView::getNDCZToViewZ(proj));
 
 
 		Matrix4 viewOffsetMat = Matrix4::translation(-light->getPosition());
 		Matrix4 viewOffsetMat = Matrix4::translation(-light->getPosition());
 
 
@@ -1274,7 +1348,7 @@ TEMPL_INSTANTIATE(3)
 			{  1.0f,  1.0f * flipY, near },
 			{  1.0f,  1.0f * flipY, near },
 			{ -1.0f,  1.0f * flipY, near },
 			{ -1.0f,  1.0f * flipY, near },
 
 
-			// Near plane
+			// Far plane
 			{ -1.0f, -1.0f * flipY, far },
 			{ -1.0f, -1.0f * flipY, far },
 			{  1.0f, -1.0f * flipY, far },
 			{  1.0f, -1.0f * flipY, far },
 			{  1.0f,  1.0f * flipY, far },
 			{  1.0f,  1.0f * flipY, far },
@@ -1297,7 +1371,7 @@ TEMPL_INSTANTIATE(3)
 		RenderAPI& rapi = RenderAPI::instance();
 		RenderAPI& rapi = RenderAPI::instance();
 
 
 		// Update VB with new vertices
 		// Update VB with new vertices
-		mPlaneVB->writeData(0, sizeof(Vector3) * 8, corners.data(), BWT_DISCARD);
+		mFrustumVB->writeData(0, sizeof(Vector3) * 8, corners.data(), BWT_DISCARD);
 
 
 		// Draw the mesh
 		// Draw the mesh
 		rapi.setVertexDeclaration(mPositionOnlyVD);
 		rapi.setVertexDeclaration(mPositionOnlyVD);
@@ -1308,6 +1382,21 @@ TEMPL_INSTANTIATE(3)
 		rapi.drawIndexed(0, 36, 0, 8);
 		rapi.drawIndexed(0, 36, 0, 8);
 	}
 	}
 
 
+	UINT32 ShadowRendering::getShadowQuality(UINT32 requestedQuality, UINT32 shadowMapResolution, UINT32 minAllowedQuality)
+	{
+		static const UINT32 TARGET_RESOLUTION = 512;
+
+		// If shadow map resolution is smaller than some target resolution drop the number of PCF samples (shadow quality)
+		// so that the penumbra better matches with larger sized shadow maps.
+		while(requestedQuality > minAllowedQuality && shadowMapResolution < TARGET_RESOLUTION)
+		{
+			shadowMapResolution *= 2;
+			requestedQuality = std::max(requestedQuality - 1, 1U);
+		}
+
+		return requestedQuality;
+	}
+
 	ConvexVolume ShadowRendering::getCSMSplitFrustum(const RendererView& view, const Vector3& lightDir, UINT32 cascade, 
 	ConvexVolume ShadowRendering::getCSMSplitFrustum(const RendererView& view, const Vector3& lightDir, UINT32 cascade, 
 		UINT32 numCascades, Sphere& outBounds)
 		UINT32 numCascades, Sphere& outBounds)
 	{
 	{