本章节源码 点击此处 文档持续更新
透视投影:
对于点光源的深度贴图,我们需要在上下左右前后六个方向都生成深度贴图
for(int i = 0; i < 6; i++)
{
GLuint face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, face, depthCubemap, 0);
/* 伪代码: 也就是视角矩阵 */
BindViewMatrix(lightViewMatrices[i]);
RenderScene();
}
我们可以利用几何着色器的功能,在一次渲染过程中就完成多个方向上的深度立方体贴图。我们生成一个深度缓冲,后续利用这个深度缓冲来进行深度值的对比。
// 创建一个帧缓冲对象
glGenFramebuffers(1,&depthCubeMapFBO);
// 创建一个立方体贴图
glGenTextures(1,&depthCubeMap);
// 绑定纹理 并设置每个方向上纹理格式
glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubeMap);
for (unsigned int i = 0; i < 6; ++i)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT,
SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
// 设置纹理的过滤方式。
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
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);
// 将创建的立方体贴图提供给帧缓冲作为深度附件
glBindFramebuffer(GL_FRAMEBUFFER,depthCubeMapFBO);
// 将纹理附件depthCubeMap作为深度缓冲(GL_DEPTH_ATTACHMENT)绑定到帧缓冲对象上
glFramebufferTexture(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,depthCubeMap,0);
// 别禁用了当前帧缓冲对象的颜色绘制和读取功能,使得后续的渲染和像素
// 读取操作不涉及任何颜色数据。这在进行深度测试、模板测试、只关注非颜色附件的渲染任务等场景中是合理的
// 显示告诉OpenGL不适用颜色进行渲染
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
qDebug() << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER,defaultFramebufferObject());
QMatrix4x4 shadowProj;
QMatrix4x4 shadowView;
float near_plane = 1.0f, far_plane = 25.0f;
// 定义一个透视投影 这个透视投影矩阵是深度缓冲中的裁剪空间 并且这个并不会在每个方向上改变,改变的只是观察矩阵
shadowProj.perspective(90.0f, (float)SHADOW_WIDTH / (float)SHADOW_HEIGHT, near_plane, far_plane);
// 准备6个不同方向的观察矩阵
std::vector<QMatrix4x4> shadowTransforms;
shadowView.lookAt(lightPos, lightPos + QVector3D( 1.0, 0.0, 0.0), QVector3D(0.0,-1.0, 0.0));
shadowTransforms.push_back(shadowProj * shadowView); shadowView.setToIdentity();
shadowView.lookAt(lightPos, lightPos + QVector3D(-1.0, 0.0, 0.0), QVector3D(0.0,-1.0, 0.0));
shadowTransforms.push_back(shadowProj * shadowView); shadowView.setToIdentity();
shadowView.lookAt(lightPos, lightPos + QVector3D( 0.0, 1.0, 0.0), QVector3D(0.0, 0.0, 1.0));
shadowTransforms.push_back(shadowProj * shadowView); shadowView.setToIdentity();
shadowView.lookAt(lightPos, lightPos + QVector3D( 0.0,-1.0, 0.0), QVector3D(0.0, 0.0,-1.0));
shadowTransforms.push_back(shadowProj * shadowView); shadowView.setToIdentity();
shadowView.lookAt(lightPos, lightPos + QVector3D( 0.0, 0.0, 1.0), QVector3D(0.0,-1.0, 0.0));
shadowTransforms.push_back(shadowProj * shadowView); shadowView.setToIdentity();
shadowView.lookAt(lightPos, lightPos + QVector3D( 0.0, 0.0,-1.0), QVector3D(0.0,-1.0, 0.0));
shadowTransforms.push_back(shadowProj * shadowView);
// 设置绘制的视窗大小,因为这个是绘制在缓冲中的,并不是真正的绘制在屏幕上面的,所以我们最好保持它和深度纹理贴图采样的分辨率一样是最好的
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
// 将当前帧缓冲区绑定到depthCubeMapFBO上
glBindFramebuffer(GL_FRAMEBUFFER, depthCubeMapFBO);
// 清除当前缓冲区的信息
glClear(GL_DEPTH_BUFFER_BIT);
// 绑定深度缓冲的shader
depthProgramObject.bind();
// 将6个观察矩阵传递给GPU端
for (unsigned int i = 0; i < 6; ++i){
std::string str="shadowMatrices[" + std::to_string(i) + "]";
depthProgramObject.setUniformValue(str.c_str(), shadowTransforms[i]);
}
// 将远平面传递给GPU端
depthProgramObject.setUniformValue("far_plane", far_plane);
// 将点光源的位置传递给GPU端
depthProgramObject.setUniformValue("lightPos", lightPos);
// 渲染场景
renderScene(&depthProgramObject);
// 将帧缓冲区绑定到默认的缓冲区对象上,这样才能让后续的绘制绘制到屏幕上面
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
depthProgramObject.release();
// 绑定shader
shaderProgramObject.bind();
// 生成透视投影,这个透视投影矩阵是真正的视角的裁剪空间
projection.perspective(m_camera.Zoom,(float)width()/(float)height(),_near,_far);
// 获得摄像机的观察视角矩阵
view = m_camera.GetViewMatrix();
// 设置视窗大小,这里是真实的绘制到屏幕上面,所以要保持和openGL的绘制窗口相同。
glViewport(0, 0, width(), height());
// 传递数据到GPU端
shaderProgramObject.setUniformValue("far_plane", far_plane);
shaderProgramObject.setUniformValue("lightPos", lightPos);
shaderProgramObject.setUniformValue("shadows", true);
shaderProgramObject.setUniformValue("projection", projection);
shaderProgramObject.setUniformValue("view", view);
shaderProgramObject.setUniformValue("viewPos",m_camera.Position);
shaderProgramObject.setUniformValue("depthCubeMap",1);
// 绑定纹理单元
glActiveTexture(GL_TEXTURE1);
// 将纹理绑定到纹理单元上
glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubeMap);
renderScene(&shaderProgramObject);
shaderProgramObject.release();
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
void main()
{
gl_Position = model * vec4(aPos, 1.0);
}