Skip to content

Commit 124f264

Browse files
committed
*Switched to higher quality PCF filter for softer shadows
*Fixed some artifacts which occured when blending between shadow map cascades *Added some more shader utility functions (namely, PCF filters)
1 parent 0088906 commit 124f264

File tree

3 files changed

+233
-60
lines changed

3 files changed

+233
-60
lines changed

GITechDemo/Code/AppMain/GITechDemo/GITechDemo.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ Matrix44f worldViewProjMat;
4040
const bool GBUFFER_Z_PREPASS = false;
4141

4242
#define MAX_NUM_CASCADES (9)
43-
#define PCF_BLUR_SIZE (4)
43+
#define PCF_BLUR_SIZE (16)
4444
const bool DEBUG_CASCADES = false;
4545
const unsigned int NUM_CASCADES = 4;
46-
const float CASCADE_SPLIT_FACTOR = 0.75f;
46+
const float CASCADE_SPLIT_FACTOR = 0.7f;
4747
const float CASCADE_MAX_VIEW_DEPTH = 3000.f;
4848
const float CASCADE_BLEND_SIZE = 50.f;
4949

GITechDemo/Data/shaders/DeferredLightDir.hlsl

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ static const int nCascadeCount = 4; // number of cascades
5151
static const int nCascadesPerRow = 2; // number of cascades per row, i.e. ceil(sqrt(nCascadeCount))
5252
static const float fCascadeNormSize = 0.5f; // normalized size of a cascade, i.e. 1.f / nCascadesPerRow
5353

54+
// PCF method
55+
#define PCF_SAMPLE PCF4x4Poisson
56+
5457
struct PSOut
5558
{
5659
float4 colorOut : SV_TARGET;
@@ -110,7 +113,7 @@ void psmain(VSOut input, out PSOut output)
110113
float2(fCascadeNormSize * fmod(nValidCascade, nCascadesPerRow), fCascadeNormSize * floor(nValidCascade / nCascadesPerRow));
111114

112115
// PCF Poisson disc shadow sampling
113-
float fPercentLit = PCF4x4Poisson(texShadowMap, f2OneOverShadowMapSize, f3CascadeTexCoord.xy, f3CascadeTexCoord.z - fShadowDepthBias);
116+
float fPercentLit = PCF_SAMPLE(texShadowMap, f2OneOverShadowMapSize, f3CascadeTexCoord.xy, f3CascadeTexCoord.z - fShadowDepthBias);
114117
//float fPercentLit = tex2D(texShadowMap, f3CascadeTexCoord.xy).r > f3CascadeTexCoord.z - fShadowDepthBias;
115118

116119
// if required, blend between cascade seams
@@ -119,29 +122,56 @@ void psmain(VSOut input, out PSOut output)
119122
if (nValidCascade != nCascadeCount - 1)
120123
{
121124
// the blend amount depends on the position of the point inside the blend band of the current cascade
122-
float fBlendAmount = (f2CascadeBoundsMin[nValidCascade].x + fCascadeBlendSize) - f4LightViewPos.x;
123-
fBlendAmount = max(fBlendAmount, (f2CascadeBoundsMin[nValidCascade].y + fCascadeBlendSize) - f4LightViewPos.y);
124-
fBlendAmount = max(fBlendAmount, f4LightViewPos.x - (f2CascadeBoundsMax[nValidCascade].x - fCascadeBlendSize));
125-
fBlendAmount = max(fBlendAmount, f4LightViewPos.y - (f2CascadeBoundsMax[nValidCascade].y - fCascadeBlendSize));
125+
float fScaledBlendSize = fCascadeBlendSize * (nValidCascade * nValidCascade + 1);
126+
float fBlendAmount = 0.f;
127+
128+
// partition the cascade into 4 parts and only allow blending in the parts furthest from the camera
129+
// (i.e. the part that is closest to the camera is not adjacent to any valid cascade, because they
130+
// fit very tightly around the view frustum, so blending in that zone would result in artifacts)
131+
// Disclaimer: there probably exists an easier way to do this, but I'm just lazy
132+
float2 NE = float2(0.707107f, 0.707107f); //normalize(float2(0.5f, 0.5f));
133+
float2 SE = float2(0.707107f, -0.707107f); //normalize(float2(0.5f, -0.5f));
134+
float2 SW = float2(-0.707107f, -0.707107f); //normalize(float2(-0.5f, -0.5f));
135+
float2 NW = float2(-0.707107f, 0.707107f); //normalize(float2(-0.5f, 0.5f));
136+
137+
float2 f2CascadeSpaceViewDir = normalize(mul(f44CascadeProjMat[0], f4LightViewPos).xy);
138+
float4 f4LightSpaceCameraDir = mul(f44CascadeProjMat[0], mul(f44ScreenToLightViewMat, float4(0.f, 0.f, 1.f, 1.f)));
139+
bool dotNE = dot(f2CascadeSpaceViewDir, NE) > 0.f;
140+
bool dotSE = dot(f2CascadeSpaceViewDir, SE) > 0.f;
141+
bool dotSW = dot(f2CascadeSpaceViewDir, SW) > 0.f;
142+
bool dotNW = dot(f2CascadeSpaceViewDir, NW) > 0.f;
143+
bool dotCamDirNE = dot(f4LightSpaceCameraDir.xy, NE) > 0.f;
144+
bool dotCamDirSE = dot(f4LightSpaceCameraDir.xy, SE) > 0.f;
145+
bool dotCamDirSW = dot(f4LightSpaceCameraDir.xy, SW) > 0.f;
146+
bool dotCamDirNW = dot(f4LightSpaceCameraDir.xy, NW) > 0.f;
147+
148+
if (dotSW && dotNW && (dotCamDirSW || dotCamDirNW))
149+
fBlendAmount = max(fBlendAmount, (f2CascadeBoundsMin[nValidCascade].x + fScaledBlendSize) - f4LightViewPos.x);
150+
if (dotSW && dotSE && (dotCamDirSW || dotCamDirSE))
151+
fBlendAmount = max(fBlendAmount, (f2CascadeBoundsMin[nValidCascade].y + fScaledBlendSize) - f4LightViewPos.y);
152+
if (dotSE && dotNE && (dotCamDirSE || dotCamDirNE))
153+
fBlendAmount = max(fBlendAmount, f4LightViewPos.x - (f2CascadeBoundsMax[nValidCascade].x - fScaledBlendSize));
154+
if (dotNW && dotNE && (dotCamDirNW || dotCamDirNE))
155+
fBlendAmount = max(fBlendAmount, f4LightViewPos.y - (f2CascadeBoundsMax[nValidCascade].y - fScaledBlendSize));
156+
157+
fBlendAmount /= fScaledBlendSize;
126158

127159
// if our point is inside the blend band, we can continue with blending
128160
if (fBlendAmount > 0.f)
129161
{
130162
// calculate texture coordinates for sampling from the cascade one order
131163
// higher than the cascade from which we sampled earlier
132164
float3 f3LQCascadeTexCoord = mul(f44CascadeProjMat[nValidCascade + 1], f4LightViewPos).xyz;
133-
if (f3LQCascadeTexCoord.x > 0.f && f3LQCascadeTexCoord.x < 1.f && f3LQCascadeTexCoord.y > 0.f && f3LQCascadeTexCoord.y < 1.f)
134-
{
135-
fBlendAmount /= fCascadeBlendSize;
136-
f3LQCascadeTexCoord.xy =
137-
(f3LQCascadeTexCoord.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f)) *
138-
fCascadeNormSize +
139-
float2(fCascadeNormSize * fmod(nValidCascade + 1, nCascadesPerRow), fCascadeNormSize * floor((nValidCascade + 1) / nCascadesPerRow));
140-
141-
// sample from the lower quality cascade and blend between samples appropriately
142-
float fPercentLitLQ = PCF4x4Poisson(texShadowMap, f2OneOverShadowMapSize, f3LQCascadeTexCoord.xy, f3LQCascadeTexCoord.z - fShadowDepthBias);
143-
fPercentLit = fPercentLit * (1.f - fBlendAmount) + fPercentLitLQ * fBlendAmount;
144-
}
165+
f3LQCascadeTexCoord.xy =
166+
(f3LQCascadeTexCoord.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f)) *
167+
fCascadeNormSize +
168+
float2(fCascadeNormSize * fmod(nValidCascade + 1, nCascadesPerRow), fCascadeNormSize * floor((nValidCascade + 1) / nCascadesPerRow));
169+
170+
// sample from the lower quality cascade and blend between samples appropriately
171+
float fPercentLitLQ = PCF_SAMPLE(texShadowMap, f2OneOverShadowMapSize, f3LQCascadeTexCoord.xy, f3LQCascadeTexCoord.z - fShadowDepthBias);
172+
//float fPercentLitLQ = tex2D(texShadowMap, f3LQCascadeTexCoord.xy).r > f3LQCascadeTexCoord.z - fShadowDepthBias;
173+
if (fPercentLitLQ > 0.f && fPercentLitLQ < 1.f) // only blend at shadow edges
174+
fPercentLit = lerp(fPercentLit, fPercentLitLQ, fBlendAmount);
145175
}
146176
}
147177
}

