#define NOVERTEX 0xfffffffe struct VSSceneIn { float4 Pos : POSITION; float3 Norm : NORMAL; float2 Tex : TEXCOORD; }; struct PSSceneIn { float4 Pos : SV_Position; float4 Color : COLOR0; }; struct GSShadowIn { float3 Pos : POS; float3 Norm : NORMAL; }; struct PSShadowIn { float4 Pos : SV_Position; }; matrix WorldMatrix : WorldMatrix; matrix ViewMatrix : ViewMatrix; matrix ProjectionMatrix : ProjectionMatrix; float3 LightPos : LightPosition; float ExtrudeAmt = 10.0f; float ExtrudeBias = 0.0f; float4 ShadowColor = float4(0.0f, 0.0f, 0.0f, 1.0f); DepthStencilState DisableDepth { DepthEnable = FALSE; DepthWriteMask = ZERO; }; DepthStencilState EnableDepth { DepthEnable = TRUE; DepthWriteMask = ALL; }; DepthStencilState TwoSidedStencil { DepthEnable = true; DepthWriteMask = ZERO; DepthFunc = Less; // Setup stencil states StencilEnable = true; StencilReadMask = 0xFFFFFFFF; StencilWriteMask = 0xFFFFFFFF; BackFaceStencilFunc = Always; BackFaceStencilDepthFail = Incr; BackFaceStencilPass = Keep; BackFaceStencilFail = Keep; FrontFaceStencilFunc = Always; FrontFaceStencilDepthFail = Decr; FrontFaceStencilPass = Keep; FrontFaceStencilFail = Keep; }; DepthStencilState RenderNonShadows { DepthEnable = true; DepthWriteMask = ZERO; DepthFunc = Less_Equal; StencilEnable = true; StencilReadMask = 0xFFFFFFFF; StencilWriteMask = 0x0; FrontFaceStencilFunc = NOT_EQUAL; FrontFaceStencilPass = Keep; FrontFaceStencilFail = ZERO; BackFaceStencilFunc = Never; BackFaceStencilPass = ZERO; BackFaceStencilFail = ZERO; }; BlendState DisableFrameBuffer { BlendEnable[0] = FALSE; RenderTargetWriteMask[0] = 0x0; }; BlendState EnableFrameBuffer { BlendEnable[0] = FALSE; RenderTargetWriteMask[0] = 0x0F; }; BlendState SrcAlphaBlending { AlphaToCoverageEnable = FALSE; BlendEnable[0] = TRUE; SrcBlend = SRC_ALPHA; DestBlend = INV_SRC_ALPHA; BlendOp = ADD; SrcBlendAlpha = ZERO; DestBlendAlpha = ZERO; BlendOpAlpha = ADD; RenderTargetWriteMask[0] = 0x0F; }; BlendState AdditiveBlending { AlphaToCoverageEnable = FALSE; BlendEnable[0] = TRUE; SrcBlend = ONE; DestBlend = ONE; BlendOp = SUBTRACT; SrcBlendAlpha = ZERO; DestBlendAlpha = ZERO; BlendOpAlpha = ADD; RenderTargetWriteMask[0] = 0x0F; }; RasterizerState DisableCulling { CullMode = NONE; }; RasterizerState EnableCulling { CullMode = BACK; }; // // VS for sending information to the shadow GS // GSShadowIn VSShadowmain( VSSceneIn input ) { GSShadowIn output = (GSShadowIn)0.0; //output our position in world space float4 pos = mul( float4(input.Pos.xyz,1), WorldMatrix ); output.Pos = pos.xyz; //world space normal output.Norm = mul( input.Norm, (float3x3)WorldMatrix ); return output; } PSSceneIn VSScenemain( VSSceneIn input ) { PSSceneIn output = (PSSceneIn)0.0; //output our final position in clipspace output.Pos = mul(input.Pos, WorldMatrix); output.Pos = mul(output.Pos, ViewMatrix); output.Pos = mul(output.Pos, ProjectionMatrix); //world space normal float3 norm = mul( input.Norm, (float3x3)WorldMatrix ); //find the light dir float3 wpos = mul( input.Pos, (float3x3)WorldMatrix ); float3 lightDir = normalize( LightPos - wpos ); float lightLenSq = length(LightPos - wpos); output.Color = saturate(dot(lightDir,norm)) * ShadowColor * 8.0f/lightLenSq; return output; } // PS for rendering lit and textured triangles float4 PSScenemain(PSSceneIn input) : SV_Target { return input.Color; } // Helper to detect a silhouette edge and extrude a volume from it void DetectAndProcessSilhouette(float3 N, // Un-normalized triangle normal GSShadowIn v1, // Shared vertex GSShadowIn v2, // Shared vertex GSShadowIn vAdj, // Adjacent triangle vertex inout TriangleStream ShadowTriangleStream // triangle stream ) { float3 NAdj = cross(v2.Pos - vAdj.Pos, v1.Pos - vAdj.Pos); float fDot = dot(normalize(N), normalize(NAdj)); if (fDot < 0.0) { float3 outpos[4]; float3 extrude1 = normalize(v1.Pos - LightPos); float3 extrude2 = normalize(v2.Pos - LightPos); outpos[0] = v1.Pos + ExtrudeBias * extrude1; outpos[1] = v1.Pos + ExtrudeAmt * extrude1; outpos[2] = v2.Pos + ExtrudeBias * extrude2; outpos[3] = v2.Pos + ExtrudeAmt * extrude2; // Extrude silhouette to create two new triangles PSShadowIn Out; for(int v = 0; v < 4; v++) { Out.Pos = mul(float4(outpos[v], 1.0f), ViewMatrix); Out.Pos = mul(Out.Pos, ProjectionMatrix); ShadowTriangleStream.Append(Out); } ShadowTriangleStream.RestartStrip(); } } // GS for generating shadow volumes [maxvertexcount(18)] void GSShadowmain(triangleadj GSShadowIn In[6], inout TriangleStream ShadowTriangleStream) { // Compute un-normalized triangle normal float3 N = normalize(cross(In[2].Pos - In[0].Pos, In[4].Pos - In[0].Pos)); // Compute direction from this triangle to the light float3 lightDir[3]; lightDir[0] = normalize(LightPos - In[0].Pos); lightDir[1] = normalize(LightPos - In[2].Pos); lightDir[2] = normalize(LightPos - In[4].Pos); //if we're facing the light if(dot(N, lightDir[0]) > 0.0f || dot(N, lightDir[1]) > 0.0f || dot(N, lightDir[2]) > 0.0f) { // For each edge of the triangle, determine if it is a silhouette edge DetectAndProcessSilhouette(lightDir[0], In[0], In[2], In[1], ShadowTriangleStream); DetectAndProcessSilhouette(lightDir[1], In[2], In[4], In[3], ShadowTriangleStream); DetectAndProcessSilhouette(lightDir[2], In[4], In[0], In[5], ShadowTriangleStream); //near cap PSShadowIn Out; for(int v = 0; v < 6; v += 2) { float3 extrude = normalize(In[v].Pos - LightPos); float3 Pos = In[v].Pos + ExtrudeBias * extrude; Out.Pos = mul(float4(Pos, 1.0f), ViewMatrix); Out.Pos = mul(Out.Pos, ProjectionMatrix); ShadowTriangleStream.Append(Out); } ShadowTriangleStream.RestartStrip(); //far cap (reverse the order) for(int v2 = 4; v2 >= 0; v2 -= 2) { float3 extrude = normalize(In[v2].Pos - LightPos); float3 Pos = In[v2].Pos + ExtrudeAmt * extrude; Out.Pos = mul(float4(Pos, 1.0f), ViewMatrix); Out.Pos = mul(Out.Pos, ProjectionMatrix); ShadowTriangleStream.Append( Out ); } ShadowTriangleStream.RestartStrip(); } } // PS for rendering shadow scene float4 PSShadowmain(PSShadowIn input) : SV_Target { return float4(0.3,0,0,0.25); } // RenderShadow - extrudes shadows from geometry technique10 CastShadows { pass p0 { SetVertexShader( CompileShader( vs_4_0, VSShadowmain() ) ); SetGeometryShader( CompileShader( gs_4_0, GSShadowmain() ) ); SetPixelShader( CompileShader( ps_4_0, PSShadowmain() ) ); SetBlendState( DisableFrameBuffer, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetDepthStencilState( TwoSidedStencil, 1 ); //state, stencilref SetRasterizerState( DisableCulling ); } } technique10 ShowShadowVolume { pass p0 { SetVertexShader( CompileShader( vs_4_0, VSShadowmain() ) ); SetGeometryShader( CompileShader( gs_4_0, GSShadowmain() ) ); SetPixelShader( CompileShader( ps_4_0, PSShadowmain() ) ); SetBlendState( SrcAlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetDepthStencilState( TwoSidedStencil, 1 ); //state, stencilref SetRasterizerState( DisableCulling ); } } technique10 ReceiveShadows { pass p0 { SetVertexShader( CompileShader( vs_4_0, VSScenemain() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, PSScenemain() ) ); SetBlendState( SrcAlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetDepthStencilState( RenderNonShadows, 0 ); //state, stencilref SetRasterizerState( EnableCulling ); } }