C++ 比较阴影贴图矩阵和绘制矩阵不会产生阴影
C++ Comparing shadow map matrix with draw matrix doesn't create shadows
我正在尝试实现阴影贴图。我能够将深度图渲染到帧缓冲区纹理上。并将其发送到着色器,以及灯光正交矩阵以测试片段是否在阴影中。但结果不正确。如果它们看起来像 "inside" 光矩阵,那么所有片段都不会创建阴影,而是直接绘制。
正如您在左下角看到的,生成的深度纹理是正确的。
我试过很多教程,但 none 提到任何类似这样的问题。可能是光线方向和正射投影不对,但我好像找不到能产生阴影的工作光线方向。
这就是我生成帧缓冲区和纹理的方式(frameBuffer 和 framebufferTexture 在 class header 中实例化):
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
//texture buffer for frame buffer
glGenTextures(1, &framebufferTexture);
glBindTexture(GL_TEXTURE_2D, framebufferTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 896, 504, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
//Set framebufferTexture as our color attachment #0
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D, framebufferTexture, 0);
glDrawBuffer(GL_NONE);
这就是我创建深度纹理的方式
glCullFace(GL_BACK);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer);
//Setup depth texture framebuffer rendering
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(2.0f, 2.0f);
glViewport(0, 0, 896, 504);
glClear(GL_DEPTH_BUFFER_BIT);
glUniform1i(glGetUniformLocation(shaderProgram, "fragmentTypeTarget"), -1);
glm::vec3 lightInvDir = glm::vec3(5, 17, 1);
//Calculate the matrix for drawing the framebuffer
//Make it an orthographic that takes the whole screen
glm::mat4 depthProjectionMatrix = glm::ortho<float>(-17, 17, -17, 17, 0, 37);
glm::mat4 depthViewMatrix = glm::lookAt(
lightInvDir,
glm::vec3(0, 0, 0),
glm::vec3(0, 1, 0));
glm::mat4 lightSpaceMatrix = depthProjectionMatrix * depthViewMatrix;
//Render the objects
for (int i = 0; i < objectPool.size(); i++) {
objectPool[i]->draw(lightSpaceMatrix);
}
这是我发送用于绘制阴影的阴影贴图矩阵的方式:
glm::mat4 Model = glm::translate(glm::mat4(1.0), glm::vec3(x, y, z));
glm::vec3 lightInvDir = glm::vec3(5, 17, 1);
//Calculate the matrix for drawing the framebuffer
//Make it an orthographic that takes the whole screen
glm::mat4 depthProjectionMatrix = glm::ortho<float>(-17, 17, -17, 17, 0, 37);
glm::mat4 depthViewMatrix = glm::lookAt(
lightInvDir,
glm::vec3(0, 0, 0),
glm::vec3(0, 1, 0));
glm::mat4 lightSpaceMatrix = depthProjectionMatrix * depthViewMatrix * Model;
glUniformMatrix4fv(glGetUniformLocation(shader, "shadowMatrix"), 1, GL_FALSE, &lightSpaceMatrix[0][0]);
我的片段着色器:
#version 330 core
layout(location = 0) out vec4 outColor;
in vec2 UV;
in vec4 ShadowCoord;
uniform sampler2D textureSampler;
uniform sampler2D shadowMap;
uniform int fragmentTypeTarget;
// 'colorImage' is a sampler2D with the depth image
// read from the current depth buffer bound to it.
float LinearizeDepth(in vec2 uv, sampler2D t)
{
float zNear = 1.0;
float zFar = 5.0;
float depth = texture2D(t, uv).x;
return (2.0 * zNear) / (zFar + zNear - depth * (zFar - zNear));
}
void main(){
if(fragmentTypeTarget == -1){//draw frame buffer
}else if(fragmentTypeTarget == 0){//Draw everything normal
vec4 tex = texture2D(textureSampler, UV);
outColor = vec4(tex.rgb, tex.a);
}else if(fragmentTypeTarget == 1){//Draw depth texture
//vec2 res = gl_FragCoord.xy / vec2(1024, 1024);
outColor = texture(textureSampler, UV);
float c = LinearizeDepth(UV, textureSampler);
outColor = vec4(c, c, c, 1.0);
}else if(fragmentTypeTarget == 2){//Draw everything but apply the shadow
float visibility = 1.0;
float bias = 0.0039;
if(texture(shadowMap, ShadowCoord.xy).z < ShadowCoord.z){
visibility = 0.3;
}
vec4 tex = texture2D(textureSampler, UV);
outColor = visibility * vec4(tex.rgb, tex.a);
}
}
我的顶点着色器:
#version 330 core
in vec2 UVsIn;
out vec2 UV;
in vec3 position;
in vec3 vertexColor;
out vec3 fragmentColor;
uniform mat4 mvp;
uniform mat4 shadowMatrix;
out vec4 ShadowCoord;
void main(){
gl_Position = mvp * vec4(position, 1.0);
fragmentColor = vertexColor;
UV = UVsIn;
ShadowCoord = shadowMatrix * vec4(position, 1.0);
}
结果应该更像这样:
编辑:
我想我让它工作了。
在生成帧缓冲区纹理时,我使用了错误的比较函数。它应该是:"GL_COMPARE_R_TO_TEXTURE" 而不是 "GL_COMPARE_REF_TO_TEXTURE".
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
openGl 文档说:GL_COMPARE_R_TO_TEXTURE:指定插值和钳位的 r 纹理坐标应与当前绑定的深度纹理中的值进行比较
这正是我想要的。
我还发现纹理坐标在 (s, t, r) 而不是 (x, y, z)。
因为我想比较矩阵和纹理的深度,所以我需要使用 r 而不是 z。
所以片段着色器应该这样写:
float visibility = 1.0;
vec2 mapUV = ShadowCoord.xy * 0.5 + 0.5;
float depthFrag = ShadowCoord.z * 0.5 + 0.5;
float depthMap = texture(shadowMap, mapUV).r;//r instead of z since its (s, t, r)
if(depthMap < depthFrag){
visibility = 0.3;
}
vec4 tex = texture2D(textureSampler, UV);
outColor = visibility * vec4(tex.rgb, tex.a);
还需要从 Rabbid76 响应中添加“* 0.5 + 0.5”。
阴影贴图的内容是深度范围 [0.0, 1.0] 中的深度值(除非您将深度范围更改为 glDepthRange
)
ShadowCoord
是剪辑 space 坐标。剪辑 space 坐标可以通过 Perspective divide (ShadowCoord.xyz/ShadowCoord.w
) 转换为规范化设备 space 坐标。由于阴影投影是正交的,这个可以跳过,因为ShadowCoord.w == 1.0
。
标准化设备 space 坐标在 [-1.0, 1.0] 范围内。
所以它必须是:
vec2 mapUV = ShadowCoord.xy * 0.5 + 0.5; // [-1.0, 1.0] -> [0.0, 1.0]
float depthFrag = ShadowCoord.z * 0.5 + 0.5; // [-1.0, 1.0] -> [0.0, 1.0]
float depthMap = texture(shadowMap, mapUV).r;
if(depthMap < depthFrag) {
visibility = 0.3;
}
我正在尝试实现阴影贴图。我能够将深度图渲染到帧缓冲区纹理上。并将其发送到着色器,以及灯光正交矩阵以测试片段是否在阴影中。但结果不正确。如果它们看起来像 "inside" 光矩阵,那么所有片段都不会创建阴影,而是直接绘制。
正如您在左下角看到的,生成的深度纹理是正确的。
我试过很多教程,但 none 提到任何类似这样的问题。可能是光线方向和正射投影不对,但我好像找不到能产生阴影的工作光线方向。
这就是我生成帧缓冲区和纹理的方式(frameBuffer 和 framebufferTexture 在 class header 中实例化):
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
//texture buffer for frame buffer
glGenTextures(1, &framebufferTexture);
glBindTexture(GL_TEXTURE_2D, framebufferTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 896, 504, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
//Set framebufferTexture as our color attachment #0
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D, framebufferTexture, 0);
glDrawBuffer(GL_NONE);
这就是我创建深度纹理的方式
glCullFace(GL_BACK);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer);
//Setup depth texture framebuffer rendering
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(2.0f, 2.0f);
glViewport(0, 0, 896, 504);
glClear(GL_DEPTH_BUFFER_BIT);
glUniform1i(glGetUniformLocation(shaderProgram, "fragmentTypeTarget"), -1);
glm::vec3 lightInvDir = glm::vec3(5, 17, 1);
//Calculate the matrix for drawing the framebuffer
//Make it an orthographic that takes the whole screen
glm::mat4 depthProjectionMatrix = glm::ortho<float>(-17, 17, -17, 17, 0, 37);
glm::mat4 depthViewMatrix = glm::lookAt(
lightInvDir,
glm::vec3(0, 0, 0),
glm::vec3(0, 1, 0));
glm::mat4 lightSpaceMatrix = depthProjectionMatrix * depthViewMatrix;
//Render the objects
for (int i = 0; i < objectPool.size(); i++) {
objectPool[i]->draw(lightSpaceMatrix);
}
这是我发送用于绘制阴影的阴影贴图矩阵的方式:
glm::mat4 Model = glm::translate(glm::mat4(1.0), glm::vec3(x, y, z));
glm::vec3 lightInvDir = glm::vec3(5, 17, 1);
//Calculate the matrix for drawing the framebuffer
//Make it an orthographic that takes the whole screen
glm::mat4 depthProjectionMatrix = glm::ortho<float>(-17, 17, -17, 17, 0, 37);
glm::mat4 depthViewMatrix = glm::lookAt(
lightInvDir,
glm::vec3(0, 0, 0),
glm::vec3(0, 1, 0));
glm::mat4 lightSpaceMatrix = depthProjectionMatrix * depthViewMatrix * Model;
glUniformMatrix4fv(glGetUniformLocation(shader, "shadowMatrix"), 1, GL_FALSE, &lightSpaceMatrix[0][0]);
我的片段着色器:
#version 330 core
layout(location = 0) out vec4 outColor;
in vec2 UV;
in vec4 ShadowCoord;
uniform sampler2D textureSampler;
uniform sampler2D shadowMap;
uniform int fragmentTypeTarget;
// 'colorImage' is a sampler2D with the depth image
// read from the current depth buffer bound to it.
float LinearizeDepth(in vec2 uv, sampler2D t)
{
float zNear = 1.0;
float zFar = 5.0;
float depth = texture2D(t, uv).x;
return (2.0 * zNear) / (zFar + zNear - depth * (zFar - zNear));
}
void main(){
if(fragmentTypeTarget == -1){//draw frame buffer
}else if(fragmentTypeTarget == 0){//Draw everything normal
vec4 tex = texture2D(textureSampler, UV);
outColor = vec4(tex.rgb, tex.a);
}else if(fragmentTypeTarget == 1){//Draw depth texture
//vec2 res = gl_FragCoord.xy / vec2(1024, 1024);
outColor = texture(textureSampler, UV);
float c = LinearizeDepth(UV, textureSampler);
outColor = vec4(c, c, c, 1.0);
}else if(fragmentTypeTarget == 2){//Draw everything but apply the shadow
float visibility = 1.0;
float bias = 0.0039;
if(texture(shadowMap, ShadowCoord.xy).z < ShadowCoord.z){
visibility = 0.3;
}
vec4 tex = texture2D(textureSampler, UV);
outColor = visibility * vec4(tex.rgb, tex.a);
}
}
我的顶点着色器:
#version 330 core
in vec2 UVsIn;
out vec2 UV;
in vec3 position;
in vec3 vertexColor;
out vec3 fragmentColor;
uniform mat4 mvp;
uniform mat4 shadowMatrix;
out vec4 ShadowCoord;
void main(){
gl_Position = mvp * vec4(position, 1.0);
fragmentColor = vertexColor;
UV = UVsIn;
ShadowCoord = shadowMatrix * vec4(position, 1.0);
}
结果应该更像这样:
编辑:
我想我让它工作了。 在生成帧缓冲区纹理时,我使用了错误的比较函数。它应该是:"GL_COMPARE_R_TO_TEXTURE" 而不是 "GL_COMPARE_REF_TO_TEXTURE".
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
openGl 文档说:GL_COMPARE_R_TO_TEXTURE:指定插值和钳位的 r 纹理坐标应与当前绑定的深度纹理中的值进行比较
这正是我想要的。
我还发现纹理坐标在 (s, t, r) 而不是 (x, y, z)。
因为我想比较矩阵和纹理的深度,所以我需要使用 r 而不是 z。 所以片段着色器应该这样写:
float visibility = 1.0;
vec2 mapUV = ShadowCoord.xy * 0.5 + 0.5;
float depthFrag = ShadowCoord.z * 0.5 + 0.5;
float depthMap = texture(shadowMap, mapUV).r;//r instead of z since its (s, t, r)
if(depthMap < depthFrag){
visibility = 0.3;
}
vec4 tex = texture2D(textureSampler, UV);
outColor = visibility * vec4(tex.rgb, tex.a);
还需要从 Rabbid76 响应中添加“* 0.5 + 0.5”。
阴影贴图的内容是深度范围 [0.0, 1.0] 中的深度值(除非您将深度范围更改为 glDepthRange
)
ShadowCoord
是剪辑 space 坐标。剪辑 space 坐标可以通过 Perspective divide (ShadowCoord.xyz/ShadowCoord.w
) 转换为规范化设备 space 坐标。由于阴影投影是正交的,这个可以跳过,因为ShadowCoord.w == 1.0
。
标准化设备 space 坐标在 [-1.0, 1.0] 范围内。
所以它必须是:
vec2 mapUV = ShadowCoord.xy * 0.5 + 0.5; // [-1.0, 1.0] -> [0.0, 1.0]
float depthFrag = ShadowCoord.z * 0.5 + 0.5; // [-1.0, 1.0] -> [0.0, 1.0]
float depthMap = texture(shadowMap, mapUV).r;
if(depthMap < depthFrag) {
visibility = 0.3;
}