shapeare
12-01-2011, 02:53 AM
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:
<code>
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &depthTexture);
glBindTexture(GL_TEXTURE_2D, depthTexture);
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);
</code>
Nothing special here.
In the fragment shader, the texture uniform is defined as
<code>
uniform sampler2DShadow depthTex;
</code>
then use the following function to directly return a value that represents how much the current fragment is in shadow:
<code>
float shadeFactor = textureProj(depthTex, texPos);
</code>
Refer to Figure 1 for the result.
http://graphicsprojects.files.wordpress.com/2011/12/directshadow_textureproj.png
2. 16-sample PCF
I use the technique described in GPU Gems - Chapter 11. Shadow Map Antialiasing (http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html) 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.
<code>
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));
}
</code>
The implement of 16-sample version in a fragment program is as follows:
<code>
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;
</code>
The result is shown here.
http://graphicsprojects.files.wordpress.com/2011/12/post3.png
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:
<code>
if(texCoord.z < depth)
return 1.0;
else
return 0;
</code>
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).
http://graphicsprojects.files.wordpress.com/2011/12/post4.png
Therefore I tried another way. Changed the definition of the texture uniform to
<code>
uniform sampler2D depthTex;
</code>
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).
http://graphicsprojects.files.wordpress.com/2011/12/post5.png
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.
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:
<code>
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &depthTexture);
glBindTexture(GL_TEXTURE_2D, depthTexture);
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);
</code>
Nothing special here.
In the fragment shader, the texture uniform is defined as
<code>
uniform sampler2DShadow depthTex;
</code>
then use the following function to directly return a value that represents how much the current fragment is in shadow:
<code>
float shadeFactor = textureProj(depthTex, texPos);
</code>
Refer to Figure 1 for the result.
http://graphicsprojects.files.wordpress.com/2011/12/directshadow_textureproj.png
2. 16-sample PCF
I use the technique described in GPU Gems - Chapter 11. Shadow Map Antialiasing (http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html) 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.
<code>
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));
}
</code>
The implement of 16-sample version in a fragment program is as follows:
<code>
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;
</code>
The result is shown here.
http://graphicsprojects.files.wordpress.com/2011/12/post3.png
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:
<code>
if(texCoord.z < depth)
return 1.0;
else
return 0;
</code>
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).
http://graphicsprojects.files.wordpress.com/2011/12/post4.png
Therefore I tried another way. Changed the definition of the texture uniform to
<code>
uniform sampler2D depthTex;
</code>
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).
http://graphicsprojects.files.wordpress.com/2011/12/post5.png
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.