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?

  • 1
    \$\begingroup\$ Have you tried using HLSL's 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\$ Nov 9, 2013 at 5:03
  • \$\begingroup\$ fmod produces the same. But I think you're right about not being able to sample 2 texels apart. It makes sense that the samles in the middle are being sampled differently. However, shouldn't that get resolved if I reduced the filter width to compensate? If I were only able to get (1 / blurFactors) samples, the shader wouldn't be able to get this "in between" samples that cause the artifacts. Is there a way to alter the granularity of the texel fetching? Maybe something like this:float2 coord = ProjectedTexCoords - ProjectedTexCoords % blurFactor; \$\endgroup\$ Nov 9, 2013 at 16:54
  • \$\begingroup\$ I don't really understand what you're saying. You are already altering the granularity of texel fetching by altering the value of blurFactor. I assume you're using nearest-neighbor sampling on the texture? When you use a larger blurFactor, 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\$ Nov 9, 2013 at 17:59

1 Answer 1


Your code is only sampling the corner texels, so is only valid for a 2x2 filter. Sample all the texels within your filter shape to achieve correct results.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .