[12115] | 1 | ///////////////////////////////////////////////////////////////////////////////// |
---|
| 2 | // |
---|
| 3 | // shadowreceiverfp.cg |
---|
| 4 | // |
---|
| 5 | // Hamilton Chong |
---|
| 6 | // (c) 2006 |
---|
| 7 | // |
---|
| 8 | // This is an example fragment shader for shadow receiver objects. |
---|
| 9 | // |
---|
| 10 | ///////////////////////////////////////////////////////////////////////////////// |
---|
| 11 | |
---|
| 12 | |
---|
| 13 | sampler2D ShadowMap : TEXUNIT0; |
---|
| 14 | |
---|
| 15 | // Define outputs from vertex shader. |
---|
| 16 | struct Vertex |
---|
| 17 | { |
---|
| 18 | float4 position : POSITION; // fragment position in post projective space |
---|
| 19 | float4 shadowCoord : TEXCOORD0; // fragment position in shadow map coordinates |
---|
| 20 | float diffuse : TEXCOORD1; // diffuse shading value |
---|
| 21 | }; |
---|
| 22 | |
---|
| 23 | struct Fragment |
---|
| 24 | { |
---|
| 25 | float4 color : COLOR0; |
---|
| 26 | }; |
---|
| 27 | |
---|
| 28 | Fragment main(Vertex In, |
---|
| 29 | uniform float uSTexWidth, |
---|
| 30 | uniform float uSTexHeight) |
---|
| 31 | { |
---|
| 32 | Fragment Out; |
---|
| 33 | |
---|
| 34 | // compute the shadow coordinates for texture lookup |
---|
| 35 | // NOTE: texture_viewproj_matrix maps z into [0,1] range, not [-1,1], so |
---|
| 36 | // have to make sure shadow caster stores depth values with same convention. |
---|
| 37 | float4 scoord = In.shadowCoord / In.shadowCoord.w; |
---|
| 38 | |
---|
| 39 | |
---|
| 40 | // -- Bilinear Filtering of Sample -------------------------------------------- |
---|
| 41 | |
---|
| 42 | // One could use scoord.xy to look up the shadow map for depth testing, but |
---|
| 43 | // we'll be implementing a simple "percentage closest filtering" algorithm instead. |
---|
| 44 | // This mimics the behavior of turning on bilinear filtering on NVIDIA hardware |
---|
| 45 | // when also performing shadow comparisons. This causes bilinear filtering of |
---|
| 46 | // depth tests. Note that this is NOT the same as bilinear filtering the depth |
---|
| 47 | // values and then doing the depth comparison. The two operations are not |
---|
| 48 | // commutative. PCF is explicitly about filtering the test values since |
---|
| 49 | // testing filtered z values is often meaningless. |
---|
| 50 | |
---|
| 51 | // Real percentage closest filtering should sample from the entire footprint |
---|
| 52 | // on the shadow map, not just seek the closest four sample points. Such |
---|
| 53 | // an improvement is for future work. |
---|
| 54 | |
---|
| 55 | |
---|
| 56 | // NOTE: Assuming OpenGL convention for texture lookups with integers in centers. |
---|
| 57 | // DX convention is to have integers mark sample corners |
---|
| 58 | float2 tcoord; |
---|
| 59 | tcoord.x = (scoord.x * uSTexWidth) - 0.5; |
---|
| 60 | tcoord.y = (scoord.y * uSTexHeight) - 0.5; |
---|
| 61 | float x0 = floor(tcoord.x); |
---|
| 62 | float x1 = ceil(tcoord.x); |
---|
| 63 | float fracx = frac(tcoord.x); |
---|
| 64 | float y0 = floor(tcoord.y); |
---|
| 65 | float y1 = ceil(tcoord.y); |
---|
| 66 | float fracy = frac(tcoord.y); |
---|
| 67 | |
---|
| 68 | // sample coordinates in [0,1]^2 domain |
---|
| 69 | float2 t00, t01, t10, t11; |
---|
| 70 | float invWidth = 1.0 / uSTexWidth; |
---|
| 71 | float invHeight = 1.0 / uSTexHeight; |
---|
| 72 | t00 = float2((x0+0.5) * invWidth, (y0+0.5) * invHeight); |
---|
| 73 | t10 = float2((x1+0.5) * invWidth, (y0+0.5) * invHeight); |
---|
| 74 | t01 = float2((x0+0.5) * invWidth, (y1+0.5) * invHeight); |
---|
| 75 | t11 = float2((x1+0.5) * invWidth, (y1+0.5) * invHeight); |
---|
| 76 | |
---|
| 77 | // grab the samples |
---|
| 78 | float2 z00 = tex2D(ShadowMap, t00).xy; |
---|
| 79 | float2 z01 = tex2D(ShadowMap, t01).xy; |
---|
| 80 | float2 z10 = tex2D(ShadowMap, t10).xy; |
---|
| 81 | float2 z11 = tex2D(ShadowMap, t11).xy; |
---|
| 82 | |
---|
| 83 | // bilinear filter the sample data |
---|
| 84 | float2 d0 = ((1.0 - fracx) * z00) + (fracx * z10); |
---|
| 85 | float2 d1 = ((1.0 - fracx) * z01) + (fracx * z11); |
---|
| 86 | float2 datum = ((1.0 - fracy) * d0) + (fracy * d1); |
---|
| 87 | |
---|
| 88 | // -- Variance Shadow Mapping --------------------------------------------------- |
---|
| 89 | |
---|
| 90 | float zVariance = datum.y - (datum.x * datum.x); |
---|
| 91 | float zDeviation = scoord.z - datum.x; |
---|
| 92 | zDeviation = (zDeviation < 0.0) ? 0.0 : zDeviation; |
---|
| 93 | float visibility = zVariance / (zVariance + (zDeviation * zDeviation)); |
---|
| 94 | float ztest = (scoord.z < datum.x) ? 1.0:0.0; // filtering depth ok, because used only for small variance |
---|
| 95 | visibility = (zVariance > 0.0) ? visibility : ztest; // if variance too small, we get garbage |
---|
| 96 | //0.0000001 |
---|
| 97 | |
---|
| 98 | // determine that all geometry within pixel border of shadow map (and outside) is lit |
---|
| 99 | float filterBorder = max(invWidth, invHeight); |
---|
| 100 | visibility = (all(abs(scoord.xy-0.5)<=0.5-filterBorder)) ? visibility : 1.0; |
---|
| 101 | |
---|
| 102 | // ------------------------------------------------------------------------------ |
---|
| 103 | |
---|
| 104 | visibility *= In.diffuse; |
---|
| 105 | Out.color = float4(visibility, visibility, visibility, 0.0); |
---|
| 106 | return Out; |
---|
| 107 | } |
---|