OpenGL阴影贴图 - 阴影贴图纹理根本没有被采样?

时间:2017-09-28 20:56:54

标签: c++ opengl graphics 3d glsl

我目前正在开发一个OpenGL项目,我正在尝试使阴影贴图正常工作。我可以到达阴影贴图渲染到纹理中的点,但在渲染时似乎不会应用于场景。这是我的代码中最重要的部分:

阴影贴图顶点着色器,基本上是一个简单的通过着色器(也做一些像法线一样的额外的东西,但这不应该分散你的注意力);它基本上只是转换顶点,所以它们是从光的角度看到的(它是一个定向光,但由于我们需要假设一个位置,它基本上是一个远点):

#version 430 core

layout(location = 0) in vec3 v_position;
layout(location = 1) in vec3 v_normal;
layout(location = 2) in vec3 v_texture;
layout(location = 3) in vec4 v_color;

out vec3 f_texture;
out vec3 f_normal;
out vec4 f_color;

uniform mat4 modelMatrix;
uniform mat4 depthViewMatrix;
uniform mat4 depthProjectionMatrix;

// Shadow map vertex shader.
void main() {
    mat4 mvp = depthProjectionMatrix * depthViewMatrix * modelMatrix;
    gl_Position = mvp * vec4(v_position, 1.0);

    // Passing attributes on to the fragment shader
    f_texture = v_texture;
    f_normal = (transpose(inverse(modelMatrix)) * vec4(v_normal, 1.0)).xyz;
    f_color = v_color;
}

将深度值写入纹理的阴影贴图片段着色器:

#version 430 core

layout(location = 0) out float fragmentDepth;

in vec3 f_texture;
in vec3 f_normal;
in vec4 f_color;

uniform vec3 lightDirection;
uniform sampler2DArray texSampler;

// Shadow map fragment shader.
void main() {
  fragmentDepth = gl_FragCoord.z;
}

实际渲染场景的顶点着色器,还可以从灯光视角(shadowCoord)计算当前顶点的位置,以与深度纹理进行比较;它也应用偏差矩阵,因为坐标不在正确的[0,1]间隔内进行采样:

#version 430 core

layout(location = 0) in vec3 v_position;
layout(location = 1) in vec3 v_normal;
layout(location = 2) in vec3 v_texture;
layout(location = 3) in vec4 v_color;

out vec3 f_texture;
out vec3 f_normal;
out vec4 f_color;
out vec3 f_shadowCoord;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

uniform mat4 depthViewMatrix;
uniform mat4 depthProjectionMatrix;

// Simple vertex shader.
void main() {
    mat4 mvp = projectionMatrix * viewMatrix * modelMatrix;
    gl_Position = mvp * vec4(v_position, 1.0);


    // This bias matrix adjusts the projection of a given vertex on a texture to be within 0 and 1 for proper sampling
    mat4 depthBias = mat4(0.5, 0.0, 0.0, 0.5,
                          0.0, 0.5, 0.0, 0.5,
                          0.0, 0.0, 0.5, 0.5,
                          0.0, 0.0, 0.0, 1.0);

    mat4 depthMVP = depthProjectionMatrix * depthViewMatrix * modelMatrix;
    mat4 biasedDMVP = depthBias * depthMVP;

    // Passing attributes on to the fragment shader
    f_shadowCoord = (biasedDMVP * vec4(v_position, 1.0)).xyz;
    f_texture = v_texture;
    f_normal = (transpose(inverse(modelMatrix)) * vec4(v_normal, 1.0)).xyz;
    f_color = v_color;
}                       

片段着色器,它应用纹理数组中的纹理并接收深度纹理(uniform sampler2D shadowMap)并检查片段是否落后于某些东西:

#version 430 core

in vec3 f_texture;
in vec3 f_normal;
in vec4 f_color;
in vec3 f_shadowCoord;

out vec4 color;

uniform vec3 lightDirection;
uniform sampler2D shadowMap;
uniform sampler2DArray tileTextureArray;

// Very basic fragment shader.
void main() {
        float visibility = 1.0;
        if (texture(shadowMap, f_shadowCoord.xy).z < f_shadowCoord.z) {
            visibility = 0.5;
        }

        color = texture(tileTextureArray, f_texture) * visibility;
}

最后:渲染多个块以生成阴影贴图然后使用阴影贴图渲染场景的函数:

// Generating the shadow map
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_depthTexture);

m_shadowShader->bind();
glViewport(0, 0, 1024, 1024);
glDisable(GL_CULL_FACE);

glm::vec3 lightDir = glm::vec3(1.0f, -0.5f, 1.0f);
glm::vec3 sunPosition = FPSCamera::getPosition() - lightDir * 64.0f;
glm::mat4 depthViewMatrix = glm::lookAt(sunPosition, FPSCamera::getPosition(), glm::vec3(0, 1, 0));
glm::mat4 depthProjectionMatrix = glm::ortho<float>(-100.0f, 100.0f, -100.0f, 100.0f, 0.1f, 800.0f);

