//Projective Shadow Mapping by Michael Auerbach (c) 2008//

/*
This shader is a 2 stage shader that takes a pre-rendered depth map to
calculate the depth differences of each screen-space pixel at 2 different
perspectives. Although expensive at first, projective shadow mapping is
a far more natural and efficient way of rendering shadows in complex scenes.

This shader is also optimized to run in one pass and only requires 1 pre-
-rendering stage and no post-processing.

Future improvments would be supporting more than one render-target, matrix,
and light source per mesh. Additional optimizations must be included.

version 1.0

FULLY commented

preformance:

~60fps @ 3072 shader modules w/ 2048 visable polygons w/ alpha overlays @ 1024 pixels^2

*/


/////////////////////////////
//----Vars and Samplers----//
/////////////////////////////


//--Tweakables-------------//
static const float dark_term = 0.3;//Multiplication value for pixels in shadow
static const float light_term = 0.8;//Multiplication value for illuminated pixels
static const float edge_smooth = 0.995;//shadow vertex blending and real/map depth offset
//--higher = sharper shadow detection/z-errors
//--lower = smoother shadows around surfaces/shadow offset erors


//--Application fed data--//
const float4x4 matWorldViewProj; // World*view*projection matrix
const float4x4 matWorld; // World matrix
const float4x4 matMtl; // Precalculated texture projection matrix (screen adjusted matViewProj of light source)

//--Textures (app fed)----//
texture TargetMap;//render target of pre-rendered depth map
texture entSkin1;//entity skin1 (color map)
texture mtlSkin1;//projection texture

//--Samplers--------------//
sampler depth_map = sampler_state
{
Texture = <TargetMap>;//depth buffer
AddressU = 4;//use *border* value float4(0,0,0,0)
AddressV = 4;//use *border* value float4(0,0,0,0)
MinFilter = 3;//D3DTEXF_ANISOTROPIC filter. GAUSSIANQUAD(7) is also very nice but expensive
MipFilter = 3;//D3DTEXF_ANISOTROPIC filter. GAUSSIANQUAD(7) is also very nice but expensive
MagFilter = 3;//D3DTEXF_ANISOTROPIC filter. GAUSSIANQUAD(7) is also very nice but expensive
};
sampler projection_map = sampler_state
{
Texture = <mtlSkin1>;//projection texture
AddressU = 3;//clamp coords
AddressV = 3;//clamp coords
};
sampler base_map = sampler_state
{
Texture = <entSkin1>;//color map
AddressU = 1;//repeat coords
AddressV = 1;//repeat coords
};


/////////////////////////////
//--VS/PS data structures--//
/////////////////////////////


//App fed Vertex input structure:
struct Vertex_in
{
float4 Pos: POSITION;
float2 Tex: TEXCOORD0;
};

//Vertex shader output:
struct Vertex_out
{
float4 Pos: POSITION;
float2 Tex: TEXCOORD0;
float4 Depth: TEXCOORD2;//a float4 for proj. tex coords.
};


////////////////////////////////////
//--Shadow mapping vertex shader--//
////////////////////////////////////


Vertex_out ShadowMapping_VS (Vertex_in IN)
{
//Create a temp output struct
Vertex_out OUT;

//Transform the vertex from object space to clip space:
OUT.Pos = mul(IN.Pos, matWorldViewProj);

//Send tex coords to PS
OUT.Tex = IN.Tex;

//Calculate the projective texture coordinates
OUT.Depth = mul( mul(IN.Pos,matWorld), matMtl );

//return temp output struct
return OUT;
}

////////////////////////////////////
//--distance comparison function--//
////////////////////////////////////

//future improvments:
//-use a simple rate equation to interpolate 45 degree pixel coords:
//result would smooth diagonal lines and create a much more refined pixel search
//note: above must be done in regard of projection transformation

float fetch_shadow(float4 coord)
{
//return fDark if light Depth < view Depth for pixel
//(if less, an object obstructed the light view,creating a depth value that is in front of the view pixel)
return
tex2Dproj(depth_map,coord).r < (coord.z*edge_smooth)? dark_term : light_term;

}


///////////////////////////////////
//--Shadow mapping pixel shader--//
///////////////////////////////////


float4 ShadowMapping_PS (Vertex_out IN) : COLOR0
{
// Calculate the shadow term
float shadow_term = fetch_shadow(IN.Depth);

//result; set to diffuse value (texture)
float4 r0 = tex2D(base_map,IN.Tex);

//projection texture: rgb == color, a == gamma (support for hdr soon)
float4 projection = tex2Dproj(projection_map,IN.Depth).rgba;

//multiply result by projection color if in light (avoids using else for aliasing reasons)
if((shadow_term == light_term)&&(IN.Depth.z > 0))
{
r0.rgb *= projection.rgb;
}

//partial box blur
//quality is considerably lower than a 2 stage shadow shader
shadow_term += fetch_shadow(float4(IN.Depth.x * 1.001,IN.Depth.y * 1.001,IN.Depth.z,IN.Depth.w));
shadow_term += fetch_shadow(float4(IN.Depth.x * 0.999,IN.Depth.y * 1.001,IN.Depth.z,IN.Depth.w));
shadow_term += fetch_shadow(float4(IN.Depth.x * 1.001,IN.Depth.y * 0.999,IN.Depth.z,IN.Depth.w));
shadow_term += fetch_shadow(float4(IN.Depth.x * 0.999,IN.Depth.y * 0.999,IN.Depth.z,IN.Depth.w));
shadow_term += fetch_shadow(float4(IN.Depth.x * 1.002,IN.Depth.y,IN.Depth.z,IN.Depth.w));
shadow_term += fetch_shadow(float4(IN.Depth.x * 0.998,IN.Depth.y,IN.Depth.z,IN.Depth.w));

//average sumed color values
shadow_term /= 7;

//multiply shadow term by projection gamma
shadow_term *= projection.a;

//if depth is behind light, set shadow term to fDark (v1.0,set to 0)
if((IN.Depth.z < 0))
{
shadow_term = 0;
}

//multiply result by shadow term
r0.rgb *= shadow_term;

//return ((tex*shadow*gamma*proj_color),tex alpha value)
return r0;
}


/////////////////
//--Technique--//
/////////////////


technique techShadow
{
pass p0
{
ZWriteEnable = True;//allow z-writing for overlay shadows
AlphaTestEnable = True;//test alpha for overlay value - reomves black edges of overlays
AlphaFunc = Greater;//must be greater to replace overlay/alpha
AlphaRef = 97;//give some room for error in overlay textures
//compile both shaders
VertexShader = compile vs_2_0 ShadowMapping_VS();
PixelShader = compile ps_2_0 ShadowMapping_PS();
}
}


http://www.groundtacticsgame.com/
Alienware m17x R Custom laptop
specs:
Intel Core 2 Extreme Quad CPU Q9300
2x Nvidia 280GTX 2GB vram
6GB ddr3 memory@ 1333Mhz
512GB SSD
1200p 17' screen
runs Crysis Warhead on max settings at 1200p at 90 fps