奇怪的OpenGL阴影映射行为

8
我正在使用C++和OpenGL 3.2以及SFML开发一个3D游戏。我一直在努力实现点光源阴影映射,但是似乎我的做法符合我学到的和看到的示例,但仍然没有阴影。
我已经完成的工作是将我使用的所有代码按照精简的列表形式,按照使用顺序排列,而不是完整的源代码,只包含相关的代码(因为我的项目被分成了几个类):
Omnidirectional shadow mapping


C++
- Initialization
-- Use shadow pass shader program
-- Generate + bind the shadow frame buffer
glGenFramebuffers(1, &shadowFrameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, shadowFrameBuffer);


-- Generate a texture
glGenTextures(1, &shadowMap);

-- Bind texture as cubemap
glBindTexture(GL_TEXTURE_CUBE_MAP);

-- Set texture parameters
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

-- Generate empty 1024 x 1024 for every face of the cube
for (int face = 0; face < 6; face++)
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_DEPTH_COMPONENT32F , 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

-- Attach the cubemap to the framebuffer
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowMap, 0);

-- Only draw depth to framebuffer
glDrawBuffer(GL_NONE);

- Every frame
-- Clear screen
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

-- Render shadow map
--- Bind shadow frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, shadowFrameBuffer);

--- Set the viewport to the size of the shadow map
glViewport(0, 0, 1024, 1024);

-- Cull front faces
glCullFace(GL_FRONT);

-- Use shadow mapping program
--- Define projection matrix for rendering each face
glm::mat4 depthProjectionMatrix = glm::perspective(90.0f, 1.0f, 1.0f, 10.0f);

--- Define view matrices for all six faces
std::vector<glm::mat4> depthViewMatrices;

depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(1,0,0),  glm::vec3(0,-1,0) )); // +X
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(-1,0,0), glm::vec3(0,1,0) )); // -X
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(0,1,0),  glm::vec3(0,0,1)  )); // +Y
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(0,-1,0), glm::vec3(0,0,-1) )); // -Y
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(0,0,1),  glm::vec3(0,-1,0) )); // +Z
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(0,0,-1), glm::vec3(0,1,0)  )); // -Z

--- For every object in the scene
---- Bind the VBO of the object
---- Define the model matrix for the object based on its position and orientation
---- For all six sides of the cube
----- Set the correct side to render to
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, shadowMap, 0);

----- Clear depth buffer
glClear(GL_DEPTH_BUFFER_BIT);

----- Send model, view and projection matrices to shadow mapping shader
glUniformMatrix4fv(glGetUniformLocation(shadowMapper, "lightModelMatrix"), 1, GL_FALSE, glm::value_ptr(depthModelMatrix));
glUniformMatrix4fv(glGetUniformLocation(shadowMapper, "lightViewMatrix"), 1, GL_FALSE, glm::value_ptr(depthViewMatrices[i]));
glUniformMatrix4fv(glGetUniformLocation(shadowMapper, "lightProjectionMatrix"), 1, GL_FALSE, glm::value_ptr(depthProjectionMatrix));

----- Draw the object
glDrawElements(....);
- END SHADOW MAP DRAW
-- Cull back faces
glCullFace(GL_BACK);

-- Use standard shader program
-- Bind default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);

-- Activate cubemap texture
glActiveTexture(GL_TEXTURE1);

-- Bind cubemap texture
glBindTexture(GL_TEXTURE_CUBE_MAP, shadowMap);

-- Tell shader to use first texture
glUniform1i(glGetUniformLocation(currentProgram->id, "shadowmap"), 1);

-- Send standard MVPs and draw objects
glDrawElements(...);

- END C++

=================================
GLSL

shadowpass vertex shader source

#version 150

in vec3 position;
out vec3 worldPosition;

uniform mat4 lightModelMatrix;
uniform mat4 lightViewMatrix;
uniform mat4 lightProjectionMatrix;

void main()
{
    gl_Position = lightProjectionMatrix * lightViewMatrix * lightModelMatrix * vec4(position, 1.0);
    worldPosition = (lightModelMatrix * vec4(position, 1.0)).xyz; // Send world position of vertex to fragment shader
}


