Shaders for Shadows
Shader code for rendering shadows of translucent occluders, soft shadows and single scattering with moment shadow maps.
 All Classes Files Functions Variables Pages
ShadowUtility.fx
Go to the documentation of this file.
1 
11 void ComputePCFShadowIntensity(out float OutShadowIntensity,Texture2D ShadowMap,SamplerState ShadowMapSampler,float2 ShadowMapTexCoord,float FragmentDepth,float4 ShadowMapSize,const float StandardDeviation,const int KernelRadius,float DepthBias){
12  float BiasedDepth=mad(FragmentDepth-DepthBias,0.5f,0.5f);
13  // Determine the region that needs to be sampled. It is extended by one texel
14  // for bilinear filtering
15  const int iFirstSample=-KernelRadius-1;
16  const int iLastSample=KernelRadius;
17  // Prepare quantities for computation of filter weights
18  const float InvVariance=1.0f/(StandardDeviation*StandardDeviation);
19  ShadowMapTexCoord+=ShadowMapSize.zw*0.5f;
20  float2 TexelCoord=frac(ShadowMapTexCoord*ShadowMapSize.xy);
21  OutShadowIntensity=0.0f;
22  float TotalWeight=0.0f;
23  // Loop over the samples. This code might seem inefficient at first glance but
24  // the HLSL compiler should take good care of it because typically most input
25  // parameters it depends upon are hard-coded literals.
26  [unroll] for(int x=iFirstSample;x<=iLastSample;++x){
27  // Linearly interpolate between two Gaussian weights to implement bilinear
28  // filtering on top of an image-space Gaussian filtering
29  float HorizontalWeight=lerp((x==iLastSample)?0.0f:exp(-0.5f*(x+1)*(x+1)*InvVariance),(x==iFirstSample)?0.0f:exp(-0.5f*x*x*InvVariance),TexelCoord.x);
30  [unroll] for(int y=iFirstSample;y<=iLastSample;++y){
31  // Obtain the PCF sample
32  float2 OffsetTexCoord=ShadowMapTexCoord+ShadowMapSize.zw*float2(x,y);
33  float SampledShadowMapDepth=ShadowMap.Sample(ShadowMapSampler,OffsetTexCoord);
34  float Summand=(SampledShadowMapDepth<BiasedDepth)?1.0f:0.0f;
35  // Compute the vertical weight in analogy to the horizontal weight
36  float VerticalWeight=lerp((y==iLastSample)?0.0f:exp(-0.5f*(y+1)*(y+1)*InvVariance),(y==iFirstSample)?0.0f:exp(-0.5f*y*y*InvVariance),TexelCoord.y);
37  float Weight=HorizontalWeight*VerticalWeight;
38  OutShadowIntensity+=Weight*Summand;
39  TotalWeight+=Weight;
40  }
41  }
42  OutShadowIntensity/=TotalWeight;
43 }
44 
45 
48 void ComputePCFShadowIntensityBilinear(out float OutShadowIntensity,Texture2D<float> ShadowMap,SamplerComparisonState ShadowMapSampler,float2 ShadowMapTexCoord,float FragmentDepth,float4 ShadowMapSize,const float StandardDeviation,const int KernelRadius,float DepthBias){
49  float BiasedDepth=mad(FragmentDepth-DepthBias,0.5f,0.5f);
50  // Determine the region that needs to be sampled. Conveniently, this is always a
51  // region with an even edge-length because the kernel has an odd edge-length and
52  // one additional sample is needed for bilinear interpolation.
53  const int iFirstSample=-KernelRadius-1;
54  const int iLastSample=KernelRadius;
55  // Prepare quantities for computation of filter weights. In particular we need a
56  // shadow map texture coordinate located exactly in the center of a texel i.e.
57  // sampling only one value.
58  const float InvVariance=1.0f/(StandardDeviation*StandardDeviation);
59  ShadowMapTexCoord+=ShadowMapSize.zw*0.5f;
60  float2 TexelCoord=frac(ShadowMapTexCoord*ShadowMapSize.xy);
61  ShadowMapTexCoord-=ShadowMapSize.zw*TexelCoord-ShadowMapSize.zw*0.5f;
62  OutShadowIntensity=0.0f;
63  float TotalWeight=0.0f;
64  // Loop over the samples always taking two steps at a time. We use SampleCmp()
65  // to get four samples at once without changing the weighting.
66  [unroll] for(int x=iFirstSample;x<=iLastSample;x+=2){
67  // Compute weights for the left and right boundary of the texel
68  int LeftX=x;
69  float LeftWeight=lerp(exp(-0.5f*(LeftX+1)*(LeftX+1)*InvVariance),(LeftX==iFirstSample)?0.0f:exp(-0.5f*LeftX*LeftX*InvVariance),TexelCoord.x);
70  int RightX=x+1;
71  float RightWeight=lerp((RightX==iLastSample)?0.0f:exp(-0.5f*(RightX+1)*(RightX+1)*InvVariance),exp(-0.5f*RightX*RightX*InvVariance),TexelCoord.x);
72  // Compute the relation between these weights to pipe it into SampleCmp() as
73  // texture coordinate
74  float HorizontalWeight=LeftWeight+RightWeight;
75  float RelativeHorizontalWeight=RightWeight/HorizontalWeight;
76  [unroll] for(int y=iFirstSample;y<=iLastSample;y+=2){
77  // Compute vertical weights in analogy to the horizontal weights
78  int TopY=y;
79  float TopWeight=lerp(exp(-0.5f*(TopY+1)*(TopY+1)*InvVariance),(TopY==iFirstSample)?0.0f:exp(-0.5f*TopY*TopY*InvVariance),TexelCoord.y);
80  int BottomY=y+1;
81  float BottomWeight=lerp((BottomY==iLastSample)?0.0f:exp(-0.5f*(BottomY+1)*(BottomY+1)*InvVariance),exp(-0.5f*BottomY*BottomY*InvVariance),TexelCoord.y);
82  float VerticalWeight=TopWeight+BottomWeight;
83  float RelativeVerticalWeight=BottomWeight/VerticalWeight;
84  // Take a comparison sample using the relative weights as intra-texel
85  // coordinate
86  float2 OffsetTexCoord=ShadowMapTexCoord+ShadowMapSize.zw*float2(x+RelativeHorizontalWeight,y+RelativeVerticalWeight);
87  float Summand=ShadowMap.SampleCmpLevelZero(ShadowMapSampler,OffsetTexCoord,BiasedDepth);
88  float Weight=HorizontalWeight*VerticalWeight;
89  OutShadowIntensity+=Weight*Summand;
90  TotalWeight+=Weight;
91  }
92  }
93  OutShadowIntensity/=TotalWeight;
94 }
95 
96 
101 float2 GetRoots(float3 Coefficients){
102  float Scaling=1.0f/Coefficients[2];
103  float p=Coefficients[1]*Scaling;
104  float q=Coefficients[0]*Scaling;
105  float D=p*p*0.25f-q;
106  float r=sqrt(D);
107  return float2(-0.5f*p-r,-0.5f*p+r);
108 }
109 
110 
113 float GetHankelDeterminant(float4 Moments){
114  float L21D11=mad(-Moments[0],Moments[1],Moments[2]);
115  float D11=mad(-Moments[0],Moments[0], Moments[1]);
116  float SquaredDepthVariance=mad(-Moments[1],Moments[1], Moments[3]);
117  return dot(float2(SquaredDepthVariance,-L21D11),float2(D11,L21D11));
118 }
119 
120 
124 float GetFourthMomentOffset(float4 Moments){
125  float D11=mad(-Moments[0],Moments[0],Moments[1]);
126  float L21D11=mad(-Moments[0],Moments[1],Moments[2]);
127  float L21=L21D11/D11;
128  float SquaredDepthVariance=mad(-Moments[1],Moments[1],Moments[3]);
129  return mad(-L21D11,L21,SquaredDepthVariance);
130 }
float GetFourthMomentOffset(float4 Moments)
float3 GetRoots(float4 Coefficient)
float GetHankelDeterminant(float4 Moments)
void ComputePCFShadowIntensity(out float OutShadowIntensity, Texture2D ShadowMap, SamplerState ShadowMapSampler, float2 ShadowMapTexCoord, float FragmentDepth, float4 ShadowMapSize, const float StandardDeviation, const int KernelRadius, float DepthBias)
void ComputePCFShadowIntensityBilinear(out float OutShadowIntensity, Texture2D< float > ShadowMap, SamplerComparisonState ShadowMapSampler, float2 ShadowMapTexCoord, float FragmentDepth, float4 ShadowMapSize, const float StandardDeviation, const int KernelRadius, float DepthBias)