C++自研3D教程OPENGL版本---动态批处理的基本实现

又开始找工作了,借机休息出去旅行两个月,顺便利用这段时间整理下以前写的东西。

以下是一个简单的动态批处理实现:

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)进一步优化性能。

多线程更新:

在多线程环境中,可以将顶点数据的更新和渲染分离到不同的线程中。

通过动态批处理,你可以有效地减少绘制调用,提高渲染性能,特别是在需要渲染大量小对象的场景中。

相关推荐
再卷也是菜13 分钟前
C++篇(21)图
数据结构·c++·算法
星轨初途18 分钟前
C++入门(算法竞赛类)
c++·经验分享·笔记·算法
灰灰勇闯IT1 小时前
KMP算法在鸿蒙系统中的应用:从字符串匹配到高效系统级开发(附实战代码)
算法·华为·harmonyos
小龙报1 小时前
【算法通关指南:数据结构和算法篇 】队列相关算法题:3.海港
数据结构·c++·算法·贪心算法·创业创新·学习方法·visual studio
csuzhucong1 小时前
一阶魔方、一阶金字塔魔方、一阶五魔方
算法
五花就是菜2 小时前
P12906 [NERC 2020] Guide 题解
算法·深度优先·图论
辞旧 lekkk2 小时前
【c++】封装红黑树实现mymap和myset
c++·学习·算法·萌新
星轨初途2 小时前
C++的输入输出(上)(算法竞赛类)
开发语言·c++·经验分享·笔记·算法
n***F8752 小时前
SpringMVC 请求参数接收
前端·javascript·算法
Liangwei Lin2 小时前
洛谷 P1025 [NOIP 2001 提高组] 数的划分
算法