GITechDemo/Data/shaders/Utils.hlsl

Lines changed: 184 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,73 +29,197 @@ float3 DecodeNormal(float4 enc)
2929
// Percentage Closer Filtering variations //
3030
//////////////////////////////////////////////
3131

32-
float PCF3x3(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
32+
float PCF2x2Poisson(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
33+
{
34+
float2 poissonDisk[4] =
35+
{
36+
float2(-0.94201624, -0.39906216),
37+
float2(0.94558609, -0.76890725),
38+
float2(-0.094184101, -0.92938870),
39+
float2(0.34495938, 0.29387760)
40+
};
41+
float percentLit = 0.f;
42+
43+
for (int i = 0; i < 4; i++)
44+
{
45+
bool isLit =
46+
tex2D(
47+
shadowMap,
48+
texCoord +
49+
poissonDisk[i] * oneOverShadowMapSize
50+
).r > depthCompare;
51+
percentLit += isLit * 0.25f;
52+
}
53+
54+
return percentLit;
55+
}
56+
57+
float PCF3x3Poisson(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
58+
{
59+
float2 poissonDisk[9] =
60+
{
61+
float2(0.4677864f, 1.0492188f),
62+
float2(0.8965628f, -0.3094058f),
63+
float2(-0.3828112f, 1.6226606f),
64+
float2(-0.214711f, -0.7530054f),
65+
float2(-0.6589904f, 0.4324332f),
66+
float2(1.480888f, 1.2347982f),
67+
float2(1.9457676f, -0.13916496f),
68+
float2(1.1015856f, -1.5681816f),
69+
float2(0.06198422f, -1.7864658f)
70+
};
71+
float percentLit = 0.f;
72+
73+
for (int i = 0; i < 9; i++)
74+
{
75+
bool isLit =
76+
tex2D(
77+
shadowMap,
78+
texCoord +
79+
poissonDisk[i] * oneOverShadowMapSize
80+
).r > depthCompare;
81+
percentLit += isLit * 0.1111111f;
82+
}
83+
84+
return percentLit;
85+
}
86+
87+
float PCF12TapPoisson(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
3388
{
89+
float2 poissonDisk[12] =
90+
{
91+
float2(-0.5946302f, 0.6008242f),
92+
float2(-1.762928f, 0.5199542f),
93+
float2(-1.56506f, -0.8692048f),
94+
float2(0.14760224f, 1.7710094f),
95+
float2(0.7065646f, 0.04265878f),
96+
float2(-0.5888886f, -0.429245f),
97+
float2(1.024129f, 1.1201256f),
98+
float2(-1.118836f, 1.6018892f),
99+
float2(-0.9557046f, -1.7215218f),
100+
float2(0.02812326f, -1.4003752f),
101+
float2(1.6575236f, -0.287287f),
102+
float2(1.3867802f, -1.375697f)
103+
};
34104
float percentLit = 0.f;
105+
106+
for (int i = 0; i < 12; i++)
107+
{
108+
bool isLit =
109+
tex2D(
110+
shadowMap,
111+
texCoord +
112+
poissonDisk[i] * oneOverShadowMapSize
113+
).r > depthCompare;
114+
percentLit += isLit * 0.0833333f;
115+
}
116+
117+
return percentLit;
118+
}
119+
120+
float PCF4x4Poisson(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
121+
{
122+
float2 poissonDisk[16] =
123+
{
124+
float2(2.688109f, 0.686785f),
125+
float2(3.0212565f, -1.1432755f),
126+
float2(1.9640215f, 1.5282315f),
127+
float2(3.293479f, 1.5044285f),
128+
float2(3.7991725f, 0.47122805f),
129+
float2(1.211579f, 0.587791f),
130+
float2(1.8459345f, -0.8353495f),
131+
float2(4.1637455f, -0.639698f),
132+
float2(2.3383775f, 2.6021615f),
133+
float2(4.58543f, 1.3013345f),
134+
float2(3.9300595f, 2.3949865f),
135+
float2(3.537996f, 3.44172f),
136+
float2(1.055484f, 2.1200395f),
137+
float2(1.9478455f, 3.7591355f),
138+
float2(1.0448675f, 3.2125375f),
139+
float2(-0.25313345f, 2.309396f)
140+
};
141+
float percentLit = 0.f;
142+
143+
for (int i = 0; i < 16; i++)
144+
{
145+
bool isLit =
146+
tex2D(
147+
shadowMap,
148+
texCoord +
149+
poissonDisk[i] * oneOverShadowMapSize
150+
).r > depthCompare;
151+
percentLit += isLit * 0.0625f;
152+
}
153+
154+
return percentLit;
155+
}
156+
157+
float PCF3x3Dithered(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
158+
{
159+
// use modulo to vary the sample pattern
160+
float2 offset = floor(texCoord.xy) % 3.0;
35161

162+
float percentLit = 0.f;
36163
for(int x = -1; x <= 1; x++)
37164
for(int y = -1; y <= 1; y++)
38165
{
39166
bool isLit =
40167
tex2D(
41168
shadowMap,
42169
texCoord +
43-
float2(x * oneOverShadowMapSize.x, y * oneOverShadowMapSize.y)
170+
float2(x, y) * oneOverShadowMapSize +
171+
offset
44172
).r > depthCompare;
45173
percentLit += isLit;
46174
}
47175

48176
return percentLit * 0.111111f;
49177
}
50178

51-
float PCF4x4Poisson(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
179+
float PCF5x5Dithered(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
52180
{
53-
float2 poissonDisk[4] =
54-
{
55-
float2( -0.94201624, -0.39906216 ),
56-
float2( 0.94558609, -0.76890725 ),
57-
float2( -0.094184101, -0.92938870 ),
58-
float2( 0.34495938, 0.29387760 )
59-
};
60-
float percentLit = 0.f;
181+
// use modulo to vary the sample pattern
182+
float2 offset = floor(texCoord.xy) % 5.0;
61183

62-
for(int i = 0; i < 4; i++)
63-
{
64-
bool isLit =
65-
tex2D(
66-
shadowMap,
67-
texCoord +
68-
poissonDisk[i] * oneOverShadowMapSize
69-
).r > depthCompare;
70-
percentLit += isLit * 0.2f;
71-
}
184+
float percentLit = 0.f;
185+
for(int x = -2; x <= 2; x++)
186+
for(int y = -2; y <= 2; y++)
187+
{
188+
bool isLit =
189+
tex2D(
190+
shadowMap,
191+
texCoord +
192+
float2(x, y) * oneOverShadowMapSize +
193+
offset
194+
).r > depthCompare;
195+
percentLit += isLit;
196+
}
72197

73-
return percentLit;
198+
return percentLit * 0.04f;
74199
}
75200

76-
float PCF4x4Dithered(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
201+
float PCF8x8Dithered(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
77202
{
78203
// use modulo to vary the sample pattern
79-
float2 offset = floor(texCoord.xy) % 2.0;
204+
float2 offset = floor(texCoord.xy) % 8.0;
80205

81-
float percentLit = 0.f;
82-
percentLit += tex2D(shadowMap, texCoord + (float2(-1.5, 1.5) + offset) * oneOverShadowMapSize).r > depthCompare;
83-
percentLit += tex2D(shadowMap, texCoord + (float2(0.5, 1.5) + offset) * oneOverShadowMapSize).r > depthCompare;
84-
percentLit += tex2D(shadowMap, texCoord + (float2(-1.5, -0.5) + offset) * oneOverShadowMapSize).r > depthCompare;
85-
percentLit += tex2D(shadowMap, texCoord + (float2(0.5, -0.5) + offset) * oneOverShadowMapSize).r > depthCompare;
86-
87-
return percentLit * 0.25 ;
206+
float x, y, percentLit = 0.f;
207+
for (y = -3.5f; y <= 3.5f; y += 1.0f)
208+
for (x = -3.5f; x <= 3.5f; x += 1.0f)
209+
percentLit += tex2D(shadowMap, texCoord + float2(x, y) * oneOverShadowMapSize + offset).r > depthCompare;
210+
211+
return percentLit * 0.015625f;
88212
}
89213

90214
float PCF5x5Gaussian(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
91215
{
92216
const float GaussianKernel[5][5] =
93217
{
94-
0.00296901674395065, 0.013306209891014005, 0.02193823127971504, 0.013306209891014005, 0.00296901674395065,
95-
0.013306209891014005, 0.05963429543618023, 0.09832033134884507, 0.05963429543618023, 0.013306209891014005,
96-
0.02193823127971504, 0.09832033134884507, 0.16210282163712417, 0.09832033134884507, 0.02193823127971504,
97-
0.013306209891014005, 0.05963429543618023, 0.09832033134884507, 0.05963429543618023, 0.013306209891014005,
98-
0.00296901674395065, 0.013306209891014005, 0.02193823127971504, 0.013306209891014005, 0.00296901674395065
218+
0.00296901674395065f, 0.013306209891014005f, 0.02193823127971504f, 0.013306209891014005f, 0.00296901674395065f,
219+
0.013306209891014005f, 0.05963429543618023f, 0.09832033134884507f, 0.05963429543618023f, 0.013306209891014005f,
220+
0.02193823127971504f, 0.09832033134884507f, 0.16210282163712417f, 0.09832033134884507f, 0.02193823127971504f,
221+
0.013306209891014005f, 0.05963429543618023f, 0.09832033134884507f, 0.05963429543618023f, 0.013306209891014005f,
222+
0.00296901674395065f, 0.013306209891014005f, 0.02193823127971504f, 0.013306209891014005f, 0.00296901674395065f
99223
};
100224
float percentLit = 0.f;
101225

@@ -106,21 +230,40 @@ float PCF5x5Gaussian(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 te
106230
tex2D(
107231
shadowMap,
108232
texCoord +
109-
float2(x * oneOverShadowMapSize.x, y * oneOverShadowMapSize.y)
233+
float2(x, y) * oneOverShadowMapSize
110234
).r > depthCompare;
111235
percentLit += isLit * GaussianKernel[x+2][y+2];
112236
}
113237

114238
return percentLit;
115239
}
116240

241+
float PCF3x3(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
242+
{
243+
float percentLit = 0.f;
244+
245+
for (int x = -1; x <= 1; x++)
246+
for (int y = -1; y <= 1; y++)
247+
{
248+
bool isLit =
249+
tex2D(
250+
shadowMap,
251+
texCoord +
252+
float2(x, y) * oneOverShadowMapSize
253+
).r > depthCompare;
254+
percentLit += isLit;
255+
}
256+
257+
return percentLit * 0.111111f;
258+
}
259+
117260
float PCF8x8(sampler2D shadowMap, float2 oneOverShadowMapSize, float2 texCoord, float depthCompare)
118261
{
119-
float x, y, percentLit = 0.f;
120-
for (y = -3.5 ; y <=3.5 ; y+=1.0)
121-
for (x = -3.5 ; x <=3.5 ; x+=1.0)
262+
float percentLit = 0.f;
263+
for (float y = -3.5f ; y <=3.5f ; y += 1.0f)
264+
for (float x = -3.5f ; x <=3.5f ; x += 1.0f)
122265
percentLit += tex2D(shadowMap, texCoord + float2(x, y) * oneOverShadowMapSize).r > depthCompare;
123266

124-
return percentLit / 64.0 ;
267+
return percentLit * 0.015625f;
125268
}
126269
////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)