我编写了一个简单的 3D 应用程序,使用著名的正面剔除技术实现硬和 PCF 阴影映射算法。不幸的是,这种技术的问题是只有密封的网格会产生投射阴影。例如,平面不能产生这样的效果,因为平面本身就是正面。
因此解决方案是使用函数“glPolygonOffset”,其目标是在“光照视图”可见的每个顶点的深度渲染路径期间稍微修改深度值。换句话说,需要此功能来避免“阴影痤疮”伪影,此时保持所有网格正面。
这是使用硬阴影映射算法进行此类渲染的显示:
如您所见,阴影渲染非常完美,没有任何伪影!
这是定义深度纹理渲染路径的 C++ 客户端代码:
/*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);
现在片段着色器代码中用于计算第二个渲染路径期间的阴影因子的代码:
/*
** \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);
}
“ShadowCoords”统一变量是光照空间中的顶点位置,“索引”是光照索引。
但是现在我在使用 PCF 阴影映射算法(一个有 4 个样本的示例)时遇到了问题,在第一条路径中还使用了函数“glPolygonOffset”:
如您所见,我们可以清楚地看到“暗疮”伪影!
这是我的片段着色器中的代码:
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);
}
'textureProjOffset' 代码等于以下代码:
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);
}
只有 'textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(0, 0))' 的调用才能正常工作(当然它指的是第一个硬阴影映射技术)。
如果我只使用以下调用(所以一个简单的硬阴影映射但使用偏移):
shadowFactor = textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, 0));
我有以下渲染:
如您所见,仅在立方体的右面上有“阴影痤疮”伪影!
为了解决我的问题,我尝试了几种代码组合,添加了一些偏差值来处理光空间中的顶点深度值,但没有任何成功。