我正在实现级联阴影贴图技术,但结果出乎意料
首先,我初始化缓冲区和纹理:
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glGenTextures(NUM_CASCADES, m_shadowMap);
for (uint i = 0; i < NUM_CASCADES; i++) {
glBindTexture(GL_TEXTURE_2D, m_shadowMap[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, size, size, 0, GL_DEPTH_COMPONENT,
GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_EQUAL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_shadowMap[i], 0);
}
glDrawBuffers(1, GL_NONE);
glReadBuffer(GL_NONE);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
然后我渲染到深度缓冲区: 在这里,我为每个级联计算光投影视图矩阵,然后从光透视图渲染场景。
glUseProgram(programID);
GLfloat minDistance = 0.0f;
GLfloat nearClip = camera->getProjection().getNear();
GLfloat farClip = camera->getProjection().getFar();
GLfloat cascadeSplits[NUM_CASCADES+1] = {nearClip,(farClip-nearClip)*0.08f,(farClip-nearClip)*0.2f,(farClip-nearClip)*0.6f,farClip};
for (unsigned int cascadeIterator = 0; cascadeIterator < NUM_CASCADES; ++cascadeIterator) {
GLfloat prevSplitDistance =
cascadeIterator == 0 ? minDistance : cascadeSplits[cascadeIterator - 1];
GLfloat splitDistance = cascadeSplits[cascadeIterator];
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->getProjection().getProjectionMatrix() * camera->getView().getViewMatrix());
for (unsigned int i = 0; i < 8; ++i) {
glm::vec4 inversePoint = invViewProj * glm::vec4(frustumCornersWS[i], 1.0f);
frustumCornersWS[i] = glm::vec3(inversePoint / inversePoint.w);
}
for (unsigned int i = 0; i < 4; ++i) {
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;
}
glm::vec3 frustumCenter = glm::vec3(0.0f);
for (unsigned int i = 0; i < 8; ++i)
frustumCenter += frustumCornersWS[i];
frustumCenter /= 8.0f;
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;
glm::vec3 maxExtents = glm::vec3(radius, radius, radius);
glm::vec3 minExtents = -maxExtents;
//Position the viewmatrix looking down the center of the frustum with an arbitrary lighht direction
glm::vec3 lightDirection =
frustumCenter - glm::normalize(light->getDirection()) * -minExtents.z;
glm::mat4 lightViewMatrix = glm::mat4(1.0f);
lightViewMatrix = glm::lookAt(lightDirection, frustumCenter, glm::vec3(0.0f, 1.0f, 0.0f));
glm::vec3 cascadeExtents = maxExtents - minExtents;
glm::mat4 lightOrthoMatrix = glm::ortho(minExtents.x, maxExtents.x, minExtents.y,
maxExtents.y, 0.0f, cascadeExtents.z);
// The rounding matrix that ensures that shadow edges do not shimmer
glm::mat4 shadowMatrix = lightOrthoMatrix * lightViewMatrix;
glm::vec4 shadowOrigin = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
shadowOrigin = shadowMatrix * shadowOrigin;
float mShadowMapSize = static_cast<float>(size);
shadowOrigin = shadowOrigin * mShadowMapSize / 2.0f;
glm::vec4 roundedOrigin = glm::round(shadowOrigin);
glm::vec4 roundOffset = roundedOrigin - shadowOrigin;
roundOffset = roundOffset * 2.0f / mShadowMapSize;
roundOffset.z = 0.0f;
roundOffset.w = 0.0f;
glm::mat4 shadowProj = lightOrthoMatrix;
shadowProj[3] += roundOffset;
lightOrthoMatrix = shadowProj;
//Store the split distances and the relevant matrices
const float clipDist = farClip - nearClip;
cascadeEndSpace[cascadeIterator] = (nearClip + splitDistance * clipDist) * -1.0f;
lightProjectionView[cascadeIterator] = lightOrthoMatrix * lightViewMatrix;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
glViewport(0, 0, mShadowMapSize, mShadowMapSize);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_shadowMap[cascadeIterator],0);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glCullFace(GL_FRONT);
glUniformMatrix4fv(glGetUniformLocation(programID, "lightProjectionView"), 1, GL_FALSE,
glm::value_ptr(lightProjectionView[cascadeIterator]));
for (Geometry::Object *object:objects) {
object->RenderToDepth(programID);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
glUseProgram(0);
最后我渲染场景:
for (uint i = 0; i < NUM_CASCADES; i++) {
glActiveTexture(GL_TEXTURE4 + i);
glBindTexture(GL_TEXTURE_2D, m_shadowMap[i]);
const char *shadowLoc = (const char *) ("map_shadow[" + Tools::ToString(i)+"]").c_str();
glUniform1i(glGetUniformLocation(programID, shadowLoc), 4 + i);
const char *lightLoc = (const char *) ("lightProjectionView[" + Tools::ToString(i) +
"]").c_str();
glUniformMatrix4fv(glGetUniformLocation(programID, lightLoc), 1, GL_FALSE,
glm::value_ptr(lightProjectionView[i]));
int cascadeSpaceLoc = glGetUniformLocation(programID, (const char *) ("cascadeEndSpace[" +
Tools::ToString(i) +
"]").c_str());
glUniform1f(cascadeSpaceLoc, cascadeEndSpace[i]);
}
最后,Shader方法是:
"float readShadowMap(){"
" float positiveViewSpaceZ = FViewPos.z;"
" int cascadeIdx = 0;"
" for(int i = 0; i < NUM_CASCADES - 1; ++i){"
" if(positiveViewSpaceZ < cascadeEndSpace[i]){"
" cascadeIdx = i + 1;"
" }"
" }"
" vec4 fragmentShadowPosition = LightSpacePos[cascadeIdx];"
" vec3 projCoords = fragmentShadowPosition.xyz / fragmentShadowPosition.w;"
" projCoords = projCoords * 0.5f + 0.5f;"
" float currentDepth = projCoords.z;"
" float pcfDepth = 0.0f;"
" if(cascadeIdx == 0)"
" pcfDepth = texture(map_shadow[0], projCoords.xy).x;"
" else if(cascadeIdx == 1)"
" pcfDepth = texture(map_shadow[1], projCoords.xy).x;"
" else if(cascadeIdx == 2)"
" pcfDepth = texture(map_shadow[2], projCoords.xy).x;"
" float shadow = currentDepth + 0.00001 > pcfDepth ? 0.5 : 1.0;"
" return shadow;"
"}"
我尝试更改纹理参数,但没有任何改变。
答案 0 :(得分:1)
您正在寻找的是一个通常被称为“阴影痤疮”的假象。您可以找到带有插图here的很好的解释。基本上,由于有限的分辨率和精度,发生的事情是对象表面的某些部分最终在自身上投射了阴影。对于从摄影机角度渲染的每个片段,将其位置投影到阴影贴图中并比较深度值。除非相机图像和阴影贴图的采样率完全匹配(除非进行大规模的过度采样,否则常规采样基本上不可能实现),否则会有多个片段最终投影到同一阴影贴图像素的区域。您的片段全部来自一个平面,该平面通常相对于相机以与朝向光线的角度不同。因此,您最终得到多个相邻片段,这些片段的深度都略有不同,但是所有贴图都映射到相同的阴影贴图像素,即与相同的深度值进行比较。这些片段中大约一半的深度小于阴影图中的像素,大约大于阴影图中的像素的一半。再加上一些舍入误差噪声,您将获得上面发布的图像。
解决此问题的经典方法是在渲染阴影贴图或相机图像时(例如,使用glPolygonOffset)应用倾斜比例的深度偏差:
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0f, 1.0f);
// render shadow map
glDisable(GL_POLYGON_OFFSET_FILL);
// render scene with shadows