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

多线程更新:

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

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

相关推荐
蒙奇D索大23 分钟前
【数据结构】图论核心算法解析:深度优先搜索(DFS)的纵深遍历与生成树实战指南
数据结构·算法·深度优先·图论·图搜索算法
让我们一起加油好吗30 分钟前
【基础算法】高精度(加、减、乘、除)
c++·算法·高精度·洛谷
不会敲代码的灵长类37 分钟前
机器学习算法-k-means
算法·机器学习·kmeans
Studying 开龙wu37 分钟前
机器学习有监督学习sklearn实战二:六种算法对鸢尾花(Iris)数据集进行分类和特征可视化
学习·算法·机器学习
鑫鑫向栄1 小时前
[蓝桥杯]缩位求和
数据结构·c++·算法·职场和发展·蓝桥杯
Tony__Ferguson1 小时前
简述八大排序(Sort)
数据结构·算法·排序算法
芜湖xin1 小时前
【题解-洛谷】P9422 [蓝桥杯 2023 国 B] 合并数列
算法·队列
鑫鑫向栄1 小时前
[蓝桥杯]外卖店优先级
数据结构·c++·算法·职场和发展·蓝桥杯
<但凡.1 小时前
题海拾贝:P8598 [蓝桥杯 2013 省 AB] 错误票据
数据结构·算法·蓝桥杯
wangyuxuan10292 小时前
AtCoder Beginner Contest 399题目翻译
开发语言·c++·算法