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

多线程更新:

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

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

相关推荐
子燕若水2 小时前
在Adobe Substance 3D Painter中,已经有基础图层,如何新建一个图层A,clone基础图层的纹理和内容到A图层
3d·adobe
地平线开发者3 小时前
理想汽车智驾方案介绍专题 1 端到端+VLM 方案介绍
算法·自动驾驶
地平线开发者3 小时前
征程 6 | UCP 任务优先级/抢占简介与实操
算法·自动驾驶
杰克尼3 小时前
912. 排序数组
算法
jndingxin4 小时前
OpenCV直线段检测算法类cv::line_descriptor::LSDDetector
人工智能·opencv·算法
秋说4 小时前
【PTA数据结构 | C语言版】阶乘的递归实现
c语言·数据结构·算法
小指纹5 小时前
巧用Bitset!优化dp
数据结构·c++·算法·代理模式·dp·bitset
爱Java&Java爱我6 小时前
数组:从键盘上输入10个数,合法值为1、2或3,不是这三个数则为非法数字,试编辑统计每个整数和非法数字的个数
java·开发语言·算法
是店小二呀7 小时前
【算法-BFS 解决最短路问题】探索BFS在图论中的应用:最短路径问题的高效解法
算法·图论·宽度优先
qq_513970447 小时前
力扣 hot100 Day46
算法·leetcode