OpenGL Shadow mapping
libai 发布于 2024-08-16

基本阴影贴图

基本的阴影贴图算法分为两步。首先,从灯光的角度渲染场景。仅计算每个片段的深度。接下来,场景将照常渲染,但需要进行额外的测试以查看当前片段是否在阴影中。

“在阴影中”测试实际上很简单。如果当前样本在同一点上比阴影贴图离灯光更远,这意味着场景包含一个离灯光更近的对象。换句话说,当前片段处于阴影中。

下图可能有助于您理解原理:

渲染阴影贴图

在本教程中,我们将只考虑平行光——距离如此之远,以至于所有光线都可以被视为平行的光。因此,渲染阴影贴图是使用正交投影矩阵完成的。正交矩阵就像通常的透视投影矩阵一样,只是不考虑透视——无论物体在相机附近还是远处,它看起来都是一样的。

设置渲染目标和MVP矩阵

在这里,我们使用1024x1024 16位深度纹理来包含阴影贴图。对于阴影贴图,16位通常就足够了。请随意尝试这些值。请注意,我们使用的是深度纹理,而不是深度渲染缓冲区,因为我们稍后需要对其进行采样。

// The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
 GLuint FramebufferName = 0;
 glGenFramebuffers(1, &FramebufferName);
 glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);

 // Depth texture. Slower than a depth buffer, but you can sample it later in your shader
 GLuint depthTexture;
 glGenTextures(1, &depthTexture);
 glBindTexture(GL_TEXTURE_2D, depthTexture);
 glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT16, 1024, 1024, 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_EDGE);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

 glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0);

 glDrawBuffer(GL_NONE); // No color buffer is drawn to.

 // Always check that our framebuffer is ok
 if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
 return false;

用于从灯光的视角渲染场景的MVP矩阵计算如下:

  • 投影矩阵是一个正交矩阵,它将分别包含X、Y和Z轴上的轴对齐框(-10,10)、(-10,10)、(-10,20)中的所有内容。设置这些值是为了使我们的整个可见场景始终可见;更多信息请参阅“更进一步”一节。
  • 视图矩阵旋转世界,这样在相机空间中,光线方向为-Z
  • 模型矩阵是你想要的任何东西。
glm::vec3 lightInvDir = glm::vec3(0.5f,2,2);

 // Compute the MVP matrix from the light's point of view
 glm::mat4 depthProjectionMatrix = glm::ortho<float>(-10,10,-10,10,-10,20);
 glm::mat4 depthViewMatrix = glm::lookAt(lightInvDir, glm::vec3(0,0,0), glm::vec3(0,1,0));
 glm::mat4 depthModelMatrix = glm::mat4(1.0);
 glm::mat4 depthMVP = depthProjectionMatrix * depthViewMatrix * depthModelMatrix;

 // Send our transformation to the currently bound shader,
 // in the "MVP" uniform
 glUniformMatrix4fv(depthMatrixID, 1, GL_FALSE, &depthMVP[0][0])

着色器

此过程中使用的着色器非常简单。顶点着色器是一个传递着色器,它简单地计算顶点在齐次坐标系中的位置:

#version 330 core

// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;

// Values that stay constant for the whole mesh.
uniform mat4 depthMVP;

void main(){
 gl_Position =  depthMVP * vec4(vertexPosition_modelspace,1);
}

片段着色器同样简单:它只是在位置0(即在我们的深度纹理中)写入片段的深度。

#version 330 core

// Ouput data
layout(location = 0) out float fragmentdepth;

void main(){
    // Not really needed, OpenGL does it anyway
    fragmentdepth = gl_FragCoord.z;
}

渲染阴影贴图的速度通常是普通渲染的两倍多,因为只写入低精度深度,而不是深度和颜色;内存带宽通常是GPU上最大的性能问题。

结果

生成的纹理看起来像这样:

 

 

 

原文:https://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/#basic-shadowmap

 

李白
关注 私信
文章
12
关注
0
粉丝
0