延迟渲染(Deferred Rendering)是一种渲染技术,它将场景中的几何处理(例如,变换和光栅化)与光照和着色处理分离。这种技术可以有效地处理大量的光源,因为它只需要对屏幕上的每个像素执行一次光照计算,而不是对每个物体执行一次。
在使用OpenGL实现延迟渲染的阴影贴图时,你需要进行以下步骤:
G-Buffer(Geometry Buffer)是一个屏幕大小的纹理数组,用于存储场景中的几何信息,如位置、法线、颜色等。
GLuint gBuffer;
glGenFramebuffers(1, &gBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
GLuint gPosition, gNormal, gColorSpec;
// 位置纹理
glGenTextures(1, &gPosition);
glBindTexture(GL_TEXTURE_2D, gPosition);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, screenWidth, screenHeight, 0, GL_RGB, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0);
// 法线纹理
glGenTextures(1, &gNormal);
glBindTexture(GL_TEXTURE_2D, gNormal);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, screenWidth, screenHeight, 0, GL_RGB, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal, 0);
// 颜色+镜面纹理
glGenTextures(1, &gColorSpec);
glBindTexture(GL_TEXTURE_2D, gColorSpec);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screenWidth, screenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gColorSpec, 0);
// 告诉OpenGL我们将使用这些附件
GLuint attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, attachments);
// 创建并绑定深度缓冲
GLuint depthBuffer;
glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, screenWidth, screenHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
// 检查帧缓冲完整性
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
在这个阶段,你将场景中的每个物体渲染到G-Buffer中。
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
// 渲染场景中的每个物体
for(auto& object : sceneObjects) {
// 设置模型矩阵、视图矩阵、投影矩阵
// ...
// 绑定VAO并绘制物体
glBindVertexArray(object.vao);
glDrawElements(GL_TRIANGLES, object.indexCount, GL_UNSIGNED_INT, 0);
}
在这个阶段,你将对屏幕上的每个像素执行光照计算。
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 设置视口
glViewport(0, 0, screenWidth, screenHeight);
// 使用着色器程序
shader.use();
// 绑定G-Buffer纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gPosition);
shader.setInt("gPosition", 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, gNormal);
shader.setInt("gNormal", 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, gColorSpec);
shader.setInt("gColorSpec", 2);
// 渲染一个全屏四边形
renderQuad();
// 在片段着色器中进行光照计算
// ...
为了在延迟渲染中实现阴影贴图,你需要在光照阶段考虑光源的视角。这通常涉及到创建一个光源的深度贴图,并在片段着色器中使用它来计算阴影。
// 设置光源的视口和投影矩阵
// ...
// 渲染场景到光源深度贴图
glBindFramebuffer(GL_FRAMEBUFFER, lightDepthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
glCullFace(GL_FRONT);
for(auto& object : sceneObjects) {
// 设置模型矩阵、光源视图矩阵、光源投影矩阵
// ...
// 绑定VAO并绘制物体
glBindVertexArray(object.vao);
glDrawElements(GL_TRIANGLES, object.indexCount, GL_UNSIGNED_INT, 0);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glCullFace(GL_BACK);
// 片段着色器
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gColorSpec;
uniform sampler2D shadowMap;
uniform vec3 viewPos;
uniform vec3 lightPos;
uniform mat4 lightSpaceMatrix;
float ShadowCalculation(vec4 fragPosLightSpace) {
// 执行透视除法
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
float closestDepth = texture(shadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float shadow = currentDepth > closestDepth ? 1.0 : 0.0;
return shadow;
}
void main() {
// 从G-Buffer中获取位置、法线和颜色
vec3 FragPos = texture(gPosition, TexCoords).rgb;
vec3 Normal = texture(gNormal, TexCoords).rgb;
vec3 Diffuse = texture(gColorSpec, TexCoords).rgb;
// 计算光照
vec3 lighting = vec3(0.0);
vec3 ambient = 0.1 * Diffuse;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 lightDir = normalize(lightPos - FragPos);
// 计算漫反射
float diff = max(dot(Normal, lightDir), 0.0);
vec3 diffuse = diff * Diffuse;
// 计算镜面高光
vec3 reflectDir = reflect(-lightDir, Normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = vec3(0.2) * spec;
// 计算阴影
vec4 fragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
float shadow = ShadowCalculation(fragPosLightSpace);
lighting = (ambient + (1.0 - shadow) * (diffuse + specular));
FragColor = vec4(lighting, 1.0);
}
通过以上步骤,你可以在OpenGL中实现一个基本的延迟渲染系统,并添加阴影贴图以增强场景的真实感。
领取专属 10元无门槛券
手把手带您无忧上云