目录
13.1 项目规划和设计
13.1.1 项目目标
13.1.2 设计要求
13.2 实现场景中的光照、材质和纹理
13.2.1 创建基础场景
13.2.2 应用材质和纹理
13.3 集成高级渲染效果和后期处理
13.3.1 阴影映射(Shadow Mapping)
13.3.2 环境光遮蔽(AO)
13.3.3 简单的景深效果(Depth of Field)
13.3.4 阴影技术
13.4 性能优化
13.4.1 批处理渲染
13.4.2 LOD(细节层次)
13.4.3 减少状态切换
13.4.4 延迟渲染
13.5 资源管理
13.5.1 纹理管理
13.5.2 模型管理
13.5.3 着色器管理
13.5.4 缓冲区管理
13.6 项目文档和案例研究
13.6.1 项目文档
13.6.2 案例研究
13.7 结论
在本章中,我们将创建一个完整的渲染场景,涵盖从基础的场景设置到高级的渲染效果和后期处理。我们将使用OpenGL和GLSL来实现场景的渲染,并逐步引入光照、材质、纹理、阴影映射、环境光遮蔽(AO)以及景深效果等技术。
13.1 项目规划和设计
13.1.1 项目目标
本项目的目标是创建一个包含地面、墙壁和一个简单3D模型(如立方体)的基本渲染场景。除此之外,我们还将实现以下高级渲染效果:
- 阴影映射(Shadow Mapping)
- 环境光遮蔽(Ambient Occlusion,AO)
- 简单的景深效果(Depth of Field)
13.1.2 设计要求
- 场景内容:
- 地面
- 墙壁
- 3D模型(立方体)
- 光照效果:
- 点光源
- 环境光
- 材质效果:
- 漫反射和高光
- 高级渲染效果:
- 阴影映射
- 环境光遮蔽(AO)
- 简单的景深效果
13.2 实现场景中的光照、材质和纹理
13.2.1 创建基础场景
首先,我们将设置基础场景,包括地面、墙壁和一个简单的3D模型(立方体)。我们使用OpenGL创建这些基本几何体,并为每个对象定义适当的顶点和片段着色器。
顶点着色器 ():
-
#version 330 core
-
-
layout(location = 0) in vec3 aPos;
-
layout(location = 1) in vec3 aNormal;
-
layout(location = 2) in vec2 aTexCoord;
-
-
out vec3 FragPos;
-
out vec3 Normal;
-
out vec2 TexCoord;
-
-
uniform mat4 model;
-
uniform mat4 view;
-
uniform mat4 projection;
-
-
void main() {
-
vec4 worldPosition = model * vec4(aPos, 1.0);
-
FragPos = ;
-
Normal = mat3(transpose(inverse(model))) * aNormal;
-
TexCoord = aTexCoord;
-
gl_Position = projection * view * worldPosition;
-
}
片段着色器 ():
-
#version 330 core
-
-
in vec3 FragPos;
-
in vec3 Normal;
-
in vec2 TexCoord;
-
-
out vec4 FragColor;
-
-
struct Material {
-
vec3 ambient;
-
vec3 diffuse;
-
vec3 specular;
-
float shininess;
-
};
-
-
struct Light {
-
vec3 position;
-
vec3 ambient;
-
vec3 diffuse;
-
vec3 specular;
-
};
-
-
uniform Material material;
-
uniform Light light;
-
uniform vec3 viewPos;
-
uniform sampler2D diffuseTexture;
-
-
void main() {
-
// Ambient
-
vec3 ambient = * ;
-
-
// Diffuse
-
vec3 norm = normalize(Normal);
-
vec3 lightDir = normalize( - FragPos);
-
float diff = max(dot(norm, lightDir), 0.0);
-
vec3 diffuse = * diff * ;
-
-
// Specular
-
vec3 viewDir = normalize(viewPos - FragPos);
-
vec3 reflectDir = reflect(-lightDir, norm);
-
float spec = pow(max(dot(viewDir, reflectDir), 0.0), );
-
vec3 specular = * spec * ;
-
-
vec3 result = ambient + diffuse + specular;
-
FragColor = vec4(result, 1.0) * texture(diffuseTexture, TexCoord);
-
}
13.2.2 应用材质和纹理
接下来,我们为场景中的物体应用基础的材质和纹理。材质的主要属性包括漫反射、镜面反射和环境光成分。纹理则用于为物体添加更多的细节和颜色。
纹理加载和绑定 ():
-
#include ""
-
#include <iostream>
-
-
Texture::Texture(const char* imagePath) {
-
glGenTextures(1, &ID);
-
glBindTexture(GL_TEXTURE_2D, ID);
-
-
// 设置纹理参数
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-
// 加载并生成纹理
-
int width, height, nrChannels;
-
unsigned char* data = stbi_load(imagePath, &width, &height, &nrChannels, 0);
-
if (data) {
-
GLenum format;
-
if (nrChannels == 1)
-
format = GL_RED;
-
else if (nrChannels == 3)
-
format = GL_RGB;
-
else if (nrChannels == 4)
-
format = GL_RGBA;
-
-
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
-
glGenerateMipmap(GL_TEXTURE_2D);
-
} else {
-
std::cerr << "Failed to load texture: " << imagePath << std::endl;
-
}
-
stbi_image_free(data);
-
}
-
-
void Texture::bind() const {
-
glBindTexture(GL_TEXTURE_2D, ID);
-
}
场景对象加载 (scene_object.cpp
):
-
#include "scene_object.hpp"
-
#include <glm/gtc/matrix_transform.hpp>
-
-
SceneObject::SceneObject(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices, const char* texturePath)
-
: vertices(vertices), indices(indices), texture(texturePath) {
-
setupMesh();
-
}
-
-
void SceneObject::setupMesh() {
-
glGenVertexArrays(1, &VAO);
-
glGenBuffers(1, &VBO);
-
glGenBuffers(1, &EBO);
-
-
glBindVertexArray(VAO);
-
-
glBindBuffer(GL_ARRAY_BUFFER, VBO);
-
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
-
-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
-
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
-
-
// 顶点位置
-
glEnableVertexAttribArray(0);
-
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
-
-
// 法线
-
glEnableVertexAttribArray(1);
-
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
-
-
// 纹理坐标
-
glEnableVertexAttribArray(2);
-
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
-
-
glBindVertexArray(0);
-
}
-
-
void SceneObject::draw(const Shader& shader) {
-
shader.use();
-
glBindVertexArray(VAO);
-
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
-
glBindVertexArray(0);
-
}
13.3 集成高级渲染效果和后期处理
13.3.1 阴影映射(Shadow Mapping)
阴影映射用于在场景中生成逼真的阴影效果。我们将使用深度贴图来计算阴影,并在片段着色器中进行深度比较。
阴影映射Shader (shadow_mapping_shader.vert
and shadow_mapping_shader.frag
):
顶点着色器 (shadow_mapping_shader.vert
):
-
#version 330 core
-
-
layout(location = 0) in vec3 aPos;
-
-
uniform mat4 lightSpaceMatrix;
-
uniform mat4 model;
-
-
void main() {
-
gl_Position = lightSpaceMatrix * model * vec4(aPos, 1.0);
-
}
片段着色器 (shadow_mapping_shader.frag
):
-
#version 330 core
-
-
void main() {
-
// 这里不需要输出颜色,深度缓冲会自动存储深度值
-
}
生成阴影贴图 (shadow_mapping.cpp
):
-
#include "shadow_mapping.hpp"
-
-
void ShadowMapping::init(unsigned int width, unsigned int height) {
-
shadowWidth = width;
-
shadowHeight = height;
-
-
glGenFramebuffers(1, &depthMapFBO);
-
-
glGenTextures(1, &depthMap);
-
glBindTexture(GL_TEXTURE_2D, depthMap);
-
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadowWidth, shadowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
-
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
-
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
-
-
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
-
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
-
glDrawBuffer(GL_NONE);
-
glReadBuffer(GL_NONE);
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
}
-
-
void ShadowMapping::render(const Shader& shader, const std::vector<SceneObject>& objects, const glm::mat4& lightSpaceMatrix) {
-
shader.use();
-
shader.setMat4("lightSpaceMatrix", lightSpaceMatrix);
-
-
glViewport(0, 0, shadowWidth, shadowHeight);
-
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
-
glClear(GL_DEPTH_BUFFER_BIT);
-
-
for (const auto& object : objects) {
-
glm::mat4 model = object.getModelMatrix();
-
shader.setMat4("model", model);
-
object.draw(shader);
-
}
-
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
}
应用阴影映射 (shadow_mapping_application.cpp
):
-
#include "shadow_mapping.hpp"
-
#include ""
-
#include "scene_object.hpp"
-
-
// 初始化阴影映射相关内容
-
ShadowMapping shadowMapping;
-
shadowMapping.init(1024, 1024);
-
-
Shader shadowMapShader("shadow_mapping_shader.vert", "shadow_mapping_shader.frag");
-
Shader sceneShader("scene_shader.vert", "scene_shader.frag");
-
-
std::vector<SceneObject> sceneObjects = {
-
// 初始化场景中的对象
-
};
-
-
glm::mat4 lightProjection, lightView;
-
glm::mat4 lightSpaceMatrix;
-
-
// 计算光源的投影矩阵和视图矩阵
-
float near_plane = 1.0f, far_plane = 7.5f;
-
lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
-
lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0, 1.0, 0.0));
-
lightSpaceMatrix = lightProjection * lightView;
-
-
// 渲染阴影贴图
-
shadowMapping.render(shadowMapShader, sceneObjects, lightSpaceMatrix);
-
-
// 渲染场景并应用阴影映射
-
sceneShader.use();
-
sceneShader.setMat4("lightSpaceMatrix", lightSpaceMatrix);
-
glActiveTexture(GL_TEXTURE1);
-
glBindTexture(GL_TEXTURE_2D, shadowMapping.getDepthMap());
-
sceneShader.setInt("shadowMap", 1);
-
-
for (const auto& object : sceneObjects) {
-
object.draw(sceneShader);
-
}
13.3.2 环境光遮蔽(AO)
环境光遮蔽(AO)用于模拟全局光照的遮蔽效果,增强场景的深度感。我们将使用屏幕空间环境光遮蔽(SSAO)技术来实现这一效果。
SSAO实现 ():
-
#include ""
-
#include <random>
-
-
// 生成样本核
-
std::vector<glm::vec3> generateSSAOKernel() {
-
std::vector<glm::vec3> ssaoKernel;
-
std::uniform_real_distribution<float> randomFloats(0.0, 1.0);
-
std::default_random_engine generator;
-
for (unsigned int i = 0; i < 64; ++i) {
-
glm::vec3 sample(
-
randomFloats(generator) * 2.0 - 1.0,
-
randomFloats(generator) * 2.0 - 1.0,
-
randomFloats(generator)
-
);
-
sample = glm::normalize(sample);
-
sample *= randomFloats(generator);
-
float scale = float(i) / 64.0;
-
scale = 0.1f + 0.9f * scale * scale;
-
sample *= scale;
-
ssaoKernel.push_back(sample);
-
}
-
return ssaoKernel;
-
}
-
-
// 生成噪声纹理
-
std::vector<glm::vec3> generateSSAONoise() {
-
std::vector<glm::vec3> ssaoNoise;
-
std::uniform_real_distribution<float> randomFloats(0.0, 1.0);
-
std::default_random_engine generator;
-
for (unsigned int i = 0; i < 16; ++i) {
-
glm::vec3 noise(
-
randomFloats(generator) * 2.0 - 1.0,
-
randomFloats(generator) * 2.0 - 1.0,
-
0.0f
-
);
-
ssaoNoise.push_back(noise);
-
}
-
return ssaoNoise;
-
}
-
-
void SSAO::init(unsigned int width, unsigned int height) {
-
// 初始化帧缓冲和纹理
-
glGenFramebuffers(1, &ssaoFBO);
-
glGenFramebuffers(1, &ssaoBlurFBO);
-
-
glGenTextures(1, &ssaoColorBuffer);
-
glBindTexture(GL_TEXTURE_2D, ssaoColorBuffer);
-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RGB, GL_FLOAT, NULL);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
glBindFramebuffer(GL_FRAMEBUFFER, ssaoFBO);
-
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoColorBuffer, 0);
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
-
glGenTextures(1, &ssaoColorBufferBlur);
-
glBindTexture(GL_TEXTURE_2D, ssaoColorBufferBlur);
-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RGB, GL_FLOAT, NULL);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
glBindFramebuffer(GL_FRAMEBUFFER, ssaoBlurFBO);
-
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoColorBufferBlur, 0);
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
-
// 生成样本核和噪声纹理
-
ssaoKernel = generateSSAOKernel();
-
std::vector<glm::vec3> ssaoNoise = generateSSAONoise();
-
-
glGenTextures(1, &noiseTexture);
-
glBindTexture(GL_TEXTURE_2D, noiseTexture);
-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-
}
-
-
void SSAO::renderSSAO(const Shader& ssaoShader, unsigned int gPosition, unsigned int gNormal, const glm::mat4& projection) {
-
glBindFramebuffer(GL_FRAMEBUFFER, ssaoFBO);
-
glClear(GL_COLOR_BUFFER_BIT);
-
-
ssaoShader.use();
-
for (unsigned int i = 0; i < 64; ++i) {
-
ssaoShader.setVec3("samples[" + std::to_string(i) + "]", ssaoKernel[i]);
-
}
-
ssaoShader.setMat4("projection", projection);
-
ssaoShader.setVec2("screenSize", glm::vec2(screenWidth, screenHeight));
-
-
glActiveTexture(GL_TEXTURE0);
-
glBindTexture(GL_TEXTURE_2D, gPosition);
-
glActiveTexture(GL_TEXTURE1);
-
glBindTexture(GL_TEXTURE_2D, gNormal);
-
glActiveTexture(GL_TEXTURE2);
-
glBindTexture(GL_TEXTURE_2D, noiseTexture);
-
-
renderQuad();
-
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
}
-
-
void SSAO::blurSSAO(const Shader& blurShader) {
-
glBindFramebuffer(GL_FRAMEBUFFER, ssaoBlurFBO);
-
glClear(GL_COLOR_BUFFER_BIT);
-
-
blurShader.use();
-
glActiveTexture(GL_TEXTURE0);
-
glBindTexture(GL_TEXTURE_2D, ssaoColorBuffer);
-
renderQuad();
-
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
}
-
-
void SSAO::renderQuad() {
-
if (quadVAO == 0) {
-
float quadVertices[] = {
-
// positions // texture Coords
-
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-
-
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-
1.0f, 1.0f, 0.0f, 1.0f, 1.0f
-
};
-
glGenVertexArrays(1, &quadVAO);
-
glGenBuffers(1, &quadVBO);
-
glBindVertexArray(quadVAO);
-
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
-
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
-
glEnableVertexAttribArray(0);
-
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
-
glEnableVertexAttribArray(1);
-
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
-
}
-
glBindVertexArray(quadVAO);
-
glDrawArrays(GL_TRIANGLES, 0, 6);
-
glBindVertexArray(0);
-
}
SSAO应用 (ssao_application.cpp
):
-
#include ""
-
#include ""
-
-
// 初始化SSAO
-
SSAO ssao;
-
ssao.init(screenWidth, screenHeight);
-
-
Shader ssaoShader("ssao_shader.vert", "ssao_shader.frag");
-
Shader blurShader("blur_shader.vert", "blur_shader.frag");
-
-
unsigned int gPosition, gNormal;
-
// 这里假设gPosition和gNormal是已经在G缓冲中生成的纹理
-
-
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)screenWidth / (float)screenHeight, 0.1f, 100.0f);
-
-
// 渲染SSAO
-
ssao.renderSSAO(ssaoShader, gPosition, gNormal, projection);
-
-
// 模糊SSAO
-
ssao.blurSSAO(blurShader);
-
-
// 在最终场景渲染中使用SSAO结果
-
Shader sceneShader("scene_shader.vert", "scene_shader.frag");
-
sceneShader.use();
-
glActiveTexture(GL_TEXTURE2);
-
glBindTexture(GL_TEXTURE_2D, ssao.getColorBufferBlur());
-
sceneShader.setInt("ssao", 2);
-
-
// 渲染场景...
13.3.3 简单的景深效果(Depth of Field)
景深效果用于模拟真实相机的焦距效果,使得场景中的某些部分看起来模糊。我们将使用高斯模糊和深度缓冲来实现这一效果。
景深Shader (dof_shader.vert
and dof_shader.frag
):
顶点着色器 (dof_shader.vert
):
-
#version 330 core
-
-
layout(location = 0) in vec3 aPos;
-
layout(location = 1) in vec2 aTexCoord;
-
-
out vec2 TexCoords;
-
-
void main() {
-
TexCoords = aTexCoord;
-
gl_Position = vec4(aPos, 1.0);
-
}
片段着色器 (dof_shader.frag
):
-
#version 330 core
-
-
out vec4 FragColor;
-
in vec2 TexCoords;
-
-
uniform sampler2D scene;
-
uniform sampler2D depthMap;
-
-
uniform float near_plane;
-
uniform float far_plane;
-
uniform float focalDepth;
-
uniform float focalLength;
-
uniform float fStop;
-
uniform vec2 screenSize;
-
-
float LinearizeDepth(float depth) {
-
float z = depth * 2.0 - 1.0; // Back to NDC
-
return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane));
-
}
-
-
void main() {
-
float depth = texture(depthMap, TexCoords).r;
-
float linearDepth = LinearizeDepth(depth);
-
-
float f = focalLength;
-
float d = linearDepth - focalDepth;
-
float coc = abs(f * d / (d - (f * f / (f - focalDepth)))) / (fStop * 0.05);
-
-
vec2 texelSize = 1.0 / screenSize;
-
vec3 color = vec3(0.0);
-
int samples = 8;
-
float weightSum = 0.0;
-
-
for (int x = -samples; x <= samples; x++) {
-
for (int y = -samples; y <= samples; y++) {
-
vec2 offset = vec2(float(x), float(y)) * texelSize * coc;
-
color += texture(scene, TexCoords + offset).rgb;
-
weightSum += 1.0;
-
}
-
}
-
-
color = color / weightSum;
-
FragColor = vec4(color, 1.0);
-
}
景深实现 ():
-
#include ""
-
-
void DOF::init(unsigned int width, unsigned int height) {
-
// 初始化帧缓冲和纹理
-
glGenFramebuffers(1, &dofFBO);
-
glBindFramebuffer(GL_FRAMEBUFFER, dofFBO);
-
-
glGenTextures(1, &dofColorBuffer);
-
glBindTexture(GL_TEXTURE_2D, dofColorBuffer);
-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dofColorBuffer, 0);
-
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
}
-
-
void DOF::renderDOF(const Shader& dofShader, unsigned int sceneTexture, unsigned int depthTexture, float nearPlane, float farPlane, float focalDepth, float focalLength, float fStop, unsigned int screenWidth, unsigned int screenHeight) {
-
glBindFramebuffer(GL_FRAMEBUFFER, dofFBO);
-
glClear(GL_COLOR_BUFFER_BIT);
-
-
dofShader.use();
-
dofShader.setFloat("near_plane", nearPlane);
-
dofShader.setFloat("far_plane", farPlane);
-
dofShader.setFloat("focalDepth", focalDepth);
-
dofShader.setFloat("focalLength", focalLength);
-
dofShader.setFloat("fStop", fStop);
-
dofShader.setVec2("screenSize", glm::vec2(screenWidth, screenHeight));
-
-
glActiveTexture(GL_TEXTURE0);
-
glBindTexture(GL_TEXTURE_2D, sceneTexture);
-
dofShader.setInt("scene", 0);
-
-
glActiveTexture(GL_TEXTURE1);
-
glBindTexture(GL_TEXTURE_2D, depthTexture);
-
dofShader.setInt("depthMap", 1);
-
-
renderQuad();
-
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
}
-
-
void DOF::renderQuad() {
-
if (quadVAO == 0) {
-
float quadVertices[] = {
-
// positions // texture Coords
-
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-
-
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-
1.0f, 1.0f, 0.0f, 1.0f, 1.0f
-
};
-
glGenVertexArrays(1, &quadVAO);
-
glGenBuffers(1, &quadVBO);
-
glBindVertexArray(quadVAO);
-
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
-
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
-
glEnableVertexAttribArray(0);
-
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
-
glEnableVertexAttribArray(1);
-
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
-
}
-
glBindVertexArray(quadVAO);
-
glDrawArrays(GL_TRIANGLES, 0, 6);
-
glBindVertexArray(0);
-
}
景深应用 (dof_application.cpp
):
-
#include ""
-
#include ""
-
-
// 初始化景深
-
DOF dof;
-
dof.init(screenWidth, screenHeight);
-
-
Shader dofShader("dof_shader.vert", "dof_shader.frag");
-
-
unsigned int sceneTexture, depthTexture;
-
// 这里假设sceneTexture和depthTexture是已经生成的场景和深度纹理
-
-
// 渲染景深效果
-
dof.renderDOF(dofShader, sceneTexture, depthTexture, nearPlane, farPlane, focalDepth, focalLength, fStop, screenWidth, screenHeight);
-
-
// 在最终场景渲染中使用景深结果
-
Shader sceneShader("scene_shader.vert", "scene_shader.frag");
-
sceneShader.use();
-
glActiveTexture(GL_TEXTURE2);
-
glBindTexture(GL_TEXTURE_2D, dof.getColorBuffer());
-
sceneShader.setInt("dof", 2);
-
-
// 渲染场景...
13.3.4 阴影技术
阴影技术用于增加场景的真实性,使得光照效果更加逼真。我们将实现简单的阴影映射技术。
阴影映射 Shader (shadow_shader.vert
and shadow_shader.frag
):
顶点着色器 (shadow_shader.vert
):
-
#version 330 core
-
-
layout(location = 0) in vec3 aPos;
-
layout(location = 1) in vec3 aNormal;
-
-
out vec4 FragPosLightSpace;
-
-
uniform mat4 lightSpaceMatrix;
-
uniform mat4 model;
-
-
void main() {
-
vec4 fragPos = model * vec4(aPos, 1.0);
-
FragPosLightSpace = lightSpaceMatrix * fragPos;
-
gl_Position = fragPos;
-
}
片段着色器 (shadow_shader.frag
):
-
#version 330 core
-
-
in vec4 FragPosLightSpace;
-
-
uniform sampler2D shadowMap;
-
-
void main() {
-
vec3 projCoords = / ;
-
projCoords = projCoords * 0.5 + 0.5;
-
-
float closestDepth = texture(shadowMap, ).r;
-
float currentDepth = ;
-
-
float shadow = currentDepth > closestDepth ? 1.0 : 0.0;
-
-
FragColor = vec4(vec3(shadow), 1.0);
-
}
阴影映射实现 ():
-
#include ""
-
-
void Shadow::init(unsigned int width, unsigned int height) {
-
shadowWidth = width;
-
shadowHeight = height;
-
-
glGenFramebuffers(1, &shadowFBO);
-
glBindFramebuffer(GL_FRAMEBUFFER, shadowFBO);
-
-
glGenTextures(1, &shadowMap);
-
glBindTexture(GL_TEXTURE_2D, shadowMap);
-
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
-
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
-
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
-
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowMap, 0);
-
glDrawBuffer(GL_NONE);
-
glReadBuffer(GL_NONE);
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
}
-
-
void Shadow::renderShadowMap(const Shader& shadowShader, const glm::mat4& lightSpaceMatrix, unsigned int sceneVAO, unsigned int numVertices) {
-
glViewport(0, 0, shadowWidth, shadowHeight);
-
glBindFramebuffer(GL_FRAMEBUFFER, shadowFBO);
-
glClear(GL_DEPTH_BUFFER_BIT);
-
-
shadowShader.use();
-
shadowShader.setMat4("lightSpaceMatrix", lightSpaceMatrix);
-
-
glBindVertexArray(sceneVAO);
-
glDrawArrays(GL_TRIANGLES, 0, numVertices);
-
glBindVertexArray(0);
-
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
}
-
-
unsigned int Shadow::getShadowMap() const {
-
return shadowMap;
-
}
阴影技术应用 (shadow_application.cpp
):
-
#include ""
-
#include ""
-
-
// 初始化阴影
-
Shadow shadow;
-
shadow.init(1024, 1024);
-
-
Shader shadowShader("shadow_shader.vert", "shadow_shader.frag");
-
-
unsigned int sceneVAO;
-
unsigned int numVertices;
-
-
// 设置光源空间矩阵
-
glm::mat4 lightProjection, lightView;
-
glm::mat4 lightSpaceMatrix;
-
float near_plane = 1.0f, far_plane = 7.5f;
-
lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
-
lightView = glm::lookAt(glm::vec3(0.0f, 5.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
-
lightSpaceMatrix = lightProjection * lightView;
-
-
// 渲染阴影贴图
-
shadow.renderShadowMap(shadowShader, lightSpaceMatrix, sceneVAO, numVertices);
-
-
// 渲染场景时使用阴影贴图
-
Shader sceneShader("scene_shader.vert", "scene_shader.frag");
-
sceneShader.use();
-
sceneShader.setInt("shadowMap", 1);
-
sceneShader.setMat4("lightSpaceMatrix", lightSpaceMatrix);
-
-
// 在渲染循环中使用阴影贴图
-
while (!glfwWindowShouldClose(window)) {
-
// 处理输入
-
processInput(window);
-
-
// 渲染场景
-
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
-
glActiveTexture(GL_TEXTURE1);
-
glBindTexture(GL_TEXTURE_2D, shadow.getShadowMap());
-
-
sceneShader.use();
-
glm::mat4 model = glm::mat4(1.0f);
-
sceneShader.setMat4("model", model);
-
sceneShader.setMat4("view", view);
-
sceneShader.setMat4("projection", projection);
-
-
glBindVertexArray(sceneVAO);
-
glDrawArrays(GL_TRIANGLES, 0, numVertices);
-
glBindVertexArray(0);
-
-
// 交换缓冲
-
glfwSwapBuffers(window);
-
glfwPollEvents();
-
}
阴影技术总结:
在这一节中,我们实现了阴影映射技术,包括生成阴影贴图和在场景渲染时应用阴影贴图。通过在光源视角下渲染场景,我们生成了深度贴图,然后在最终渲染中使用该贴图来决定哪些片段在阴影中,从而实现阴影效果。
13.4 性能优化
渲染复杂场景时,性能优化至关重要。我们将在这一节讨论几种常见的优化技术。
13.4.1 批处理渲染
批处理渲染(Batch Rendering)是一种减少绘制调用(Draw Calls)的方法。我们可以将多个对象的顶点数据合并到一个顶点缓冲区中,从而减少绘制调用的次数。
-
void BatchRenderer::addObject(const std::vector<float>& vertices, const std::vector<unsigned int>& indices) {
-
// 将顶点数据和索引数据添加到批处理中
-
this->vertices.insert(this->vertices.end(), vertices.begin(), vertices.end());
-
this->indices.insert(this->indices.end(), indices.begin(), indices.end());
-
}
-
-
void BatchRenderer::render() {
-
// 绑定缓冲区并绘制所有对象
-
glBindVertexArray(VAO);
-
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
-
glBindVertexArray(0);
-
}
13.4.2 LOD(细节层次)
LOD(Level of Detail)技术根据对象与相机的距离调整渲染细节,从而减少远距离对象的顶点数,提高渲染性能。
-
void Model::loadModel(std::string path) {
-
// 加载模型的不同细节层次
-
loadDetailLevel(path + "_high.obj", highDetail);
-
loadDetailLevel(path + "_medium.obj", mediumDetail);
-
loadDetailLevel(path + "_low.obj", lowDetail);
-
}
-
-
void Model::render(const glm::mat4& view, const glm::mat4& projection) {
-
// 根据相机距离选择合适的细节层次进行渲染
-
float distance = glm::distance(cameraPos, modelPos);
-
if (distance < mediumDistance) {
-
highDetail.render(view, projection);
-
} else if (distance < lowDistance) {
-
mediumDetail.render(view, projection);
-
} else {
-
lowDetail.render(view, projection);
-
}
-
}
13.4.3 减少状态切换
在渲染过程中频繁切换 OpenGL 状态(如绑定纹理、启用/禁用功能)会影响性能。我们可以通过批量渲染使用相同状态的对象来减少状态切换。
-
void Renderer::renderScene() {
-
// 先渲染使用相同纹理的对象
-
texture1.bind();
-
renderObjectsUsingTexture1();
-
-
texture2.bind();
-
renderObjectsUsingTexture2();
-
}
13.4.4 延迟渲染
延迟渲染(Deferred Shading)技术将几何信息存储在缓冲区中,延迟光照计算到片段着色阶段,从而提高渲染性能,特别是在处理复杂光照情况下。
-
void DeferredRenderer::initialize() {
-
// 创建帧缓冲对象并附加多个颜色附件
-
glGenFramebuffers(1, &gBuffer);
-
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
-
-
// 创建位置颜色附件
-
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, &gAlbedoSpec);
-
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
-
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, gAlbedoSpec, 0);
-
-
// 创建并附加深度附件
-
glGenRenderbuffers(1, &rboDepth);
-
glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);
-
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, screenWidth, screenHeight);
-
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth);
-
-
// 告诉OpenGL我们使用哪些颜色附件进行渲染
-
unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
-
glDrawBuffers(3, attachments);
-
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
}
延迟渲染主循环:
-
void DeferredRenderer::render() {
-
// 几何处理阶段
-
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
-
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
geometryPassShader.use();
-
for (Model& model : models) {
-
model.render(geometryPassShader);
-
}
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
-
// 光照处理阶段
-
lightingPassShader.use();
-
glActiveTexture(GL_TEXTURE0);
-
glBindTexture(GL_TEXTURE_2D, gPosition);
-
glActiveTexture(GL_TEXTURE1);
-
glBindTexture(GL_TEXTURE_2D, gNormal);
-
glActiveTexture(GL_TEXTURE2);
-
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
-
renderQuad();
-
}
性能优化总结:
通过应用批处理渲染、LOD、减少状态切换和延迟渲染等技术,我们可以显著提高渲染性能,特别是在复杂场景和多光源情况下。
13.5 资源管理
资源管理对于保持应用程序的性能和内存使用至关重要。在这一节中,我们将讨论如何有效地管理纹理、模型和其他资源。
13.5.1 纹理管理
使用纹理管理器来加载、缓存和管理纹理。
-
class TextureManager {
-
public:
-
static TextureManager& getInstance() {
-
static TextureManager instance;
-
return instance;
-
}
-
-
unsigned int loadTexture(const std::string& path) {
-
if (textureCache.find(path) != textureCache.end()) {
-
return textureCache[path];
-
}
-
-
unsigned int textureID;
-
glGenTextures(1, &textureID);
-
glBindTexture(GL_TEXTURE_2D, textureID);
-
-
// 加载纹理
-
int width, height, nrComponents;
-
unsigned char* data = stbi_load(path.c_str(), &width, &height, &nrComponents, 0);
-
if (data) {
-
GLenum format;
-
if (nrComponents == 1)
-
format = GL_RED;
-
else if (nrComponents == 3)
-
format = GL_RGB;
-
else if (nrComponents == 4)
-
format = GL_RGBA;
-
-
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
-
glGenerateMipmap(GL_TEXTURE_2D);
-
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-
textureCache[path] = textureID;
-
} else {
-
std::cout << "Failed to load texture at path: " << path << std::endl;
-
}
-
stbi_image_free(data);
-
-
return textureID;
-
}
-
-
private:
-
TextureManager() {}
-
std::unordered_map<std::string, unsigned int> textureCache;
-
};
在渲染代码中使用纹理管理器:
-
unsigned int diffuseTexture = TextureManager::getInstance().loadTexture("path/to/diffuse_texture.png");
-
unsigned int specularTexture = TextureManager::getInstance().loadTexture("path/to/specular_texture.png");
-
-
glActiveTexture(GL_TEXTURE0);
-
glBindTexture(GL_TEXTURE_2D, diffuseTexture);
-
glActiveTexture(GL_TEXTURE1);
-
glBindTexture(GL_TEXTURE_2D, specularTexture);
13.5.2 模型管理
使用模型管理器来加载和缓存模型。
-
class ModelManager {
-
public:
-
static ModelManager& getInstance() {
-
static ModelManager instance;
-
return instance;
-
}
-
-
Model& loadModel(const std::string& path) {
-
if (modelCache.find(path) != modelCache.end()) {
-
return modelCache[path];
-
}
-
-
Model model(path);
-
modelCache[path] = model;
-
return modelCache[path];
-
}
-
-
private:
-
ModelManager() {}
-
std::unordered_map<std::string, Model> modelCache;
-
};
在渲染代码中使用模型管理器:
-
Model& myModel = ModelManager::getInstance().loadModel("path/to/");
-
-
myModel.draw(shader);
13.5.3 着色器管理
使用着色器管理器来加载和缓存着色器程序。
-
class ShaderManager {
-
public:
-
static ShaderManager& getInstance() {
-
static ShaderManager instance;
-
return instance;
-
}
-
-
Shader& loadShader(const std::string& vertexPath, const std::string& fragmentPath) {
-
std::string key = vertexPath + fragmentPath;
-
if (shaderCache.find(key) != shaderCache.end()) {
-
return shaderCache[key];
-
}
-
-
Shader shader(vertexPath, fragmentPath);
-
shaderCache[key] = shader;
-
return shaderCache[key];
-
}
-
-
private:
-
ShaderManager() {}
-
std::unordered_map<std::string, Shader> shaderCache;
-
};
在渲染代码中使用着色器管理器:
-
Shader& sceneShader = ShaderManager::getInstance().loadShader("path/to/vertex_shader.vert", "path/to/fragment_shader.frag");
-
-
sceneShader.use();
-
sceneShader.setMat4("view", view);
-
sceneShader.setMat4("projection", projection);
13.5.4 缓冲区管理
使用缓冲区管理器来创建和管理 OpenGL 缓冲区对象。
-
class BufferManager {
-
public:
-
static BufferManager& getInstance() {
-
static BufferManager instance;
-
return instance;
-
}
-
-
unsigned int createVertexArray() {
-
unsigned int VAO;
-
glGenVertexArrays(1, &VAO);
-
glBindVertexArray(VAO);
-
return VAO;
-
}
-
-
unsigned int createVertexBuffer(const std::vector<float>& data) {
-
unsigned int VBO;
-
glGenBuffers(1, &VBO);
-
glBindBuffer(GL_ARRAY_BUFFER, VBO);
-
glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(float), data.data(), GL_STATIC_DRAW);
-
return VBO;
-
}
-
-
unsigned int createIndexBuffer(const std::vector<unsigned int>& data) {
-
unsigned int EBO;
-
glGenBuffers(1, &EBO);
-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
-
glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.size() * sizeof(unsigned int), data.data(), GL_STATIC_DRAW);
-
return EBO;
-
}
-
-
private:
-
BufferManager() {}
-
};
在渲染代码中使用缓冲区管理器:
-
unsigned int VAO = BufferManager::getInstance().createVertexArray();
-
unsigned int VBO = BufferManager::getInstance().createVertexBuffer(vertices);
-
unsigned int EBO = BufferManager::getInstance().createIndexBuffer(indices);
-
-
glBindVertexArray(VAO);
-
glBindBuffer(GL_ARRAY_BUFFER, VBO);
-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
13.6 项目文档和案例研究
项目文档和案例研究是开发过程中不可或缺的一部分。良好的文档和详细的案例研究有助于团队成员理解项目的结构和设计决策,并为未来的维护和升级提供参考。
13.6.1 项目文档
项目文档应包括以下内容:
- 项目简介
- 系统架构
- 模块说明
- 接口文档
- 配置说明
- 使用指南
13.6.2 案例研究
案例研究应包括以下内容:
- 项目背景
- 问题描述
- 解决方案
- 实施过程
- 结果和效果
- 经验总结
13.7 结论
在本章中,我们通过一个综合项目展示了如何创建一个完整的渲染场景。我们讨论了环境光遮蔽、景深效果、阴影技术、性能优化、资源管理等方面的实现方法,并提供了相应的代码示例。