2013-12-19 19:32:50 +01:00

107 lines
4.1 KiB
Plaintext

/////////////////////////////////////////////////////////////////////////////////
//
// shadowreceiverfp.cg
//
// Hamilton Chong
// (c) 2006
//
// This is an example fragment shader for shadow receiver objects.
//
/////////////////////////////////////////////////////////////////////////////////
sampler2D ShadowMap : TEXUNIT0;
// Define outputs from vertex shader.
struct Vertex
{
float4 position : POSITION; // fragment position in post projective space
float4 shadowCoord : TEXCOORD0; // fragment position in shadow map coordinates
float diffuse : TEXCOORD1; // diffuse shading value
};
struct Fragment
{
float4 color : COLOR0;
};
Fragment main(Vertex In,
uniform float uSTexWidth,
uniform float uSTexHeight)
{
Fragment Out;
// compute the shadow coordinates for texture lookup
// NOTE: texture_viewproj_matrix maps z into [0,1] range, not [-1,1], so
// have to make sure shadow caster stores depth values with same convention.
float4 scoord = In.shadowCoord / In.shadowCoord.w;
// -- Bilinear Filtering of Sample --------------------------------------------
// One could use scoord.xy to look up the shadow map for depth testing, but
// we'll be implementing a simple "percentage closest filtering" algorithm instead.
// This mimics the behavior of turning on bilinear filtering on NVIDIA hardware
// when also performing shadow comparisons. This causes bilinear filtering of
// depth tests. Note that this is NOT the same as bilinear filtering the depth
// values and then doing the depth comparison. The two operations are not
// commutative. PCF is explicitly about filtering the test values since
// testing filtered z values is often meaningless.
// Real percentage closest filtering should sample from the entire footprint
// on the shadow map, not just seek the closest four sample points. Such
// an improvement is for future work.
// NOTE: Assuming OpenGL convention for texture lookups with integers in centers.
// DX convention is to have integers mark sample corners
float2 tcoord;
tcoord.x = (scoord.x * uSTexWidth) - 0.5;
tcoord.y = (scoord.y * uSTexHeight) - 0.5;
float x0 = floor(tcoord.x);
float x1 = ceil(tcoord.x);
float fracx = frac(tcoord.x);
float y0 = floor(tcoord.y);
float y1 = ceil(tcoord.y);
float fracy = frac(tcoord.y);
// sample coordinates in [0,1]^2 domain
float2 t00, t01, t10, t11;
float invWidth = 1.0 / uSTexWidth;
float invHeight = 1.0 / uSTexHeight;
t00 = float2((x0+0.5) * invWidth, (y0+0.5) * invHeight);
t10 = float2((x1+0.5) * invWidth, (y0+0.5) * invHeight);
t01 = float2((x0+0.5) * invWidth, (y1+0.5) * invHeight);
t11 = float2((x1+0.5) * invWidth, (y1+0.5) * invHeight);
// grab the samples
float2 z00 = tex2D(ShadowMap, t00).xy;
float2 z01 = tex2D(ShadowMap, t01).xy;
float2 z10 = tex2D(ShadowMap, t10).xy;
float2 z11 = tex2D(ShadowMap, t11).xy;
// bilinear filter the sample data
float2 d0 = ((1.0 - fracx) * z00) + (fracx * z10);
float2 d1 = ((1.0 - fracx) * z01) + (fracx * z11);
float2 datum = ((1.0 - fracy) * d0) + (fracy * d1);
// -- Variance Shadow Mapping ---------------------------------------------------
float zVariance = datum.y - (datum.x * datum.x);
float zDeviation = scoord.z - datum.x;
zDeviation = (zDeviation < 0.0) ? 0.0 : zDeviation;
float visibility = zVariance / (zVariance + (zDeviation * zDeviation));
float ztest = (scoord.z < datum.x) ? 1.0:0.0; // filtering depth ok, because used only for small variance
visibility = (zVariance > 0.0) ? visibility : ztest; // if variance too small, we get garbage
//0.0000001
// determine that all geometry within pixel border of shadow map (and outside) is lit
float filterBorder = max(invWidth, invHeight);
visibility = (all(abs(scoord.xy-0.5)<=0.5-filterBorder)) ? visibility : 1.0;
// ------------------------------------------------------------------------------
visibility *= In.diffuse;
Out.color = float4(visibility, visibility, visibility, 0.0);
return Out;
}