级联阴影贴图无法按预期工作

Posted

技术标签:

【中文标题】级联阴影贴图无法按预期工作【英文标题】:Cascaded Shadow Maps don't work as expected 【发布时间】:2020-08-21 21:33:33 【问题描述】:

我正在尝试使用 opengl 实现级联阴影映射,但遇到了一些问题。

我首先将我的视锥体分成三个分割,每个分割都有一个 1- 附近 2-远 3- 角(世界空间中这个特定分割的平截头体的角) 4 深度图(2D 纹理,尺寸为 1024 * 1024)

对于每个分割,我首先计算它的 corners,如下所示,并使用这些 世界空间中的corner 我计算 frustum center 我将用于计算光照视图矩阵。

float width = float(mRenderer->GetGame()->GetWidth()); 
float height = float(mRenderer->GetGame()->GetHeight()); 
mProjMatrix = glm::perspective(glm::radians(90.0f), (float)width / (float)height, mNear, mFar); 
mViewMatrix = mRenderer->GetView();

glm::mat4 viewProj = mProjMatrix * mViewMatrix; 

glm::vec3 frustumCorners[8] =

    glm::vec3(-1.0f, 1.0f, -1.0f),
    glm::vec3(1.0f, 1.0f, -1.0f),
    glm::vec3(1.0f, -1.0f, -1.0f),
    glm::vec3(-1.0f, -1.0f, -1.0f),
    glm::vec3(-1.0f, 1.0f, 1.0f),
    glm::vec3(1.0f, 1.0f, 1.0f),
    glm::vec3(1.0f, -1.0f, 1.0f),
    glm::vec3(-1.0f, -1.0f, 1.0f),
;

for (int i = 0; i < 8; ++i) 

    glm::vec4 inversePoint = glm::inverse(viewProj) * glm::vec4(frustumCorners[i], 1.0f);
    mCorners[i] = glm::vec3(inversePoint / inversePoint.w); 


for (int i = 0; i < 8; ++i) 

    mFrustumCenter += mCorners[i]; 


mFrustumCenter /= 8.0f;

在我有了这个特定分割的截锥体中心之后,我需要找出我将用来渲染场景的光视图矩阵(在近和远之间)分裂),我这样做如下。 mRenderer->GetLightDirection() = 0.0f, 20.0f, -1.0f

glm::vec3 lightDir = glm::normalize(mRenderer->GetLightDirection()); 
glm::vec3 lightPos = mFrustumCenter + lightDir; 

mLightView = glm::lookAt(lightPos, mFrustumCenter , glm::vec3(0.0f, 1.0f, 0.0f));

最后我做的最后一件事是在我将它们转换为 光空间之后,使用 平截头体角计算 光的正交矩阵 > 使用光视图矩阵,我计算了上一步。

for (int i = 0; i < 8; ++i) 

    mCorners[i] = glm::vec3(mLightView * glm::vec4(mCorners[i], 1.0f)); 



float minX = std::numeric_limits<float>::max(); 
float maxX = std::numeric_limits<float>::min(); 
float minY = std::numeric_limits<float>::max(); 
float maxY = std::numeric_limits<float>::min(); 
float minZ = std::numeric_limits<float>::max();
float maxZ = std::numeric_limits<float>::min();

for (int i = 0; i < 8; ++i) 

    minX = std::min(minX, mCorners[i].x);
    maxX = std::max(maxX, mCorners[i].x);
    minY = std::min(minY, mCorners[i].y);
    maxY = std::max(maxY, mCorners[i].y);
    minZ = std::min(minZ, mCorners[i].z);
    maxZ = std::max(maxZ, mCorners[i].z);


mLightProj = glm::ortho(minX, maxX, minY, maxY, minZ, maxZ); 

当我运行我的程序时,我的影子工作正常

但是当我将相机向后移动直到地板进入第二个分割的范围而不是使用第二个分割阴影时它消失了但是当我开始上下移动相机时阴影再次出现所以我认为问题是在计算光视图矩阵,但我无法弄清楚。

这些是我的分裂范围 近->远 0.1 -> 30.0 0.1 -> 50.0 0.1 -> 1000.0

【问题讨论】:

【参考方案1】:

好吧,我发现我没有正确放置灯光,所以这个 sn-p

glm::vec3 lightDir = glm::normalize(mRenderer->GetLightDirection()); 
glm::vec3 lightPos = mFrustumCenter + lightDir; 

mLightView = glm::lookAt(lightPos, mFrustumCenter , glm::vec3(0.0f, 1.0f, 0.0f));

应该是

glm::vec3 lightDir = glm::normalize(mRenderer->GetLightDirection()); 
glm::vec3 lightPos = mFrustumCenter + lightDir * (mFar - mNear); 
mLightView = glm::lookAt(lightPos, mFrustumCenter , glm::vec3(0.0f, 1.0f, 0.0f));

i 应该将光源位置向另一个方向移动,其量等于当前分割平截头体的近端和远端之间的差异。 还有这个sn-p

float minX = std::numeric_limits<float>::max(); 
float maxX = std::numeric_limits<float>::min(); 
float minY = std::numeric_limits<float>::max(); 
float maxY = std::numeric_limits<float>::min(); 
float minZ = std::numeric_limits<float>::max();
float maxZ = std::numeric_limits<float>::min();

for (int i = 0; i < 8; ++i) 

    minX = std::min(minX, mCorners[i].x);
    maxX = std::max(maxX, mCorners[i].x);
    minY = std::min(minY, mCorners[i].y);
    maxY = std::max(maxY, mCorners[i].y);
    minZ = std::min(minZ, mCorners[i].z);
    maxZ = std::max(maxZ, mCorners[i].z);


mLightProj = glm::ortho(minX, maxX, minY, maxY, minZ, maxZ);

应该是

float minX = std::numeric_limits<float>::max(); 
float maxX = std::numeric_limits<float>::min(); 
float minY = std::numeric_limits<float>::max(); 
float maxY = std::numeric_limits<float>::min(); 
float minZ = std::numeric_limits<float>::max();
float maxZ = std::numeric_limits<float>::min();

for (int i = 0; i < 8; ++i) 

    minX = std::min(minX, mCorners[i].x);
    maxX = std::max(maxX, mCorners[i].x);
    minY = std::min(minY, mCorners[i].y);
    maxY = std::max(maxY, mCorners[i].y);
    minZ = std::min(minZ, mCorners[i].z);
    maxZ = std::max(maxZ, mCorners[i].z);


mLightProj = glm::ortho(minX, maxX, minY, maxY, near, maxZ - minZ);

光正交投影矩阵的近和应该等于分割截头体的近,远应该等于最近角和最远角(z轴)之间的距离

【讨论】:

以上是关于级联阴影贴图无法按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

确定级联阴影贴图分割范围

(阴影贴图)将深度缓冲区渲染到纹理并绘制它以检查它

提高点光源的阴影贴图性能?

全向阴影贴图

✠OpenGL-8-阴影

一步步学OpenGL 23 -《阴影贴图1》