I wrote a simple 3D application implementing hard and PCF shadow mapping algorithms using the famous front face culling technique. Unfortunatly, the problem with this technique is only sealed meshes can produce cast shadows. For example a plane can't produce a such effect because a plane is a front face by itself.
So the solution is to use the function 'glPolygonOffset' which the goal is to slightly modify the depth value during the depth rendring path of each vertex visible from the 'light view'. In other words this function is needed to avoid 'shadow acne' artifacts keeping this time all meshes front faces.
Here's a display of a such rendering using hard shadow mapping algorithm:
As you can see the shadow rendering is perfect without any artifacts!
Here's the C++ client code defining the depth texture rendering path:
/*glEnable(GL_CULL_FACE); //OLD TECHNIQUE
glCullFace(GL_FRONT);*/
for (uint32_t idy = 0; idy < lightSceneNodeList.size(); idy++)
{
if (lightSceneNodeList[idy]->IsShadowEnabled())
{
type::ShadowCasterPtr pShadowCaster = ShadowManager::GetSingleton()
.FindShadowCasterByName(lightSceneNodeList[idy]->GetName());
{
pShadowCaster->Bind(TARGET_FBO);
{
glEnable(GL_POLYGON_OFFSET_FILL); //NEW TECHNIQUE
glPolygonOffset(1.1f, 4.0f);
glClear(GL_DEPTH_BUFFER_BIT);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
{
pShadowCaster->UpdateFrustrumPosition(
lightSceneNodeList[idy]->GetParentModelMatrix());
pShadowCaster->SetViewport();
{
for (uint32_t idx = 0; idx < pBatchList.size(); idx++)
pBatchList[idx]->Render(pShadowCaster);
}
}
glDisable(GL_POLYGON_OFFSET_FILL);
}
pShadowCaster->Unbind(TARGET_FBO);
}
}
}
//glDisable(GL_CULL_FACE);
And now the code used in the fragment shader code to compute the shadow factor during the second rendering path:
/*
** \brief Recover the depth value from shadow map by projection
*/
float Tex2D_Proj(sampler2DShadow shadowSampler, vec4 LightToVertexDir_LS)
{
float ShadowFactor = 1.0f;
{
vec3 LightToVertexDir_CS = LightToVertexDir_LS.xyz/LightToVertexDir_LS.w;
ShadowFactor = texture(shadowSampler, LightToVertexDir_CS);
}
return (ShadowFactor);
}
/*
** \brief Returns biased hard shadow factor.
*/
float Get_2D_Hard_ShadowFactor(sampler2DShadow shadowSampler, int index)
{
float shadowFactor = 1.0f;
{
if (ShadowCoords[index].z <= MaxShadowDist[index])
{
if (ShadowCoords[index].w > 0.0f);
{
shadowFactor = Tex2D_Proj(shadowSampler, ShadowCoords[index]);
}
}
}
return (shadowFactor);
}
The 'ShadowCoords' uniform variable is a vertex position in light space and the 'index' is the light index.
But now I have a problem using PCF shadow mapping algorithm (an example with 4 samples) using also the function 'glPolygonOffset' during the first path:
As you can see we can see clearly 'shadow acne' artifacts!
Here's the code from my fragment shader:
float Get_2D_PCF_ShadowFactor(sampler2DShadow shadowSampler, int index)
{
float shadowFactor = 0.0f;
{
int kernel_base = int(PCFKernelType[index])/2;
float kernel_count = pow(int(PCFKernelType[index]), 2.0f);
if (ShadowCoords[index].z <= MaxShadowDist[index])
{
if (ShadowCoords[index].w > 0.0f)
{
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, 1));
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(1, 1));
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(1, -1));
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, -1));
shadowFactor *= 0.25f;
}
}
}
return (shadowFactor);
}
The 'textureProjOffset' code is equal to the following one:
float Tex2D_Proj_Offset(sampler2DShadow shadowSampler, vec4 LightToVertexDir_LS, vec2 offsetCoords, vec2 shadowMapSize)
{
float offset_x = 1.0f/shadowMapSize.x;
float offset_y = 1.0f/shadowMapSize.y;
float ShadowFactor = 1.0f;
{
vec3 LightToVertexDir_CS = LightToVertexDir_LS.xyz/LightToVertexDir_LS.w;
vec2 ShadowTexCoords = vec2(LightToVertexDir_CS.x, LightToVertexDir_CS.y);
vec2 DerivedShadowTexCoords = vec2(
ShadowTexCoords.x + offsetCoords.x * offset_x,
ShadowTexCoords.y + offsetCoords.y * offset_y);
ShadowFactor = texture(shadowSampler, vec3(
DerivedShadowTexCoords, LightToVertexDir_CS.z));
}
return (ShadowFactor);
}
Only the call of 'textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(0, 0))' works correctly (of course it refers to the first hard shadow mapping technique).
If I only use the following call (so a simple hard shadow mapping but using an offset):
shadowFactor = textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, 0));
I have the following rendering:
As you can see, there are 'shadow acne' artifacts only on the right face of the cube!
To resolve my problem I tried several combinations of code adding some bias values to deal with vertex depth value in light space without any success.
Don't know if this helps. Maybe I'm over-complicating your question. This code is from an Objective C application, but the relevant code is mostly just C. It runs and works. You'll see in the scene fragment shader a statement: if (depthInShadow > 0.006). This depth in shadow number is something I had to "tweak" to get rid of the acne.
This is an edit. After re-reading your post I see a statement: if (ShadowCoords[index].w > 0.0f). This looks very similar to the depthInShadow statement in my scene fragment shader which I had to "tweak" to something just a little greater than 0.0 to get rid of the acne. Give that a try.
I'll leave the code posted below in case it's of interest to anyone else coming by who's new to shadow mapping.
Many of the variables are declared in the .h file, but you'll get the idea. This is pretty standard shadow mapping code, so if you've seen this all before, you can stop here and save yourself a long read.
I set up the shadow buffer and shaders:
The uniform shadow matrices are, of course, from the camera position.
The shadow shaders used to render into the shadow buffer are trivial.
Shadow Vertex shader:
The Shadow fragment shader:
I first render the scene to the shadow buffer with:
I then render the scene normally using these shaders:
Scene Vertex shader:
Scene Fragment shader:
The shadow bias matrix is: