/////////////////////////////////////////////////////////////////////////////////
//
// 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;
}