我试图把我的头围绕级联阴影贴图,我遇到了一个问题与近场剔除。基本上,相机后面的对象被剔除,分割的阴影也被剔除。请看下面的图片。
的数据
我找到了一种方法来解决这个问题,做glEnable(GL_DEPTH_CLAMP);
之前的阴影通行证,但现在我有一些其他的问题,我们需要的远和近剪辑剔除。Sponza模型就是一个例子,看起来里面的所有东西都在阴影中结束,因为即使光线应该到达里面的物体,墙也会出现在阴影贴图中。
你们是如何处理这个问题的?
下面是我如何生成阴影贴图的投影视图矩阵
float clipRange = camera->getFarClipPlane() - camera->getNearClipPlane();
float minZ = camera->getNearClipPlane();
float maxZ = camera->getNearClipPlane() + clipRange;
float range = maxZ - minZ;
float ratio = maxZ / minZ;
float cascadeSplitLambda = 0.95f;
// Calculate split depths based on view camera furstum
// Based on method presentd in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html
cascadeSplits.clear();
cascadeSplitsNew.clear();
for (int i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++) {
auto i_float = static_cast<GLfloat>(i);
float p = (i_float + 1.f) / static_cast<float>(SHADOW_MAP_CASCADE_COUNT);
float log = minZ * std::pow(ratio, p);
float uniform = minZ + range * p;
float d = cascadeSplitLambda * (log - uniform) + uniform;
GLfloat cascadeSplit = (d - camera->getNearClipPlane()) / clipRange;
cascadeSplits.push_back(cascadeSplit);
}
directionalLightShadowVPMatrixes.clear();
for (int casscadeIndex = 0; casscadeIndex < SHADOW_MAP_CASCADE_COUNT; casscadeIndex++) {
glm::vec3 frustumCornersWS[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),
};
glm::mat4 invViewProj = glm::inverse(camera->getProjectionMatrix() * camera->getViewMatrix());
for (unsigned int i = 0; i < 8; i++) {
glm::vec4 inversePoint = invViewProj * glm::vec4(frustumCornersWS[i], 1.0f);
frustumCornersWS[i] = inversePoint / inversePoint.w;
//std::cout << "FrustumCorner oldValues" << i << " is: " << glm::to_string(frustumCornersWS[i]) << std::endl;
}
// TODO: Modify this if doing all 4 splits
for (unsigned int i = 0; i < 4; i++) {
GLfloat prevSplitDistance;
if (casscadeIndex == 0) {
prevSplitDistance = 0.1f / 1000.f;
} else {
prevSplitDistance = cascadeSplits.at(casscadeIndex - 1);
}
GLfloat splitDistance = cascadeSplits.at(casscadeIndex);
glm::vec3 cornerRay = frustumCornersWS[i + 4] - frustumCornersWS[i];
glm::vec3 nearCornerRay = cornerRay * prevSplitDistance;
glm::vec3 farCornerRay = cornerRay * splitDistance;
frustumCornersWS[i + 4] = frustumCornersWS[i] + farCornerRay;
frustumCornersWS[i] = frustumCornersWS[i] + nearCornerRay;
}
for (unsigned int i = 0; i < 8; i++) {
// std::cout << "FrustumCorner new values" << i << " is: " << glm::to_string(frustumCornersWS[i]) << std::endl;
}
// Finding frustum center by adding 8 corners and dividing by 8
glm::vec3 frustumCenter = glm::vec3(0.0f);
for (unsigned int i = 0; i < 8; i++) {
frustumCenter += frustumCornersWS[i];
}
frustumCenter /= 8.0f;
//std::cout << "FrustumCenter is at: " << glm::to_string(frustumCenter) << std::endl;
// Building a sphere that encapsulates the view frustum
GLfloat radius = 0.0f;
for (unsigned int i = 0; i < 8; i++) {
GLfloat distance = glm::length(frustumCornersWS[i] - frustumCenter);
radius = glm::max(radius, distance);
}
radius = std::ceil(radius * 16.0f) / 16.0f;
// std::cout << "Radius is: " << radius << std::endl;
glm::vec3 lightDirection = frustumCenter - glm::normalize(lights.at(0)->getDirection()) * radius;
glm::mat4 lightViewMatrix = glm::mat4(1.0f);
lightViewMatrix = glm::lookAt(lightDirection, frustumCenter, glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 lightOrthoMatrix = glm::ortho(-radius, radius, -radius, radius, 0.f, 2.f * radius);
// TODO: Maybe calculate splits like this (I believe this is in world pos)
//std::cout << "split is: " << (camera->getNearClipPlane() + cascadeSplits.at(casscadeIndex) * clipRange) * -1.0f << std::endl;
cascadeSplitsNew.push_back((camera->getNearClipPlane() + cascadeSplits.at(casscadeIndex) * clipRange));
// This is used to avoid shimmering when we move the camera
glm::mat4 shadowMatrix = lightOrthoMatrix * lightViewMatrix;
glm::vec4 shadowOrigin = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
shadowOrigin = shadowMatrix * shadowOrigin;
shadowOrigin = shadowOrigin * static_cast<GLfloat>(directionalLightShadowFrameBuffer->getResolutionX()) / 2.0f;
glm::vec4 roundedOrigin = glm::round(shadowOrigin);
glm::vec4 roundOffset = roundedOrigin - shadowOrigin;
roundOffset = roundOffset * 2.0f / static_cast<GLfloat>(directionalLightShadowFrameBuffer->getResolutionX());
roundOffset.z = 0.0f;
roundOffset.w = 0.0f;
glm::mat4 shadowProj = lightOrthoMatrix;
shadowProj[3] += roundOffset;
lightOrthoMatrix = shadowProj;
glm::mat4 directionalLightShadowVPMatrix = lightOrthoMatrix * lightViewMatrix;
directionalLightShadowVPMatrixes.push_back(directionalLightShadowVPMatrix);
字符集
1条答案
按热度按时间7gs2gvoe1#
正如您所发现的,深度夹紧是解决此问题的一种方法。
另一种方法是将灯光视图矩阵从
frustumCenter
移开。首先,你的
glm::lookAt
不对。第一个参数是眼睛的位置,而不是光线的方向。当您移动相机时,灯光视图矩阵应该跟随您。若要不裁剪场景中的树,需要将眼睛位置从中心移开。举例来说:
字符集
可能需要增加
glm::ortho()
函数中最后一个参数的远平面距离。取决于你的片段着色器是如何计算阴影贴图的,这应该不会导致它停止工作,只要深度贴图的中间点在精确的中心(即深度图中的
0.5f
值应该指向frustumCenter
)。您还可以根据场景边界计算近平截头体和远平截头体,如此MSDN页面所述:改进阴影深度贴图的常用技术(部分:光平截体与场景相交计算远近平面
在我自己的Vulkan引擎中,我遵循了SaschaWillems的shadowmappingcascade示例,并最终得到如下结果:
型
**这不是解决此问题的正确方法,但它有效。**推荐的方法是MSDN页面中描述的方法。