m_shadowShader->setUniformMatrix("depthViewMatrix", depthViewMatrix);
m_shadowShader->setUniformMatrix("depthProjectionMatrix", depthProjectionMatrix);

for (Chunk *chunk : m_chunks) {
  m_shadowShader->setUniformMatrix("modelMatrix", chunk->getModelMatrix());
  chunk->drawElements();
}

m_shadowShader->unbind();
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Normal draw call
m_chunkShader->bind();
glEnable(GL_CULL_FACE);
glViewport(0, 0, Window::getWidth(), Window::getHeight());
glm::mat4 viewMatrix = FPSCamera::getViewMatrix();
glm::mat4 projectionMatrix = FPSCamera::getProjectionMatrix();

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_depthTexture);
glActiveTexture(GL_TEXTURE1);
m_textures->bind();
m_chunkShader->setUniformMatrix("depthViewMatrix", depthViewMatrix);
m_chunkShader->setUniformMatrix("depthProjectionMatrix", depthProjectionMatrix);
m_chunkShader->setUniformMatrix("viewMatrix", viewMatrix);
m_chunkShader->setUniformMatrix("projectionMatrix", projectionMatrix);
m_chunkShader->setUniformVec3("lightDirection", lightDir);
m_chunkShader->setUniformInteger("shadowMap", 0);
m_chunkShader->setUniformInteger("tileTextureArray", 1);

for (Chunk *chunk : m_chunks) {
  m_chunkShader->setUniformMatrix("modelMatrix", chunk->getModelMatrix());
  chunk->drawElements();
}

大多数代码应该是不言自明的,我绑定了一个附加了纹理的FBO,我们对framebuffer进行了正常的渲染调用,它被渲染成一个纹理,然后我试图将它传递给用于正常渲染的着色器。我已经测试了纹理是否正确生成并且确实如此:在此处查看生成的阴影贴图See the generated shadow map here

但是,在渲染场景时,我看到的只有这一点。
this
没有应用阴影,可见性到处都是1.0。我还使用了一个正常工作的调试上下文,并在有任何错误时记录错误,但它似乎完全正常,没有警告或错误,所以我在这里做了一件非常错误的事情。顺便说一下,我正在使用OpenGL 4.3。

希望你们其中一个可以帮我解决这个问题,我以前从来没有使用阴影贴图,这是我最接近过的,哈哈。提前谢谢。

1 个答案:

答案 0 :(得分:0)

通常mat4 OpenGL转换矩阵如下所示:

( X-axis.x, X-axis.y, X-axis.z, 0 )
( Y-axis.x, Y-axis.y, Y-axis.z, 0 )
( Z-axis.x, Z-axis.y, Z-axis.z, 0 )
( trans.x,  trans.y,  trans.z,  1 ) 

因此,用于将标准化设备坐标(在ranage [-1,1]中)转换为纹理坐标(在范围[0,1]中)的depthBias矩阵应如下所示:< / p>

mat4 depthBias = mat4(0.5, 0.0, 0.0, 0.0,
                      0.0, 0.5, 0.0, 0.0,
                      0.0, 0.0, 0.5, 0.0,
                      0.5, 0.5, 0.5, 1.0); 

或者这个:

mat4 depthBias = mat4(
    vec4( 0.5, 0.0, 0.0, 0.0 ),
    vec4( 0.0, 0.5, 0.0, 0.0 ),
    vec4( 0.0, 0.0, 0.5, 0.0 ),
    vec4( 0.5, 0.5, 0.5, 1.0 ) ); 


通过模型矩阵,视图矩阵和投影矩阵变换顶点位置后,顶点位置位于剪辑空间(homogeneous coordinates)中。您必须从剪辑空间转换为规范化设备坐标(范围[-1,1]中的cartesian coordinates)。这可以通过将homogeneous coordinate

w组件除以来完成
mat4 depthMVP  = depthProjectionMatrix * depthViewMatrix * modelMatrix;
vec4 clipPos   = depthMVP * vec4(v_position, 1.0);
vec4 ndcPos    = vec4(clipPos.xyz / clipPos.w, 1.0);
f_shadowCoord  = (depthBias * ndcPos).xyz;


深度纹理仅具有一个通道。如果从深度纹理中读取数据,则数据包含在向量的x(或r)分量中。

调整片段着色器代码,如下所示:

if ( texture(shadowMap, f_shadowCoord.xy).x < f_shadowCoord.z) 
    visibility = 0.5;

Khronos集团的Image Format规范说:

  

图像格式不必存储每个组件。当着色器   对这样的纹理进行采样,它仍将解析为4值RGBA   向量。未填写图像格式的组件   自动。如果缺少R,G或B,则使用零,而a   失踪Alpha总是解析为1。


进一步了解:


除了解决方案:

这是解决方案的一个重要部分,但还需要另一个步骤来正确渲染阴影贴图。第二个错误是使用纹理的错误组件来比较f_shadowCoord.z:它应该是

texture(shadowMap, f_shadowCoord.xy).r

而不是

texture(shadowMap, f_shadowCoord.xy).z