shadowpass fragment shader source

#version 150

in vec3 worldPosition; // Vertex position in world space
out float distance; // Distance from vertex position to light position
vec3 lightWorldPosition = vec3(0.0, 0.0, 0.0); // Light position in world space

void main()
{
    distance = length(worldPosition - lightWorldPosition); // Distance from point to light
    // Distance will be written to the cubemap
}

standard vertex shader source

#version 150

in vec3 position;
in vec3 normal;
in vec2 texcoord;

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

out vec3 fragnormal;
out vec3 fragnormaldirection;
out vec2 fragtexcoord;
out vec4 fragposition;
out vec4 fragshadowcoord;

void main()
{
    fragposition = vec4(position, 1.0); // Position of vertex in object space
    fragtexcoord = texcoord;
    fragnormaldirection = normalize(modelInverseTranspose * normal);
    fragnormal = normalize(normal);


    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
}

standard fragment shader source

#version 150

out vec4 outColour;

in vec3 fragnormaldirection;
in vec2 fragtexcoord;
in vec3 fragnormal;
in vec4 fragposition;

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

uniform sampler2D tex;
uniform samplerCube shadowmap;

void main()
{   
    vec3 lightpos = vec3(0.0, 0.0, 0.0);

    vec3 pointToLight = (fragposition * modelMatrix).xyz - lightpos; // Get vector between this point and the light

    float dist = texture(shadowmap, pointToLight).x; // Get distance written in texture

    float shadowfactor = 1.0;

    if (length(pointToLight) > dist) // Is it occluded?
        shadowfactor = 0.5;

    outColour = texture(tex, fragtexcoord) * shadowfactor;
}

以下是我的代码所做的事情的图片:

破损的阴影映射

这是一种奇怪的效果,但似乎接近我想要的效果。看起来0,0,0处暴露在光线下的任何表面都会有一个中心无阴影的圆形,而其他所有表面则没有阴影。


我提供了链接,因为我认为完整的页面文件会更易读,genpfault。 - toficofi
2
Pastebins会过期,而SO不会。 - genpfault
每个立方体面的渲染深度图看起来正确吗? - geometrian
@IanMallet 我没检查过,糟糕。一旦我有机会,我会尝试渲染这些面孔。 - toficofi
1个回答

4
一种非常有用的调试阴影贴图的方法是以四边形的形式在屏幕上显示阴影贴图的内容。对于立方体阴影贴图,需要显示6个四边形。这可以作为一个调试彩蛋来实现,您可以在整个屏幕上显示完整的纹理,并且通过另一个按键组合“转到下一个面”,以便您可以查看6个面。
然后,立方体阴影贴图中最重要的事情之一是深度范围。点光源没有无限范围,因此通常您希望将深度存储缩放以匹配光源范围。
您可以使用浮点16位亮度(或红色通道)纹理来存储世界深度(球形,意味着使用像素着色器中的一些计算来计算真实长度(射线到交点))。或者,您可以使用线性深度(与经典Z缓冲区中存储的相同类型,即标准设备坐标的深度。也就是投影矩阵之后的深度。在照明着色器(下一个传递)中重新构建世界位置的问题是,在乘以相机-立方体面反向视图*投影矩阵之后一定要确保除以w。
调试阴影贴图的关键在于着色器调整。首先使用颜色来可视化您的阴影贴图中存储的深度,这是解决我公司引擎中点阴影贴图问题的唯一方法。您可以使用组合mix和clamp来制作颜色代码,例如蓝色从0到0.3,红色从0.3到0.6,绿色从0.6到1。如果您有世界距离存储,则更容易,但通过颜色代码进行可视化仍然很有趣。只需使用相同的函数,但将距离除以预期的世界范围即可。
使用该可视方案,您将能够立即看到阴影区域,因为它们都具有相同的颜色(因为“射线”被更近的表面截取)。一旦达到这个点; 其余的都会顺利进行。
祝你好运 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接