Răsfoiți Sursa

Bugfix: Shadow rendering now works correctly and will account for animated meshes

BearishSun 8 ani în urmă
părinte
comite
4c16e8e817

BIN
Data/Engine/Includes/ShadowDepthBase.bslinc.asset


BIN
Data/Engine/Includes/VertexInput.bslinc.asset


+ 3 - 12
Data/Engine/ShaderDependencies.json

@@ -490,42 +490,33 @@
         {
             "Path": "ShadowDepthBase.bslinc"
         },
-        {
-            "Path": "NormalVertexInput.bslinc"
-        },
         {
             "Path": "PerObjectData.bslinc"
         },
         {
-            "Path": "SkinnedVertexInput.bslinc"
+            "Path": "VertexInput.bslinc"
         }
     ],
     "ShadowDepthDirectional.bsl": [
         {
             "Path": "ShadowDepthBase.bslinc"
         },
-        {
-            "Path": "NormalVertexInput.bslinc"
-        },
         {
             "Path": "PerObjectData.bslinc"
         },
         {
-            "Path": "SkinnedVertexInput.bslinc"
+            "Path": "VertexInput.bslinc"
         }
     ],
     "ShadowDepthNormal.bsl": [
         {
             "Path": "ShadowDepthBase.bslinc"
         },
-        {
-            "Path": "NormalVertexInput.bslinc"
-        },
         {
             "Path": "PerObjectData.bslinc"
         },
         {
-            "Path": "SkinnedVertexInput.bslinc"
+            "Path": "VertexInput.bslinc"
         }
     ],
     "ShadowProject.bsl": [

BIN
Data/Engine/Shaders/ShadowDepthCube.bsl.asset


BIN
Data/Engine/Shaders/ShadowDepthDirectional.bsl.asset


BIN
Data/Engine/Shaders/ShadowDepthNormal.bsl.asset


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

@@ -159,6 +159,10 @@
         {
             "Path": "PPEyeAdaptationCommon.bslinc",
             "UUID": "1a0c4fb5-49b6-7eb8-28b8-49b698a6b7fe"
+        },
+        {
+            "Path": "VertexInput.bslinc",
+            "UUID": "2d0dc7e6-46bf-03bb-f48d-46bf25dda973"
         }
     ],
     "Shaders": [

+ 5 - 46
Data/Raw/Engine/Includes/ShadowDepthBase.bslinc

@@ -1,14 +1,11 @@
 #include "$ENGINE$\PerObjectData.bslinc"
-
-#include "$ENGINE$\SkinnedVertexInput.bslinc"
-#include "$ENGINE$\NormalVertexInput.bslinc"
-#define USE_BLEND_SHAPES
-#include "$ENGINE$\SkinnedVertexInput.bslinc"
-#include "$ENGINE$\NormalVertexInput.bslinc"
-#undef USE_BLEND_SHAPES
+#include "$ENGINE$\VertexInput.bslinc"
 
 mixin ShadowDepthBase
 {
+	mixin PerObjectData;
+	mixin VertexInput;
+
 	code
 	{
 		struct ShadowVStoFS
@@ -16,7 +13,7 @@ mixin ShadowDepthBase
 			float4 position : SV_Position;
 			
 			#ifdef USES_GS
-			float4 worldPos : TEXCOORD0;
+				float4 worldPos : TEXCOORD0;
 			#else
 			#ifdef USES_PS
 				float shadowPos : TEXCOORD0;
@@ -108,42 +105,4 @@ mixin ShadowDepthBase
 			return output;
 		}
 	};
-};
-
-technique ShadowDepth
-{
-	mixin PerObjectData;
-	mixin NormalVertexInput;
-	mixin ShadowDepthBase;
-	mixin ShadowDepth;
-};
-
-technique ShadowDepthSkinned
-{
-	mixin PerObjectData;
-	mixin SkinnedVertexInput;
-	mixin ShadowDepthBase;
-	mixin ShadowDepth;
-
-	tags = { "Skinned" };
-};
-
-technique ShadowDepthMorph
-{
-	mixin PerObjectData;
-	mixin MorphVertexInput;
-	mixin ShadowDepthBase;
-	mixin ShadowDepth;
-
-	tags = { "Morph" };
-};
-
-technique ShadowDepthSkinnedMorph
-{
-	mixin PerObjectData;
-	mixin SkinnedMorphVertexInput;
-	mixin ShadowDepthBase;
-	mixin ShadowDepth;
-
-	tags = { "SkinnedMorph" };
 };

