/*********************************************************************NVMH4**** File: $Id: //sw/devtools/SDK/9.5/SDK/MEDIA/HLSL/softStencilShadow.fx#3 $ Copyright NVIDIA Corporation 2004-2005 TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. Comments: Stencil Volume Shadowing Uses the vertex shader to do the projection of the shadow volume. See splitModelEdges.pl for required geometric pre-processing of .obj files. Write shadow to a texture, then blur it in screen space. DON'T blur shadows that are very far from the current pixel in depth. Mult lit and shadow texels to get final result. ******************************************************************************/ #include float Script : STANDARDSGLOBAL < string UIWidget = "none"; string ScriptClass = "scene"; string ScriptOrder = "standard"; string ScriptOutput = "color"; string Script = "Technique=main;"; > = 0.8; float4 ClearColor < string UIWidget = "none"; // "color"; string UIName = "background"; > = {0,0,0,0.0}; float4 White < string UIWidget = "none"; > = {1,1,1,0.0}; float ClearDepth = 1.0; DECLARE_QUAD_TEX(WhiteMap,WhiteSampler,"A8B8G8R8") DECLARE_QUAD_TEX(SceneMap,SceneSampler,"A8B8G8R8") DECLARE_QUAD_TEX(BlurMap,BlurSampler,"A8B8G8R8") DECLARE_QUAD_TEX(GrayDepthMap,GrayDepthSampler,"A8B8G8R8") DECLARE_QUAD_DEPTH_BUFFER(DepthMap,"D24S8") /********* tweakables ********************/ float4x4 WorldViewProj : WorldViewProjection < string UIWidget="None"; >; float3 SurfColor < string UIWidget = "color"; string UIName = "Surface"; > = {1,.9,.8}; float4 AmbiColor < string UIWidget = "color"; string UIName = "Ambient"; > = {.1,.1,.2,0.0}; float4 LightPos : Position < string Object = "PointLight"; string Space = "World"; > = {100.0f, 100.0f, 100.0f, 0.0f}; float GeomInset < string UIWidget = "slider"; float UIMin = 0; float UIMax = 0.02; float UIStep = 0.001; string UIName = "Geometric Inset"; > = 0.003; float ShadowExtrudeDist < string UIName = "Shadow Volume Extrusion Distance"; string UIWidget = "slider"; float UIMin = 1.0; float UIMax = 100.0; float UIStep = 0.1; > = 100.0f; float Dens < string UIName = "Shadow Density"; string UIWidget = "slider"; float UIMin = 0.0; float UIMax = 1.0; float UIStep = 0.001; > = 0.5f; float DThresh < string UIName = "Depth Threshhold"; string UIWidget = "slider"; float UIMin = 0.0; float UIMax = 0.3; float UIStep = 0.001; > = 0.2f; float Near < string UIName = "Depth Near"; string UIWidget = "slider"; float UIMin = 1.0; float UIMax = 10.0; float UIStep = 0.1; > = 0.2f; float Far < string UIName = "Depth Far"; string UIWidget = "slider"; float UIMin = 2.0; float UIMax = 20.0; float UIStep = 0.1; > = 20.0f; float Blur < string UIName = "Blur Width (in Pixels)"; string UIWidget = "slider"; float UIMin = 1.0; float UIMax = 4.0; float UIStep = 0.1; > = 1.5f; /************ structs and connections *****************************/ struct appdata { float4 Position : POSITION; float2 UV : TEXCOORD0; float3 Normal : NORMAL; }; /* data passed from vertex shader to pixel shader */ struct SimpleVertexColorOutput { float4 HPosition : POSITION; float4 diffCol : COLOR0; }; /*********** vertex shader ******/ SimpleVertexColorOutput extrudeVS( appdata IN) { SimpleVertexColorOutput OUT; // Create normalized vector from vertex to light float4 Lvec = normalize( IN.Position - LightPos ); // N dot L to decide if point should be moved away // from the light to extrude the volume float ldn = dot( -Lvec.xyz, IN.Normal.xyz ); ////////////////////////////////////////////////////////// // Inset the position along the normal vector direction // This moves the shadow volume points inside the model // slightly to minimize poping of shadowed areas as // each facet comes in and out of shadow. float4 inset_pos = float4(((IN.Position.xyz - (IN.Normal * GeomInset)).xyz),IN.Position.w); // scale the vector from light to vertex float4 extrusion_vec = Lvec * ShadowExtrudeDist; // if ldn < 0 then the vertex faces away from the light, so // move it. It will be moved along the direction from // light to vertex to extrude the shadow volume. // Consts_0512 = { 0.0f, 0.5f, 1.0f, 2.0f }; // So this does toggle = N dot L < 0 ? 1.0 : 0.0 float toggle = (float) (ldn < 0.0); // Move the back-facing shadow volume points float4 new_position = extrusion_vec*toggle + inset_pos; OUT.HPosition = mul( new_position, WorldViewProj); //////////////////////////////////////////////////////// OUT.diffCol = float4(IN.UV.xy,0,1); // OUT.TexCoord0 = IN.TexCoord0.xyyy; return( OUT ); } SimpleVertexColorOutput backingVS(appdata IN) { SimpleVertexColorOutput OUT; OUT.HPosition = mul(IN.Position, WorldViewProj); OUT.diffCol = AmbiColor; return( OUT ); } SimpleVertexColorOutput simpleVS( appdata IN) { SimpleVertexColorOutput OUT; float4 Lvec = normalize( IN.Position - LightPos ); float ldn = abs(dot(-Lvec.xyz, IN.Normal.xyz )); OUT.HPosition = mul( IN.Position, WorldViewProj ); OUT.diffCol = AmbiColor + float4((ldn*SurfColor),1); return( OUT ); } SimpleVertexColorOutput whiteVS( appdata IN) { SimpleVertexColorOutput OUT; float4 Lvec = normalize( IN.Position - LightPos ); float ldn = abs(dot(-Lvec.xyz, IN.Normal.xyz )); OUT.HPosition = mul( IN.Position, WorldViewProj ); OUT.diffCol = float4(1,1,1,1); return( OUT ); } SimpleVertexColorOutput deepGrayVS( appdata IN) { SimpleVertexColorOutput OUT; float4 Lvec = normalize( IN.Position - LightPos ); float ldn = abs(dot(-Lvec.xyz, IN.Normal.xyz )); float4 Ph = mul( IN.Position, WorldViewProj ); OUT.HPosition = Ph; float d = (Ph.z-Near)/(Far-Near); d = 1.0 - (DThresh + (1.0-DThresh)*abs(d)); OUT.diffCol = d.xxxx; return(OUT); } /// Blur //////////////////////////////////////// // // This is a very simple blur, four extra samples, one in each cardinal direction // struct BlurVertexOutput { QUAD_REAL4 Position : POSITION; QUAD_REAL2 NU : TEXCOORD0; QUAD_REAL2 CC : TEXCOORD1; QUAD_REAL2 PU : TEXCOORD2; QUAD_REAL2 NV : TEXCOORD3; QUAD_REAL2 PV : TEXCOORD4; }; BlurVertexOutput DepthBlurVS( QUAD_REAL3 Position : POSITION, QUAD_REAL3 TexCoord : TEXCOORD0 ) { BlurVertexOutput OUT=(BlurVertexOutput)0; QUAD_REAL2 center; OUT.Position = QUAD_REAL4(Position, 1); QUAD_REAL2 texelSize = QUAD_REAL2((1.0/QuadScreenSize.x),(1.0/QuadScreenSize.y)); QUAD_REAL2 blurSize = Blur * texelSize; // the NO_TEXEL_OFFSET macro is part of "Quad.fxh" #ifdef NO_TEXEL_OFFSET center = TexCoord.xy; #else /* NO_TEXEL_OFFSET */ QUAD_REAL2 off = QuadTexOffset * texelSize; QUAD_REAL2(QuadTexOffset/(QuadScreenSize.x),QuadTexOffset/(QuadScreenSize.y)); center = QUAD_REAL2(TexCoord.xy+off); #endif /* NO_TEXEL_OFFSET */ OUT.CC = center; OUT.NU = QUAD_REAL2(center.x-blurSize.x,center.y); OUT.PU = QUAD_REAL2(center.x+blurSize.x,center.y); OUT.NV = QUAD_REAL2(center.x,center.y-blurSize.y); OUT.PV = QUAD_REAL2(center.x,center.y+blurSize.y); return OUT; } // blur, but only if the beighboring pixels have about the same depth // (and thus are part of the same geometric entity) -- otherwise leave the // edge sharp QUAD_REAL4 DepthBlurPS(BlurVertexOutput IN) : COLOR { QUAD_REAL dcc = tex2D(GrayDepthSampler, IN.CC).x; QUAD_REAL scc = tex2D(WhiteSampler, IN.CC).x; QUAD_REAL dnu = tex2D(GrayDepthSampler, IN.NU).x; QUAD_REAL snu = tex2D(WhiteSampler, IN.NU).x; QUAD_REAL dpu = tex2D(GrayDepthSampler, IN.PU).x; QUAD_REAL spu = tex2D(WhiteSampler, IN.PU).x; QUAD_REAL dnv = tex2D(GrayDepthSampler, IN.NV).x; QUAD_REAL snv = tex2D(WhiteSampler, IN.NV).x; QUAD_REAL dpv = tex2D(GrayDepthSampler, IN.PV).x; QUAD_REAL spv = tex2D(WhiteSampler, IN.PV).x; float ac = 1.0; float as = scc; if ((dnu!=0)&&(abs(dnu-dcc)) < DThresh) { as += snu; ac += 1.0; } if ((dpu!=0)&&(abs(dpu-dcc)) < DThresh) { as += spu; ac += 1.0; } if ((dnv!=0)&&(abs(dnv-dcc)) < DThresh) { as += snv; ac += 1.0; } if ((dpv!=0)&&(abs(dpv-dcc)) < DThresh) { as += spv; ac += 1.0; } as = as / ac; as = 1.0 - (Dens*(1.0-as)); return float4(as.xxxx); } ///// final-output pixel mixer ////// QUAD_REAL4 shadowMixPS(QuadVertexOutput IN) : COLOR { QUAD_REAL4 scene = tex2D(SceneSampler, IN.UV); QUAD_REAL4 shad = tex2D(BlurSampler, IN.UV); return (shad*scene); } /*************** techniques *******************/ technique main < string Script = "Pass=DepthToGrayMap;" "Pass=ColorSceneToMap;" "Pass=StencilZPass;" "Pass=StencilBackPass;" "Pass=StencilFrontPass;" "Pass=DrawStencilShadowOnWhite;" "Pass=ScreenSpaceShadowBlur;" "Pass=FinalComposite;"; > { pass DepthToGrayMap < string Script = "RenderColorTarget0=GrayDepthMap;" "RenderDepthStencilTarget=DepthMap;" "ClearSetColor=ClearColor;" "ClearSetDepth=ClearDepth;" "Clear=Color;" "Clear=Depth;" "Draw=geometry;"; > { VertexShader = compile vs_1_0 deepGrayVS(); ZEnable = true; ZWriteEnable = true; CullMode = None; StencilEnable = false; } pass ColorSceneToMap < string Script = "RenderColorTarget0=SceneMap;" "RenderDepthStencilTarget=DepthMap;" "ClearSetColor=ClearColor;" "ClearSetDepth=ClearDepth;" "Clear=Color;" "Clear=Depth;" "Draw=geometry;"; > { VertexShader = compile vs_1_0 simpleVS(); ZEnable = true; ZWriteEnable = true; CullMode = None; StencilEnable = false; } pass StencilZPass < string Script = "RenderColorTarget0=WhiteMap;" "RenderDepthStencilTarget=DepthMap;" "ClearSetColor=ClearColor;" "ClearSetDepth=ClearDepth;" "Clear=Color;" "Clear=Stencil;" "Clear=Depth;" // no clear "Draw=geometry;"; > { VertexShader = compile vs_1_0 backingVS(); ZEnable = true; ZWriteEnable = true; CullMode = None; StencilEnable = false; } pass StencilBackPass < string Script = "RenderColorTarget0=WhiteMap;" "RenderDepthStencilTarget=DepthMap;" // no clear "Draw=geometry;"; > { VertexShader = compile vs_1_1 extrudeVS(); ZEnable = true; ZWriteEnable = true; ZFunc = lessequal; CullMode = CW; StencilEnable = True; StencilPass = Keep; StencilFail = Keep; StencilZFail = IncrSat; StencilFunc = Always; ColorWriteEnable = 0; } pass StencilFrontPass < string Script = "RenderColorTarget0=WhiteMap;" "RenderDepthStencilTarget=DepthMap;" // no clear "Draw=geometry;"; > { VertexShader = compile vs_1_1 extrudeVS(); ZEnable = true; ZWriteEnable = true; ZFunc = lessequal; CullMode = CCW; // TwoSidedStencilMode = false; // needed? StencilEnable = True; StencilPass = Keep; StencilFail = Keep; StencilZFail = DecrSat; StencilFunc = Always; ColorWriteEnable = 0; } pass DrawStencilShadowOnWhite < string Script = "RenderColorTarget0=WhiteMap;" "RenderDepthStencilTarget=DepthMap;" "ClearSetColor=ClearColor;" "Clear=Color;" "Draw=geometry;"; > { VertexShader = compile vs_1_1 whiteVS(); ZEnable = true; ZWriteEnable = true; ZFunc = lessequal; CullMode = None; StencilEnable = True; StencilPass = Keep; StencilZFail = Keep; StencilFail = Keep; StencilRef = 0; StencilFunc = Equal; } pass ScreenSpaceShadowBlur < string Script = "RenderColorTarget0=BlurMap;" "RenderDepthStencilTarget=DepthMap;" "ClearSetColor=ClearColor;" "ClearSetDepth=ClearDepth;" "Clear=Color;" "Clear=Depth;" "Draw=buffer;"; > { VertexShader = compile vs_1_1 DepthBlurVS(); ZEnable = false; CullMode = None; StencilEnable = false; AlphaBlendEnable = false; PixelShader = compile ps_2_a DepthBlurPS(); } pass FinalComposite < string Script = "RenderColorTarget0=;" "RenderDepthStencilTarget=;" "ClearSetColor=ClearColor;" "ClearSetDepth=ClearDepth;" "Clear=Color;" "Clear=Depth;" "Draw=buffer;"; > { VertexShader = compile vs_1_1 ScreenQuadVS(); ZEnable = false; // ZWriteEnable = true; CullMode = None; StencilEnable = false; AlphaBlendEnable = false; PixelShader = compile ps_2_a shadowMixPS(); } } /********************************** eof ***/