It is well-known that if the function textureProj takes in a sampler2DShadow type argument, it will performs a 4-sample PCF when the linear filtering is turned on. Now, I want to perform PCF by myself rather than rely on hardware, but I failed.
I will state how I implemented the regular shadow mapping (section 1), and how I tried to emulate PCF (section 2 & 3).
1. the regular shadow mapping
I created the depth texture object using the following code:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, 512, 512, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
Nothing special here.
In the fragment shader, the texture uniform is defined as
uniform sampler2DShadow depthTex;
then use the following function to directly return a value that represents how much the current fragment is in shadow:
float shadeFactor = textureProj(depthTex, texPos);
Refer to Figure 1 for the result.
2. 16-sample PCF
I use the technique described in GPU Gems - Chapter 11. Shadow Map Antialiasing to improve the shadow.
The change from last program only lies in the fragment shader. The following function can be used to do a projected texture map read with an offset given in texel units. The variable texmapscale is a vec2 containing 1/width and 1/height of the shadow map.
float offset_lookup(sampler2DShadow map, vec4 loc, vec2 offset)
vec2 texmapscale = vec2(1/512.0, 1/512.0);
return textureProj(map, vec4(loc.xy + offset * texmapscale * loc.w, loc.z, loc.w));
The implement of 16-sample version in a fragment program is as follows:
float sum = 0;
float x, y;
for (y = -1.5; y <= 1.5; y += 1.0)
for (x = -1.5; x <= 1.5; x += 1.0)
sum += offset_lookup(depthTex, texPos, vec2(x, y));
float shadeFactor = sum / 16.0;
The result is shown here.
3. My attempt to emulate the hardware PCF effect
What I have done here is to turn off the GL_TEXTURE_COMPARE_MODE by setting it to GL_NONE, so the textureProj function will not do a comparison, and the expected return value from textureProj should be the depth value in the sampled texture. In addition, I manually did a comparison between the current depth value and the depth value in the texture as follows:
if(texCoord.z < depth)
But this time, the result was not as expected. The area within the view of the light was all black, as if all the comparisons returned 0 (Figure 3).
Therefore I tried another way. Changed the definition of the texture uniform to
uniform sampler2D depthTex;
therefore the function textureProj no longer takes in a sampler2DShadow, so it and returns a vec4, which should be the depth value in the texture. I took the value from the R channel and compared with the depth of the fragment, but the result was also out of expected (Figure 4).
The problem is why the result in my last step is different from the beginning one? How can I emulate the hardware PCF.
Thanks in advance.