+ 177 - 0
Data/Raw/Engine/Includes/VertexInput.bslinc

@@ -0,0 +1,177 @@
+mixin VertexInput
+{
+	variations
+	{
+		SKINNED = { false, true };
+		MORPH = { false, true };
+	};
+
+	code
+	{
+		struct VStoFS
+		{
+			float4 position : SV_Position;
+			float2 uv0 : TEXCOORD0;
+			float3 worldPosition : TEXCOORD1;
+			
+			float3 tangentToWorldZ : NORMAL; // Note: Half-precision could be used
+			float4 tangentToWorldX : TANGENT; // Note: Half-precision could be used
+		};
+
+		struct VertexInput
+		{
+			float3 position : POSITION;
+			float3 normal : NORMAL; // Note: Half-precision could be used
+			float4 tangent : TANGENT; // Note: Half-precision could be used
+			float2 uv0 : TEXCOORD0;
+			
+			#if SKINNED
+				uint4 blendIndices : BLENDINDICES;
+				float4 blendWeights : BLENDWEIGHT;
+			#endif
+			
+			#if MORPH
+				float3 deltaPosition : POSITION1;
+				float4 deltaNormal : NORMAL1;
+			#endif				
+		};
+		
+		// Vertex input containing only position data
+		struct VertexInput_PO
+		{
+			float3 position : POSITION;
+			
+			#if SKINNED
+				uint4 blendIndices : BLENDINDICES;
+				float4 blendWeights : BLENDWEIGHT;
+			#endif			
+			
+			#if MORPH
+				float3 deltaPosition : POSITION1;
+			#endif	
+		};			
+		
+		struct VertexIntermediate
+		{
+			#if SKINNED
+				float3x4 blendMatrix;
+			#endif
+		
+			float3 worldNormal; // Note: Half-precision could be used
+			float4 worldTangent; // Note: Half-precision could be used
+		};
+		
+		#if SKINNED
+		Buffer<float4> boneMatrices;
+		
+		float3x4 getBoneMatrix(uint idx)
+		{
+			float4 row0 = boneMatrices[idx * 3 + 0];
+			float4 row1 = boneMatrices[idx * 3 + 1];
+			float4 row2 = boneMatrices[idx * 3 + 2];
+			
+			return float3x4(row0, row1, row2);
+		}
+		
+		float3x4 getBlendMatrix(float4 blendWeights, uint4 blendIndices)
+		{
+			float3x4 result = blendWeights.x * getBoneMatrix(blendIndices.x);
+			result += blendWeights.y * getBoneMatrix(blendIndices.y);
+			result += blendWeights.z * getBoneMatrix(blendIndices.z);
+			result += blendWeights.w * getBoneMatrix(blendIndices.w);
+			
+			return result;
+		}
+		#endif
+		
+		float3x3 getTangentToLocal(VertexInput input, out float tangentSign
+			#if SKINNED
+			, float3x4 blendMatrix
+			#endif
+			)
+		{
+			float3 normal = input.normal * 2.0f - 1.0f;
+			float3 tangent = input.tangent.xyz * 2.0f - 1.0f;
+			
+			#if MORPH
+				float3 deltaNormal = (input.deltaNormal.xyz * 2.0f - 1.0f) * 2.0f;
+				normal = normalize(normal + deltaNormal * input.deltaNormal.w);
+				tangent = normalize(tangent - dot(tangent, normal) * normal);
+			#endif
+			
+			#if SKINNED
+				normal = mul(blendMatrix, float4(normal, 0.0f)).xyz;
+				tangent = mul(blendMatrix, float4(tangent, 0.0f)).xyz;
+			#endif
+			
+			float3 bitangent = cross(normal, tangent) * input.tangent.w;
+			tangentSign = input.tangent.w * gWorldDeterminantSign;
+			
+			// Note: Maybe it's better to store everything in row vector format?
+			float3x3 result = float3x3(tangent, bitangent, normal);
+			result = transpose(result);
+											
+			return result;
+		}
+		
+		VertexIntermediate getVertexIntermediate(VertexInput input)
+		{
+			VertexIntermediate result;
+			
+			float tangentSign;
+			#if SKINNED
+				result.blendMatrix = getBlendMatrix(input.blendWeights, input.blendIndices);
+				float3x3 tangentToLocal = getTangentToLocal(input, tangentSign, result.blendMatrix);
+			#else
+				float3x3 tangentToLocal = getTangentToLocal(input, tangentSign);
+			#endif
+			
+			float3x3 tangentToWorld = mul((float3x3)gMatWorldNoScale, tangentToLocal);
+			
+			// Note: Consider transposing these externally, for easier reads
+			result.worldNormal = float3(tangentToWorld[0][2], tangentToWorld[1][2], tangentToWorld[2][2]); // Normal basis vector
+			result.worldTangent = float4(tangentToWorld[0][0], tangentToWorld[1][0], tangentToWorld[2][0], tangentSign); // Tangent basis vector
+			
+			return result;
+		}
+		
+		float4 getVertexWorldPosition(VertexInput input, VertexIntermediate intermediate)
+		{
+			#if MORPH
+				float4 position = float4(input.position + input.deltaPosition, 1.0f);
+			#else
+				float4 position = float4(input.position, 1.0f);
+			#endif			
+		
+			#if SKINNED
+				position = float4(mul(intermediate.blendMatrix, position), 1.0f);
+			#endif
+		
+			return mul(gMatWorld, position);
+		}
+		
+		float4 getVertexWorldPosition(VertexInput_PO input)
+		{
+			#if MORPH
+				float4 position = float4(input.position + input.deltaPosition, 1.0f);
+			#else
+				float4 position = float4(input.position, 1.0f);
+			#endif			
+		
+			#if SKINNED
+				float3x4 blendMatrix = getBlendMatrix(input.blendWeights, input.blendIndices);
+				position = float4(mul(blendMatrix, position), 1.0f);
+			#endif
+		
+			return mul(gMatWorld, position);
+		}		
+		
+		void populateVertexOutput(VertexInput input, VertexIntermediate intermediate, inout VStoFS result)
+		{
+			result.uv0 = input.uv0;
+			
+			result.tangentToWorldZ = intermediate.worldNormal;
+			result.tangentToWorldX = intermediate.worldTangent;
+		}
+	};
+};

