For a while now I used PCSS for my shadow technique of choice until I discovered a type of percentage closer filtering. This method creates really smooth shadows and with hopes of improving performance, with only a fraction of texture samples, I tried to implement PCF into my shader. This is the relevant code:
float c0, c1, c2, c3;
float f = blurFactor;
float2 coord = ProjectedTexCoords;
if (receiverDistance - tex2D(lightSampler, coord + float2(0, 0)).x > 0.0007)
c0 = 1;
if (receiverDistance - tex2D(lightSampler, coord + float2(f, 0)).x > 0.0007)
c1 = 1;
if (receiverDistance - tex2D(lightSampler, coord + float2(0, f)).x > 0.0007)
c2 = 1;
if (receiverDistance - tex2D(lightSampler, coord + float2(f, f)).x > 0.0007)
c3 = 1;
coord = (coord % f) / f;
return 1 - (c0 * (1 - coord.x) * (1 - coord.y) + c1 * coord.x * (1 - coord.y) + c2 * (1 - coord.x) * coord.y + c3 * coord.x * coord.y);
This is a very basic implementation. blurFactor is initialized with 1 / LightTextureSize. So the if statements fetch the occlusion values for the four adjacent texels. I now want to weight each value based on the actual position of the texture coordinate. If it's near the bottom-right pixel, that occlusion value should be preferred. The weighting itself is done with a simple bilinear interpolation function, however this function takes a 2d vector in the range [0..1] so I have to convert my texture coordinate to get the distance from my first pixel to the second one in range [0..1]. For that I used the mod operator to get it into [0..f] range and then divided by f. This code makes sense to me, and for specific blurFactors it works, producing really smooth one pixel wide shadows, but not for all blurFactors. Initially blurFactor is (1 / LightTextureSize) to sample the 4 adjacent texels. I now want to increase the blurFactor by factor x to get a smooth interpolation across maybe 4 or so pixels. But that is when weird artifacts show up. Here is an image: Using a 1x on blurFactor produces a good result, 0.5 is as expected not so smooth. 2x however doesn't work at all. I found that only a factor of 1/2^n produces an good result, every other factor produces artifacts. I'm pretty sure the error lies here: coord = (coord % f) / f; Maybe the modulo is not calculated correctly? I have no idea how to fix that. Is it even possible for pixel that are further than 1 pixel away?
fmod
instead of%
? BTW, if you're sampling more than 1 texel apart you can't expect to get a completely smooth result as you're not taking into account all the texels inside the filter width. But the 2x image above doesn't look like the kind of artifacts I would expect from that. \$\endgroup\$float2 coord = ProjectedTexCoords - ProjectedTexCoords % blurFactor;
\$\endgroup\$blurFactor
. I assume you're using nearest-neighbor sampling on the texture? When you use a largerblurFactor
, you skip over some texels. You would need to add more than 4 samples if you want to filter over an area larger than 4 texels. \$\endgroup\$