有名な前面カリング手法を使用して、ハードおよび PCF シャドウ マッピング アルゴリズムを実装する単純な 3D アプリケーションを作成しました。残念ながら、この手法の問題は、密閉されたメッシュのみがキャスト シャドウを生成できることです。たとえば、平面はそれ自体が前面であるため、このような効果を生み出すことはできません。
したがって、解決策は関数「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);
次に、2 番目のレンダリング パスでシャドウ ファクターを計算するためにフラグメント シェーダー コードで使用されるコード:
/*
** \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」一様変数はライト空間の頂点位置で、「index」はライト インデックスです。
しかし、最初のパスで「glPolygonOffset」関数も使用して、PCF シャドウ マッピング アルゴリズム (4 つのサンプルの例) を使用すると問題が発生します。
ご覧のとおり、「シャドウ アクネ」のアーティファクトがはっきりとわかります。
私のフラグメントシェーダーのコードは次のとおりです。
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));
次のレンダリングがあります。
ご覧のとおり、立方体の右側面のみに「影ニキビ」のアーティファクトがあります。
私の問題を解決するために、いくつかのバイアス値を追加してライト空間の頂点深度値を処理するコードの組み合わせをいくつか試しましたが、成功しませんでした。