+ 3 - 1
Data/Raw/Engine/Shaders/ShadowDepthCube.bsl

@@ -1,8 +1,10 @@
 #define USES_GS
 #include "$ENGINE$\ShadowDepthBase.bslinc"
 
-mixin ShadowDepth
+technique ShadowDepth
 { 
+	mixin ShadowDepthBase;
+	
 	code
 	{
 		struct GSToPS

+ 4 - 2
Data/Raw/Engine/Shaders/ShadowDepthDirectional.bsl

@@ -1,5 +1,7 @@
 #define CLAMP_TO_NEAR_PLANE 1
 #include "$ENGINE$\ShadowDepthBase.bslinc"
 
-mixin ShadowDepth
-{ };
+technique ShadowDepth
+{ 
+	mixin ShadowDepthBase;
+};

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

@@ -1,8 +1,10 @@
 #define USES_PS
 #include "$ENGINE$\ShadowDepthBase.bslinc"
 
-mixin ShadowDepth
+technique ShadowDepth
 { 
+	mixin ShadowDepthBase;
+
 	code
 	{
 		float4 fsmain(ShadowVStoFS input, out float outDepth : SV_Depth) : SV_Target0

+ 23 - 1
Source/BansheeCore/Renderer/BsRenderer.h

@@ -5,6 +5,7 @@
 #include "BsCorePrerequisites.h"
 #include "String/BsStringID.h"
 #include "Renderer/BsRendererMeshData.h"
+#include "Material/BsShaderVariation.h"
 
 namespace bs 
 { 
@@ -21,6 +22,27 @@ namespace bs
 	 *  @{
 	 */
 
+	/** Common shader variations. */
+	static ShaderVariation SVar_Static = ShaderVariation({
+		ShaderVariation::Param("SKINNED", false),
+		ShaderVariation::Param("MORPH", false),
+	});
+
+	static ShaderVariation SVar_Skinned = ShaderVariation({
+		ShaderVariation::Param("SKINNED", true),
+		ShaderVariation::Param("MORPH", false),
+	});
+
+	static ShaderVariation SVar_Morph = ShaderVariation({
+		ShaderVariation::Param("SKINNED", false),
+		ShaderVariation::Param("MORPH", true),
+	});
+
+	static ShaderVariation SVar_SkinnedMorph = ShaderVariation({
+		ShaderVariation::Param("SKINNED", true),
+		ShaderVariation::Param("MORPH", true),
+	});
+
 	/** Technique tags. */
 	static StringID RTag_Skinned = "Skinned";
 	static StringID RTag_Morph = "Morph";
@@ -364,4 +386,4 @@ namespace bs
 	};
 
 	/** @} */
-}}
+}}

+ 1 - 0
Source/BansheeSL/BsSLFXCompiler.cpp

@@ -1062,6 +1062,7 @@ namespace bs
 						parseVariations(metaData, variationOption->value.nodePtr);
 				}
 			}
+				break;
 			case OT_Identifier:
 				metaData.name = option->value.strValue;
 				break;

+ 3 - 0
Source/RenderBeast/BsRendererObject.h

@@ -63,6 +63,9 @@ namespace bs { namespace ct
 		/** Index of the technique in the material to render the element with. */
 		UINT32 techniqueIdx;
 
+		/** Shader variation that controls the type of vertex input that is provided. */
+		const ShaderVariation* vertexInputVariation = nullptr;
+
 		/** Binding indices representing where should the per-camera param block buffer be bound to. */
 		GpuParamBinding perCameraBindings[GPT_COUNT];
 

+ 3 - 0
Source/RenderBeast/BsRendererScene.cpp

@@ -260,10 +260,13 @@ namespace bs {	namespace ct
 
 				// Determine which technique to use
 				static StringID techniqueIDLookup[4] = { StringID::NONE, RTag_Skinned, RTag_Morph, RTag_SkinnedMorph };
+				static const ShaderVariation* variationLookup[4] = { &SVar_Static, &SVar_Skinned, &SVar_Morph, &SVar_SkinnedMorph };
 				static_assert((UINT32)RenderableAnimType::Count == 4, "RenderableAnimType is expected to have four sequential entries.");
 
 				UINT32 techniqueIdx = -1;
 				RenderableAnimType animType = renderable->getAnimType();
+				renElement.vertexInputVariation = variationLookup[(int)animType];
+
 				if (animType != RenderableAnimType::None)
 					techniqueIdx = renElement.material->findTechnique(techniqueIDLookup[(int)animType]);
 

+ 301 - 69
Source/RenderBeast/BsShadowRendering.cpp

@@ -10,6 +10,7 @@
 #include "Renderer/BsCamera.h"
 #include "Utility/BsBitwise.h"
 #include "RenderAPI/BsVertexDataDesc.h"
+#include "Renderer/BsRenderer.h"
 
 namespace bs { namespace ct
 {
@@ -32,6 +33,24 @@ namespace bs { namespace ct
 
 		RenderAPI::instance().setGpuParams(mParams);
 	}
+	
+	ShadowDepthNormalMat* ShadowDepthNormalMat::getVariation(bool skinned, bool morph)
+	{
+		if(skinned)
+		{
+			if(morph)
+				return get(getVariation<true, true>());
+
+			return get(getVariation<true, false>());
+		}
+		else
+		{
+			if(morph)
+				return get(getVariation<false, true>());
+
+			return get(getVariation<false, false>());
+		}
+	}
 
 	ShadowDepthDirectionalMat::ShadowDepthDirectionalMat()
 	{ }
@@ -49,6 +68,24 @@ namespace bs { namespace ct
 		mParams->setParamBlockBuffer("PerObject", perObjectParams);
 		RenderAPI::instance().setGpuParams(mParams);
 	}
+	
+	ShadowDepthDirectionalMat* ShadowDepthDirectionalMat::getVariation(bool skinned, bool morph)
+	{
+		if(skinned)
+		{
+			if(morph)
+				return get(getVariation<true, true>());
+
+			return get(getVariation<true, false>());
+		}
+		else
+		{
+			if(morph)
+				return get(getVariation<false, true>());
+
+			return get(getVariation<false, false>());
+		}
+	}
 
 	ShadowCubeMatricesDef gShadowCubeMatricesDef;
 	ShadowCubeMasksDef gShadowCubeMasksDef;
@@ -74,6 +111,24 @@ namespace bs { namespace ct
 
 		RenderAPI::instance().setGpuParams(mParams);
 	}
+	
+	ShadowDepthCubeMat* ShadowDepthCubeMat::getVariation(bool skinned, bool morph)
+	{
+		if(skinned)
+		{
+			if(morph)
+				return get(getVariation<true, true>());
+
+			return get(getVariation<true, false>());
+		}
+		else
+		{
+			if(morph)
+				return get(getVariation<false, true>());
+
+			return get(getVariation<false, false>());
+		}
+	}
 
 	ShadowProjectParamsDef gShadowProjectParamsDef;
 	ShadowProjectVertParamsDef gShadowProjectVertParamsDef;
@@ -358,6 +413,232 @@ namespace bs { namespace ct
 		return mTargets[cascadeIdx];
 	}
 
+	/** 
+	 * Provides a common for all types of shadow depth rendering to render the relevant objects into the depth map. Iterates
+	 * over all relevant objects in the scene, binds the relevant materials and renders the objects into the depth map.
+	 */
+	class ShadowRenderQueue
+	{
+	public:
+		struct Command
+		{
+			Command()
+			{ }
+
+			Command(BeastRenderableElement* element)
+				:element(element), isElement(true)
+			{ }
+
+			union
+			{
+				BeastRenderableElement* element;
+				RendererObject* renderable;
+			};
+
+
+			bool isElement : 1;
+			UINT32 mask : 6;
+		};
+
+		template<class Options>
+		static void execute(RendererScene& scene, const FrameInfo& frameInfo, const Options& opt)
+		{
+			static_assert((UINT32)RenderableAnimType::Count == 4, "RenderableAnimType is expected to have four sequential entries.");
+
+			const SceneInfo& sceneInfo = scene.getSceneInfo();
+
+			bs_frame_mark();
+			{
+				FrameVector<Command> commands[4];
+
+				// Make a list of relevant renderables and prepare them for rendering
+				for (UINT32 i = 0; i < sceneInfo.renderables.size(); i++)
+				{
+					const Sphere& bounds = sceneInfo.renderableCullInfos[i].bounds.getSphere();
+					if (!opt.intersects(bounds))
+						continue;
+
+					scene.prepareRenderable(i, frameInfo);
+
+					Command renderableCommand;
+					renderableCommand.mask = 0;
+
+					RendererObject* renderable = sceneInfo.renderables[i];
+					renderableCommand.isElement = false;
+					renderableCommand.renderable = renderable;
+
+					opt.prepare(renderableCommand, bounds);
+
+					bool renderableBound[4];
+					bs_zero_out(renderableBound);
+
+					for (auto& element : renderable->elements)
+					{
+						UINT32 arrayIdx = (int)element.animType;
+
+						if (!renderableBound[arrayIdx])
+						{
+							commands[arrayIdx].push_back(renderableCommand);
+							renderableBound[arrayIdx] = true;
+						}
+
+						commands[arrayIdx].push_back(Command(&element));
+					}
+				}
+
+				static const ShaderVariation* VAR_LOOKUP[4] = 
+				{ 
+					&SVar_Static, &SVar_Skinned, &SVar_Morph, &SVar_SkinnedMorph 
+				};
+
+				for (UINT32 i = 0; i < (UINT32)RenderableAnimType::Count; i++)
+				{
+					opt.bindMaterial(*VAR_LOOKUP[i]);
+
+					for (auto& command : commands[i])
+					{
+						if (command.isElement)
+						{
+							const BeastRenderableElement& element = *command.element;
+
+							if (element.morphVertexDeclaration == nullptr)
+								gRendererUtility().draw(element.mesh, element.subMesh);
+							else
+								gRendererUtility().drawMorph(element.mesh, element.subMesh, element.morphShapeBuffer,
+									element.morphVertexDeclaration);
+						}
+						else
+							opt.bindRenderable(command);
+					}
+				}
+			}
+			bs_frame_clear();
+		}
+	};
+
+	/** Specialization used for ShadowRenderQueue when rendering cube (omnidirectional) shadow maps. */
+	struct ShadowRenderQueueCubeOptions
+	{
+		ShadowRenderQueueCubeOptions(
+			const ConvexVolume (&frustums)[6], 
+			const ConvexVolume& boundingVolume, 
+			const SPtr<GpuParamBlockBuffer>& shadowParamsBuffer, 
+			const SPtr<GpuParamBlockBuffer>& shadowCubeMatricesBuffer,
+			const SPtr<GpuParamBlockBuffer>& shadowCubeMasksBuffer)
+			: frustums(frustums), boundingVolume(boundingVolume), shadowParamsBuffer(shadowParamsBuffer)
+			, shadowCubeMatricesBuffer(shadowCubeMatricesBuffer), shadowCubeMasksBuffer(shadowCubeMasksBuffer)
+		{ }
+
+		bool intersects(const Sphere& bounds) const
+		{
+			return boundingVolume.intersects(bounds);
+		}
+
+		void prepare(ShadowRenderQueue::Command& command, const Sphere& bounds) const
+		{
+			for (UINT32 j = 0; j < 6; j++)
+				command.mask |= (frustums[j].intersects(bounds) ? 1 : 0) << j;
+		}
+
+		void bindMaterial(const ShaderVariation& variation) const
+		{
+			material = ShadowDepthCubeMat::get(variation);
+			material->bind(shadowParamsBuffer, shadowCubeMatricesBuffer);
+		}
+
+		void bindRenderable(ShadowRenderQueue::Command& command) const
+		{
+			RendererObject* renderable = command.renderable;
+
+			for (UINT32 j = 0; j < 6; j++)
+				gShadowCubeMasksDef.gFaceMasks.set(shadowCubeMasksBuffer, (command.mask & (1 << j)), j);
+
+			material->setPerObjectBuffer(renderable->perObjectParamBuffer, shadowCubeMasksBuffer);
+		}
+		
+		const ConvexVolume (&frustums)[6];
+		const ConvexVolume& boundingVolume;
+		const SPtr<GpuParamBlockBuffer>& shadowParamsBuffer;
+		const SPtr<GpuParamBlockBuffer>& shadowCubeMatricesBuffer;
+		const SPtr<GpuParamBlockBuffer>& shadowCubeMasksBuffer;
+
+		mutable ShadowDepthCubeMat* material = nullptr;
+	};
+
+	/** Specialization used for ShadowRenderQueue when rendering spot light shadow maps. */
+	struct ShadowRenderQueueSpotOptions
+	{
+		ShadowRenderQueueSpotOptions(
+			const ConvexVolume& boundingVolume, 
+			const SPtr<GpuParamBlockBuffer>& shadowParamsBuffer)
+			: boundingVolume(boundingVolume), shadowParamsBuffer(shadowParamsBuffer)
+		{ }
+
+		bool intersects(const Sphere& bounds) const
+		{
+			return boundingVolume.intersects(bounds);
+		}
+
+		void prepare(ShadowRenderQueue::Command& command, const Sphere& bounds) const
+		{
+		}
+
+		void bindMaterial(const ShaderVariation& variation) const
+		{
+			material = ShadowDepthNormalMat::get(variation);
+			material->bind(shadowParamsBuffer);
+		}
+
+		void bindRenderable(ShadowRenderQueue::Command& command) const
+		{
+			RendererObject* renderable = command.renderable;
+
+			material->setPerObjectBuffer(renderable->perObjectParamBuffer);
+		}
+		
+		const ConvexVolume& boundingVolume;
+		const SPtr<GpuParamBlockBuffer>& shadowParamsBuffer;
+
+		mutable ShadowDepthNormalMat* material = nullptr;
+	};
+
+	/** Specialization used for ShadowRenderQueue when rendering directional light shadow maps. */
+	struct ShadowRenderQueueDirOptions
+	{
+		ShadowRenderQueueDirOptions(
+			const ConvexVolume& boundingVolume, 
+			const SPtr<GpuParamBlockBuffer>& shadowParamsBuffer)
+			: boundingVolume(boundingVolume), shadowParamsBuffer(shadowParamsBuffer)
+		{ }
+
+		bool intersects(const Sphere& bounds) const
+		{
+			return boundingVolume.intersects(bounds);
+		}
+
+		void prepare(ShadowRenderQueue::Command& command, const Sphere& bounds) const
+		{
+		}
+
+		void bindMaterial(const ShaderVariation& variation) const
+		{
+			material = ShadowDepthDirectionalMat::get(variation);
+			material->bind(shadowParamsBuffer);
+		}
+
+		void bindRenderable(ShadowRenderQueue::Command& command) const
+		{
+			RendererObject* renderable = command.renderable;
+
+			material->setPerObjectBuffer(renderable->perObjectParamBuffer);
+		}
+		
+		const ConvexVolume& boundingVolume;
+		const SPtr<GpuParamBlockBuffer>& shadowParamsBuffer;
+
+		mutable ShadowDepthDirectionalMat* material = nullptr;
+	};
+
 	const UINT32 ShadowRendering::MAX_ATLAS_SIZE = 8192;
 	const UINT32 ShadowRendering::MAX_UNUSED_FRAMES = 60;
 	const UINT32 ShadowRendering::MIN_SHADOW_MAP_SIZE = 32;
@@ -973,25 +1254,12 @@ namespace bs { namespace ct
 			ShadowDepthDirectionalMat* depthDirMat = ShadowDepthDirectionalMat::get();
 			depthDirMat->bind(shadowParamsBuffer);
 
-			for (UINT32 j = 0; j < sceneInfo.renderables.size(); j++)
-			{
-				if (!cascadeCullVolume.intersects(sceneInfo.renderableCullInfos[j].bounds.getSphere()))
-					continue;
-
-				scene.prepareRenderable(j, frameInfo);
-
-				RendererObject* renderable = sceneInfo.renderables[j];
-				depthDirMat->setPerObjectBuffer(renderable->perObjectParamBuffer);
-
-				for (auto& element : renderable->elements)
-				{
-					if (element.morphVertexDeclaration == nullptr)
-						gRendererUtility().draw(element.mesh, element.subMesh);
-					else
-						gRendererUtility().drawMorph(element.mesh, element.subMesh, element.morphShapeBuffer,
-							element.morphVertexDeclaration);
-				}
-			}
+			// Render all renderables into the shadow map
+			ShadowRenderQueueDirOptions spotOptions(
+				cascadeCullVolume,
+				shadowParamsBuffer);
+			
+			ShadowRenderQueue::execute(scene, frameInfo, spotOptions);
 
 			shadowMap.setShadowInfo(i, shadowInfo);
 		}
@@ -1068,9 +1336,6 @@ namespace bs { namespace ct
 		gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, mapInfo.shadowVPTransform);
 		gShadowParamsDef.gNDCZToDeviceZ.set(shadowParamsBuffer, RendererView::getNDCZToDeviceZ());
 
-		ShadowDepthNormalMat* depthNormalMat = ShadowDepthNormalMat::get();
-		depthNormalMat->bind(shadowParamsBuffer);
-
 		const Vector<Plane>& frustumPlanes = localFrustum.getPlanes();
 		Matrix4 worldMatrix = view.transpose();
 
@@ -1083,25 +1348,13 @@ namespace bs { namespace ct
 		}
 
 		ConvexVolume worldFrustum(worldPlanes);
-		for (UINT32 i = 0; i < sceneInfo.renderables.size(); i++)
-		{
-			if (!worldFrustum.intersects(sceneInfo.renderableCullInfos[i].bounds.getSphere()))
-				continue;
 
-			scene.prepareRenderable(i, frameInfo);
+		// Render all renderables into the shadow map
+		ShadowRenderQueueSpotOptions spotOptions(
+			worldFrustum,
+			shadowParamsBuffer);
 
-			RendererObject* renderable = sceneInfo.renderables[i];
-			depthNormalMat->setPerObjectBuffer(renderable->perObjectParamBuffer);
-
-			for (auto& element : renderable->elements)
-			{
-				if (element.morphVertexDeclaration == nullptr)
-					gRendererUtility().draw(element.mesh, element.subMesh);
-				else
-					gRendererUtility().drawMorph(element.mesh, element.subMesh, element.morphShapeBuffer,
-						element.morphVertexDeclaration);
-			}
-		}
+		ShadowRenderQueue::execute(scene, frameInfo, spotOptions);
 
 		// Restore viewport
 		rapi.setViewport(Rect2(0.0f, 0.0f, 1.0f, 1.0f));
@@ -1252,37 +1505,16 @@ namespace bs { namespace ct
 		rapi.setRenderTarget(cubemap.getTarget());
 		rapi.clearRenderTarget(FBT_DEPTH);
 
-		ShadowDepthCubeMat* depthCubeMat = ShadowDepthCubeMat::get();
-		depthCubeMat->bind(shadowParamsBuffer, shadowCubeMatricesBuffer);
-
-		// First cull against a global volume
+		// Render all renderables into the shadow map
 		ConvexVolume boundingVolume(boundingPlanes);
-		for (UINT32 i = 0; i < sceneInfo.renderables.size(); i++)
-		{
-			const Sphere& bounds = sceneInfo.renderableCullInfos[i].bounds.getSphere();
-			if (!boundingVolume.intersects(bounds))
-				continue;
-
-			scene.prepareRenderable(i, frameInfo);
-
-			for(UINT32 j = 0; j < 6; j++)
-			{
-				int mask = frustums[j].intersects(bounds) ? 1 : 0;
-				gShadowCubeMasksDef.gFaceMasks.set(shadowCubeMasksBuffer, mask, j);
-			}
-
-			RendererObject* renderable = sceneInfo.renderables[i];
-			depthCubeMat->setPerObjectBuffer(renderable->perObjectParamBuffer, shadowCubeMasksBuffer);
-
-			for (auto& element : renderable->elements)
-			{
-				if (element.morphVertexDeclaration == nullptr)
-					gRendererUtility().draw(element.mesh, element.subMesh);
-				else
-					gRendererUtility().drawMorph(element.mesh, element.subMesh, element.morphShapeBuffer,
-						element.morphVertexDeclaration);
-			}
-		}
+		ShadowRenderQueueCubeOptions cubeOptions(
+			frustums,
+			boundingVolume,
+			shadowParamsBuffer,
+			shadowCubeMatricesBuffer,
+			shadowCubeMasksBuffer);
+
+		ShadowRenderQueue::execute(scene, frameInfo, cubeOptions);
 
 		LightShadows& lightShadows = mRadialLightShadows[options.lightIdx];
 

+ 58 - 0
Source/RenderBeast/BsShadowRendering.h

@@ -40,6 +40,18 @@ namespace bs { namespace ct
 	{
 		RMAT_DEF("ShadowDepthNormal.bsl");
 
+		/** Helper method used for initializing variations of this material. */
+		template<bool skinned, bool morph>
+		static const ShaderVariation& getVariation()
+		{
+			static ShaderVariation variation = ShaderVariation({
+				ShaderVariation::Param("SKINNED", skinned),
+				ShaderVariation::Param("MORPH", morph)
+			});
+
+			return variation;
+		}
+
 	public:
 		ShadowDepthNormalMat();
 
@@ -48,6 +60,14 @@ namespace bs { namespace ct
 
 		/** Sets a new buffer that determines per-object properties. */
 		void setPerObjectBuffer(const SPtr<GpuParamBlockBuffer>& perObjectParams);
+
+		/** 
+		 * Returns the material variation matching the provided parameters. 
+		 * 
+		 * @param[in]	skinned		True if the shadow caster supports bone animation.
+		 * @param[in]	morph		True if the shadow caster supports morph shape animation.
+		 */
+		static ShadowDepthNormalMat* getVariation(bool skinned, bool morph);
 	};
 
 	/** Material used for rendering a single face of a shadow map, for a directional light. */
@@ -55,6 +75,17 @@ namespace bs { namespace ct
 	{
 		RMAT_DEF("ShadowDepthDirectional.bsl");
 
+		/** Helper method used for initializing variations of this material. */
+		template<bool skinned, bool morph>
+		static const ShaderVariation& getVariation()
+		{
+			static ShaderVariation variation = ShaderVariation({
+				ShaderVariation::Param("SKINNED", skinned),
+				ShaderVariation::Param("MORPH", morph)
+			});
+
+			return variation;
+		}
 	public:
 		ShadowDepthDirectionalMat();
 
@@ -63,6 +94,14 @@ namespace bs { namespace ct
 
 		/** Sets a new buffer that determines per-object properties. */
 		void setPerObjectBuffer(const SPtr<GpuParamBlockBuffer>& perObjectParams);
+
+		/** 
+		 * Returns the material variation matching the provided parameters. 
+		 * 
+		 * @param[in]	skinned		True if the shadow caster supports bone animation.
+		 * @param[in]	morph		True if the shadow caster supports morph shape animation.
+		 */
+		static ShadowDepthDirectionalMat* getVariation(bool skinned, bool morph);
 	};
 
 	BS_PARAM_BLOCK_BEGIN(ShadowCubeMatricesDef)
@@ -82,6 +121,17 @@ namespace bs { namespace ct
 	{
 		RMAT_DEF("ShadowDepthCube.bsl");
 
+		/** Helper method used for initializing variations of this material. */
+		template<bool skinned, bool morph>
+		static const ShaderVariation& getVariation()
+		{
+			static ShaderVariation variation = ShaderVariation({
+				ShaderVariation::Param("SKINNED", skinned),
+				ShaderVariation::Param("MORPH", morph)
+			});
+
+			return variation;
+		}
 	public:
 		ShadowDepthCubeMat();
 
@@ -91,6 +141,14 @@ namespace bs { namespace ct
 		/** Sets a new buffer that determines per-object properties. */
 		void setPerObjectBuffer(const SPtr<GpuParamBlockBuffer>& perObjectParams, 
 			const SPtr<GpuParamBlockBuffer>& shadowCubeMasks);
+
+		/** 
+		 * Returns the material variation matching the provided parameters. 
+		 * 
+		 * @param[in]	skinned		True if the shadow caster supports bone animation.
+		 * @param[in]	morph		True if the shadow caster supports morph shape animation.
+		 */
+		static ShadowDepthCubeMat* getVariation(bool skinned, bool morph);
 	};
 
 	BS_PARAM_BLOCK_BEGIN(ShadowProjectVertParamsDef)