OpenGL Uniform Buffer Object示例:缓冲区对象的实现(C/C++)

176 篇文章 ¥59.90 ¥99.00
本文介绍了OpenGL中的Uniform Buffer Object(UBO),用于高效传递uniform数据,减少管理开销。通过C/C++代码示例展示了如何初始化、更新和绑定UBO,以及在着色器中接收数据的方法。注意正确设置uniform块的布局修饰符以符合OpenGL要求。

Uniform Buffer Object(UBO)是OpenGL中一种用于高效传递uniform数据的机制。它允许我们在多个着色器程序之间共享uniform数据,从而减少数据复制和管理的开销。本文将为你展示如何在C/C++中使用OpenGL Uniform Buffer Object来实现这一机制。

首先,我们需要包含相应的头文件和库:

#include <GL/glew.h>
#include <GLFW/glfw3.h>

接下来,我们定义一个结构体来表示uniform数据:

struct UniformData
#ifndef DEMO_H_ #define DEMO_H_ #include <glad/glad.h> class Demo { public: Demo(); virtual ~Demo(); // 必须是虚析构函数!否则 delete 子类会出问题 // 纯虚函数:必须由子类实现 virtual void init() { } virtual void update(float dt) { } virtual void render() { } virtual void destroy() { } virtual void setRenderParameter(bool enableCullFace = false, GLenum cullMode = GL_BACK, GLenum frontFace = GL_CCW); virtual void beginRender(); // // 可选公共方法 // void beginRender(); //private: // bool enableCullFace; //是否启用面剔除功能(即不渲染背对摄像机的面) // GLenum cullMode; //指定要剔除的面类型:GL_BACK(背面)、GL_FRONT(前面)或 GL_FRONT_AND_BACK(双面) // GLenum frontFace; //定义三角形的“正面”方向:GL_CCW(逆时针为正,默认),GL_CW(顺时针为正) protected: bool enableCullFace; GLenum cullMode; GLenum frontFace; const char* vertexShaderSource; //顶点着色器的 GLSL 源代码字符串 const char* fragmentShaderSource; //默认片段着色器的 GLSL 源代码字符串 const char* fragmentShader1Source; //可选的第二种片段着色器源码(例如不同光照模型) const char* fragmentShader2Source; //可选的第三种片段着色器源码(例如边缘检测、颜色变换等) //public: // virtual void setRenderParameter(bool enableCullFace = false, GLenum cullMode = GL_FRONT, GLenum frontFace = GL_CCW); //补注释 // virtual void beginRender(); // 开始渲染前调用,用于应用当前设置的渲染状态(如启用/禁用面剔除) // virtual void render() = 0; // 纯虚函数:子类必须实现具体的渲染逻辑(如绘制几何体、使用着色器等) }; #endif #include "Rectangle.h" #include <glad/glad.h> #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> #include <iostream> Rectangle::Rectangle() { x = -1.0f; y = 0.0f; shader = nullptr; VAO = 0; } Rectangle::~Rectangle() { if (shader) delete shader; glDeleteVertexArrays(1, &VAO); } void Rectangle::initData() { // 创建着色器(从文件加载) shader = new Shader("shaders/vertex.vert", "shaders/fragment.frag"); // 创建并配置 VAO glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); unsigned int VBO, EBO; glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 位置属性 (location = 0) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 解绑 glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); std::cout << "Rectangle initialized with shader and VAO." << std::endl; } void Rectangle::renderObject() { if (!shader) return; // 使用着色器 shader->use(); // 动画逻辑:正弦轨迹移动 + Z轴自转 x += 0.005f; if (x > 1.1f) x = -1.1f; float angle = x / 2.0f * 6.28f; // 转换为弧度 y = sin(angle); // 构造 model 矩阵 glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(x, y, 0.0f)); // 平移到当前位置 model = glm::rotate(model, angle * 5.0f, glm::vec3(0, 0, 1)); // 自旋(更快一点) // 获取 uniform 位置并上传 unsigned int modelLoc = glGetUniformLocation(shader->id, "model"); if (modelLoc != -1) { glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); } // 绘制 glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } #include "Demo3_2.h" Demo3_2::Demo3_2() { //rect.setShaderString(vertexShaderSource, fragmentShader1Source); //rect.initData(); } Demo3_2::~Demo3_2() { } void Demo3_2::init() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); rect.initData(); } void Demo3_2::update(float dt) { } void Demo3_2::render() { glClear(GL_COLOR_BUFFER_BIT); rect.renderObject(); } void Demo3_2::destroy() { } #pragma once #include "Rectangle.h" #include "Demo.h" class Demo3_2:public Demo { public: Demo3_2(); ~Demo3_2(); private: Rectangle rect; //表示一个具体的矩形对象实例 // 注释:该成员变量封装了顶点数据、缓冲区(VBO/VAO)、着色器以及绘制逻辑在 render() 中会被调用来实际绘制矩形 public: //void render(); //重写基类的纯虚函数,实现本 demo 的具体渲染逻辑 // 注释:在此函数中会调用 beginRender() 设置 OpenGL 状态,然后调用 rect.render() 执行矩形的绘制操作 void init() override; void update(float dt) override; void render() override; void destroy() override; }; #pragma once #include <glad/glad.h> #include <string> #include <iostream> #include <fstream> #include <sstream> using namespace std; class Shader { public: unsigned int id; //着色器程序(Program Object)的唯一标识符(OpenGL ID) Shader() { id = 0; } Shader(const char* vs, const char* fs) { unsigned int vertex, fragment; //临时变量,分别存储顶点着色器和片段着色器的 OpenGL 对象 ID // 注释:- vertex: 通过 glCreateShader(GL_VERTEX_SHADER) 创建,- fragment: 通过 glCreateShader(GL_FRAGMENT_SHADER) 创建它们是独立编译的着色器单元,在链接前不会生效 vertex = glCreateShader(GL_VERTEX_SHADER); //创建一个顶点着色器对象,该对象将用于加载、编译顶点着色器源代码 fragment = glCreateShader(GL_FRAGMENT_SHADER); //创建一个片段着色器对象,用于处理像素颜色计算(光栅化后) id = glCreateProgram(); //创建一个着色器程序对象,所有着色器必须附加到此程序才能链接运行链接成功后,可通过 glUseProgram(id) 启用整个管线 glShaderSource(vertex, 1, &vs,nullptr); //将 C++ 字符串 vs(顶点着色器源码)绑定到 vertex 着色器对象 glCompileShader(vertex); //编译顶点着色器,将之前设置的源码编译成 GPU 可执行的形式,若语法错误则编译失败,需调用 checkCompileErrors 检查 checkCompileErrors(vertex, "VERTEX"); //检查顶点着色器是否编译成功如果失败,打印详细的错误日志到控制台 glShaderSource(fragment, 1, &fs, nullptr); //将 fs 字符串作为源码传入片段着色器对象 glCompileShader(fragment); //编译片段着色器 checkCompileErrors(fragment, "FRAGMENT"); //检查片段着色器编译状态 glAttachShader(id, vertex); //将已编译的顶点着色器附加到程序对象 id 上 glAttachShader(id, fragment); //将已编译的片段着色器附加到程序对象 id 上 glLinkProgram(id); //链接着色器程序把所有附加的着色器合并为一个可执行程序检查接口匹配性(如 vs 输出与 fs 输入)、全局符号等成功后方可使用 glDeleteShader(vertex); //链接完成后,删除临时的顶点着色器对象 glDeleteShader(fragment); // 删除临时的片段着色器对象 } Shader(const std::string& vertexPath, const std::string& fragmentPath) { // 读取着色器源码文件 std::string vertexCode = readFile(vertexPath); std::string fragmentCode = readFile(fragmentPath); const char* vShaderCode = vertexCode.c_str(); const char* fShaderCode = fragmentCode.c_str(); unsigned int vertex, fragment; vertex = glCreateShader(GL_VERTEX_SHADER); fragment = glCreateShader(GL_FRAGMENT_SHADER); id = glCreateProgram(); glShaderSource(vertex, 1, &vShaderCode, nullptr); glCompileShader(vertex); checkCompileErrors(vertex, "VERTEX"); glShaderSource(fragment, 1, &fShaderCode, nullptr); glCompileShader(fragment); checkCompileErrors(fragment, "FRAGMENT"); glAttachShader(id, vertex); glAttachShader(id, fragment); glLinkProgram(id); checkCompileErrors(id, "PROGRAM"); glDeleteShader(vertex); glDeleteShader(fragment); } void use() { glUseProgram(id); //激活当前着色器程序使 OpenGL 渲染管线使用这个 program 进行绘制后续 draw call 都会使用该着色器逻辑 } private: std::string readFile(const std::string& path) { std::ifstream file; std::stringstream buffer; // 尝试打开文件 file.exceptions(std::ifstream::failbit | std::ifstream::badbit); try { file.open(path); buffer << file.rdbuf(); // 读取全部内容 file.close(); return buffer.str(); } catch (const std::exception& e) { std::cout << "ERROR::SHADER::FILE_NOT_READ: " << e.what() << std::endl; return ""; } } void checkCompileErrors(GLuint shader, std::string type) //用于检查着色器编译或程序链接是否出错 { GLint success; GLchar infoLog[1024]; if (type != "PROGRAM") { glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(shader, 1024, NULL, infoLog); std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; } } else { glGetProgramiv(shader, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shader, 1024, NULL, infoLog); std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; } } } }; 运行后没有东西
最新发布
10-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值