又开始找工作了,借机休息出去旅行两个月,顺便利用这段时间整理下以前写的东西。
以下是一个简单的动态批处理实现:
cpp
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
// 顶点结构体
struct Vertex {
float x, y, z; // 位置
float r, g, b; // 颜色
};
// 动态批处理类
class DynamicBatch {
public:
DynamicBatch() {
// 初始化 VAO 和 VBO
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, x));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, r));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
~DynamicBatch() {
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
}
// 添加顶点数据
void AddVertex(const Vertex& vertex) {
vertices.push_back(vertex);
}
// 更新顶点缓冲区
void UpdateBuffer() {
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), GL_DYNAMIC_DRAW);
}
// 渲染批处理
void Render() {
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
glBindVertexArray(0);
}
// 清空顶点数据
void Clear() {
vertices.clear();
}
private:
GLuint VAO, VBO; // 顶点数组对象和顶点缓冲区对象
std::vector<Vertex> vertices; // 顶点数据
};
// 顶点着色器
c
const char* vertexShaderSource = R"(
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aColor;
out vec3 ourColor;
void main() {
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
}
)";
// 片段着色器
const char* fragmentShaderSource = R"(
#version 330 core
in vec3 ourColor;
out vec4 FragColor;
void main() {
FragColor = vec4(ourColor, 1.0);
}
)";
cpp
// 编译着色器
GLuint CompileShader(GLenum type, const char* source) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
std::cerr << "Shader compilation failed: " << infoLog << std::endl;
}
return shader;
}
// 链接着色器程序
GLuint CreateShaderProgram(const char* vertexSource, const char* fragmentSource) {
GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, vertexSource);
GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragmentSource);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
GLint success;
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
std::cerr << "Shader program linking failed: " << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return shaderProgram;
}
int main() {
// 初始化 GLFW
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW" << std::endl;
return -1;
}
// 创建窗口
GLFWwindow* window = glfwCreateWindow(800, 600, "Dynamic Batching Example", nullptr, nullptr);
if (!window) {
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// 初始化 GLEW
if (glewInit() != GLEW_OK) {
std::cerr << "Failed to initialize GLEW" << std::endl;
return -1;
}
// 创建着色器程序
GLuint shaderProgram = CreateShaderProgram(vertexShaderSource, fragmentShaderSource);
// 创建动态批处理对象
DynamicBatch batch;
// 主循环
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT);
// 添加顶点数据
batch.Clear();
batch.AddVertex({-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f});
batch.AddVertex({0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f});
batch.AddVertex({0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f});
// 更新缓冲区并渲染
batch.UpdateBuffer();
glUseProgram(shaderProgram);
batch.Render();
// 交换缓冲区
glfwSwapBuffers(window);
glfwPollEvents();
}
// 清理
glDeleteProgram(shaderProgram);
glfwTerminate();
return 0;
}
代码说明
DynamicBatch 类:
负责管理顶点数据、更新顶点缓冲区和渲染。使用 std::vector 存储动态顶点数据。
通过 glBufferData 将顶点数据上传到 GPU。
顶点和片段着色器:简单的着色器,用于渲染带颜色的三角形。
主循环:每一帧清空批处理数据,添加新的顶点数据,更新缓冲区并渲染。
动态更新:使用 GL_DYNAMIC_DRAW 标志更新顶点缓冲区,适用于频繁变化的顶点数据。
运行结果
运行程序后,你会看到一个彩色的三角形。每一帧都会动态更新顶点数据并渲染。
优化建议
减少内存分配:
如果顶点数据频繁变化,可以预先分配足够的内存,避免频繁调用 glBufferData。
使用实例化渲染:
如果需要渲染大量相同对象,可以使用实例化渲染(Instanced Rendering)进一步优化性能。
多线程更新:
在多线程环境中,可以将顶点数据的更新和渲染分离到不同的线程中。
通过动态批处理,你可以有效地减少绘制调用,提高渲染性能,特别是在需要渲染大量小对象的场景中。