try to emulate hardware shadow mapping

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.

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.
<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.

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).

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).

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.

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.

As far as I know it depends on the hardware/driver.


if(texCoord.z < depth)
  return 1.0;
else
  return 0;

I am not sure what you do here. You should compare the value in the texture with the z texture coordinate.

Here is a tutorial about PCF, might be useful: http://fabiensanglard.net/shadowmappingPCF/index.php

See this one for texture comparison without using Sampler2DShadow: http://fabiensanglard.net/shadowmapping/index.php

Thank you for your reply. I read the article you mentioned above, but what I want is to do a depth comparison by myself when the GL_TEXTURE_COMPARE_MODE is set to GL_NONE.

Instead of rely on the sampling function to return a comparison result, I would like to fetch the depth value from the texture and compare that with the depth of the fragment. It is what I have done in my code, but it generates the false result.

I must admit that my first post is too long. Now I am going to put my questiones one by one:

Here comes my first problem:

if the parameters of the depth texture object is like this:


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_NONE);

(notice: the value for GL_TEXTURE_COMPARE_MODE is GL_NONE.)
and in the fragment shader, the declaration of the texture is like:


uniform sampler2DShadow depthTex;

what can I get from:


float depth = textureProj(depthTex, texPos);

(notice: texPos is a vec4 variable used to sample the texture.)

Suppose this function returns the depth value from the texture, then I manually compare that with texPos.z/texPos.w, like:


if(texPos.z/texPos.w < depth)
	oColor = lightColor ;
else
	oColor = vec4(0, 0, 0, 1.0);

but the result is out of expected.

what can I get from:

Nothing. If you use a Shadow sampler type in GLSL, the texture must have a COMPARE_MODE that is not NONE. Otherwise, you get either undefined behavior or a GL error. Probably the former.

Just to add to Alfonseā€™s remark: You can also do shadow mapping using REGULAR samplers with GL_COMPARE_MODE == GL_NONE and implement the whole procedure yourself.

This is actually the proposed approach for implementing percentage closer softshadows.

Cheers.

I finally found the problem. When trying to emulate the hardware PCF, not only need to set GL_COMPARE_MODE to GL_NONE, but also need to set GL_TEXTURE_MAG_FILTER to GL_NEAREST, then the interpolation in the shader will generate correct result.