文章目录
- 使用流程
- 实践
- 根据 ubo 名称,获取所有使用了这个 ubo 块的在该 shader 中 ubo 块的索引
- 根据该 ubo 块中的每个属性名字,获取该 ubo 块的每个属性的索引值
- 根据该 ubo 块中每个属性索引值,获取 offset(偏移)、size(大小,这里的大小理解为:元素数量)、type(数据类型)
- 给这个 ubo 块 分配的 binding point index
- 创建一个 GL_UNIFORM_BUFFER 的缓存对象 ubo
- 并将 ubo 绑定到 上面分配的 binding point index 中
- 获取该 ubo 块在 GLSL 编译器分配的字节大小
- 使用该字节大小,创建对应应用层的缓存数据 buffer
- 在需要的时候对该应用层的 buffer 缓存数据进行更新
- 封装 UBO_DATA
- 驱动 UBO 更新
- 外部使用接口
- 与 ShaderProgram.setGlobalXXX 的区别
- References
前些篇:
- LearnGL - 15 - Skybox - 天空盒
- LearnGL - 15.1 - Reflection - 反射效果
- LearnGL - 15.2 - Refraction - 折射效果
- LearnGL - 15.3 - 渲染顺序
做了一些简单的 类环境映射的方法,还有其他的一些优化
这一篇:将使用 UBO 来实现 Shader Program Object 之间的数据共享
(注意是 Shader Program Object - Shader 程序对象,而不是 Shader 着色器对象,因为 着色器之间的共享数据在原来使用的 uniform 变量本身就是共享的,但是要在 Shader 着色器程序对象之间共享,就是能 UBO 或是下面简单介绍到的 SSBO)
建议先阅读 OpenGL - UBO 前置内容
本人才疏学浅,如有什么错误,望不吝指出。
数据共享有 UBO(Uniform Block Object - Uniform 块对象) 和 SSBO(Shader Storage Buffer Object - 存储缓存对象,或是叫 SBO 也行)
- UBO 一般用于绘制用,这里使用的是 UBO 的方式,它是 只读 的
- SSBO 以后再使用,一般用通用计算程序使用(类似Compute Shader),它与 UBO 最大区别于,它是 可读写 的
使用流程
- 根据 ubo 名称,获取所有使用了这个 ubo 块的在该 shader 中 ubo 块的索引
- 根据该 ubo 块中的每个属性名字,获取该 ubo 块的每个属性的索引值
- 根据该 ubo 块中每个属性索引值,获取 offset(偏移)、size(大小,这里的大小理解为:元素数量)、type(数据类型)
- 给这个 ubo 块 分配的 binding point index(这个下面会特别说明)
- 创建一个 GL_UNIFORM_BUFFER 的缓存对象 ubo
- 并将 ubo 绑定到 上面分配的 binding point index 中
- 获取该 ubo 块在 GLSL 编译器分配的字节大小
- 使用该字节大小,创建对应应用层的缓存数据 buffer
- 在需要的时候对该应用层的 buffer 缓存数据进行更新
实践
根据 ubo 名称,获取所有使用了这个 ubo 块的在该 shader 中 ubo 块的索引
首先我们得在 GLSL shader 中声明好 ubo 的内容
// camera
// layout (std140)
uniform Camera_UBO {
// world pos
vec3 _CamWorldPos; // 镜头世界坐标
// clear flag
int ClearType; // 渲染相机的 clear type
vec3 ClearColor; // 清理的颜色
// camera transform
mat4 vMat; // v 矩阵
mat4 pMat; // p 矩阵
};
然后在应用层获取这个 “Camera_UBO” 名字的 ubo 块在该 shader 中的索引值
使用到 API glGetUniformBlockIndex
// 获取指定 name 的 shader 中的 ubo 索引
GLuint ubo_idx = glGetUniformBlockIndex(shader, name);
if (ubo_idx == -1) {
// noops
std::cout << "Shader(" << shader << ") have no ubo(" << name << "\n";
return;
}
这里简单介绍一下:
GLuint glGetUniformBlockIndex( GLuint program,
const GLchar *uniformBlockName);
- program 是指定的 shader 程序对象
- uniformBlockName 是要获取 ubo 的名字,对应我们上面 shader 代码中的
"Camera_UBO"
如果该 shader 程序对象没有使用 uniformBlockName 指定的 ubo,那么返回 -1
根据该 ubo 块中的每个属性名字,获取该 ubo 块的每个属性的索引值
需要使用到 API glGetUniformIndices
const size_t count = name_map_vec.size();
const char** names = (const char**)malloc(sizeof(size_t) * count);
for (size_t i = 0; i < count; i++) {
*(names + i) = name_map_vec[i].name.c_str();
}
// 获取每个 uniform 块中对应 names 属性的索引,并保存到 indices
glGetUniformIndices(shader, count, names, indices);
glGetUniformIndices 的原型
void glGetUniformIndices( GLuint program,
GLsizei uniformCount,
const GLchar **uniformNames,
GLuint *uniformIndices);
- program 就 shader 程序对象
- uniformCount 就是指定 uniformNames 和 uniformIndices 的数量,也就是该 ubo 块中需要查询的属性名字的数量,一般我们会将所有的都列进去
- uniformNames 就是 ubo 块中每个属性的名字的字符串数组
- uniformIndices 就是获取到后的结果 都保存在 uniformIndices 中,它保存的就是每个 ubo 块中对应名字的属性在该 ubo 块中的索引值
根据该 ubo 块中每个属性索引值,获取 offset(偏移)、size(大小,这里的大小理解为:元素数量)、type(数据类型)
const size_t count = name_map_vec.size();
const char** names = (const char**)malloc(sizeof(size_t) * count);
for (size_t i = 0; i < count; i++) {
*(names + i) = name_map_vec[i].name.c_str();
}
GLuint* indices = (GLuint*)malloc(sizeof(GLuint) * count); // 每个 uniform 在 ubo 中的索引值
GLint* size = (GLint*)malloc(sizeof(GLint) * count); // 每个 uniform 的数组元素数量,不是数组则为1
GLint* offset = (GLint*)malloc(sizeof(GLint) * count); // 每个 uniform 在 ubo 中的偏移值
GLint* type = (GLint*)malloc(sizeof(GLint) * count); // 每个 uniform 的类型
// 获取每个 uniform 块中对应 names 属性的索引,并保存到 indices
glGetUniformIndices(shader, count, names, indices);
// 获取 indices 中每个属性索引 对应的地址字节偏移
glGetActiveUniformsiv(shader, count, indices, GL_UNIFORM_OFFSET, offset);
// 获取 indices 中每个属性索引 对应的类型数组长度,如果不是数组,则为 1
glGetActiveUniformsiv(shader, count, indices, GL_UNIFORM_SIZE, size);
// 获取 indices 中每个属性索引 对应的数据类型
glGetActiveUniformsiv(shader, count, indices, GL_UNIFORM_TYPE, type);
GLint* real_size = (GLint*)malloc(sizeof(GLint) * count); // uniform 中每个属性的真实字节大小
for (size_t i = 0; i < count; i++) {
// 根据数组元素的数据类型 与 数组元素个数 相乘,求得真实大小
real_size[i] = size[i] * TypeSize(type[i]);
}
给这个 ubo 块 分配的 binding point index
GLuint _binding_point_idx = -1; // 分配到的 UBO 缓存列表中的 binding point 的索引
...
// 如果该 ubo 未分配过 binding point 索引值,则分配一个 ubo binding 索引
if (_binding_point_idx == -1) {
_binding_point_idx = binding_idx_counter++;
}
else {
assert(false);
}
...
// 将 shader 的第 ubo_idx 个 ubo 绑定到全局 ubo index 索引
glUniformBlockBinding(shader, ubo_idx, _binding_point_idx);
GLuint _binding_point_idx = -1;
就是我们 ubo 管理类中的一个成员,-1 说明它还未分配过,一般是由于没有 shader 找到过有这个 ubo 的使用
这个 _binding_point_idx
有必要特别说明一下,它到底是个啥:
引用 高级GLSL 的一张图:(这张图说明足够清晰,我就不再自己画多一张了)
从图中可以看到 Binding points 只是一些索引值,你也可以理解为 Binding points 是 每个 OpenGL Context 上下文对象中都分配好的一些 buffer 插槽,需要绑定使用到全局使用的都可以先将这些 buffer 对象绑定到这些插槽中。
如果我们将所有用到这些 ubo 都绑定到对应的全局插槽中。
这些每个 shader 程序对象中使用到的 ubo 都是同一个缓存。
这么制作的好处:
- 提升性能 - 减少内存使用,相比之前每个 shader 中相同的一些冗余数据,它减少了GPU内存
- 提升性能 - 应用层只需要在渲染前设置好该 ubo 的缓存数据并上传到 GPU 即可(只需要设置一次,后续所有用到该 ubo 的 shader 都有这些数据)
创建一个 GL_UNIFORM_BUFFER 的缓存对象 ubo
这个没啥好说,与之前的一些文章中说明的 VBO,EBO 的缓存对象的创建都一样,使用到 API glGenBuffers
// 创建 ubo
ubo = new GLuint();
glGenBuffers(1, ubo);
VBO,EBO相关之前的文章,有兴趣可以参考:
- LearnGL - 02 - DrawTriangle - VBO/Shader
- LearnGL - 02.1 - DrawTriangle_Extension - VBO/Shader
- LearnGL - 03 - DrawQuad - VBO/EBO
并将 ubo 绑定到 上面分配的 binding point index 中
上面创建好缓存对象后,接着就将它绑定到 当前的 GL_UNIFORM_BUFFER 目标中,还是上面所说的文章中有介绍的 API glBindBuffer、glBindBufferBase,其中 glBindBufferBase 在前面说的 前置篇也有详细说明:OpenGL - UBO 前置内容
// 当前要设置的 ubo 信息
glBindBuffer(GL_UNIFORM_BUFFER, *ubo);
// 将 binding point 的索引值 与 ubo 数据缓存对象
glBindBufferBase(GL_UNIFORM_BUFFER, _binding_point_idx, *ubo);
先通过 glBindBuffer
将创建的 ubo
设置为当前要操作的 GL_UNIFORM_BUFFER
目标
再通过 glBindBufferBase
来将 ubo
对象绑定到前面介绍的全局的 buffer
插槽,插槽位置为:前面分配好的 _binding_point_idx
获取该 ubo 块在 GLSL 编译器分配的字节大小
使用到 API glGetActiveUniformBlockiv
// 获取 shader 中 ubo 的大小
GLint glsl_uboSize = 0;
glGetActiveUniformBlockiv(shader, ubo_idx, GL_UNIFORM_BLOCK_DATA_SIZE, &glsl_uboSize);
这样我们就可以查询 GLSL 中编译器分配给该 ubo 块的内存大小了,并存储与 glsl_uboSize
指针中
该函数原型声明:
void glGetActiveUniformBlockiv( GLuint program,
GLuint uniformBlockIndex,
GLenum pname,
GLint *params);
- program 就是 shader 程序对象
- uniformBlockIndex 该 ubo 块在该 shader 程序中的索引值
- pname 是要获取这个块的属性标记,这里我们要获取 ubo 块的大小,填入:
GL_UNIFORM_BLOCK_DATA_SIZE
即可 - params 填入用于存储查询结果的 指针
使用该字节大小,创建对应应用层的缓存数据 buffer
还是与之前文章中使用的 API glBufferData
// 分配 glsl 中当前要配置的 ubo 缓存大小
glBufferData(GL_UNIFORM_BUFFER, glsl_uboSize, NULL, GL_STATIC_DRAW);
// TODO : buffer 可以优化成外部通用的一个缓存
// 分配前应用层的 ubo 缓存大小
buffer = calloc(glsl_uboSize, 1);
buffer_size = glsl_uboSize;
if (buffer == NULL) {
std::cerr << "out of memory!" << __FILE__ << ", " << __LINE__ << "\n";
exit(-1);
}
这样就给当前要操作的 ubo 缓存对象分配了 glsl_uboSize
返回的大小
在需要的时候对该应用层的 buffer 缓存数据进行更新
最后是给该 ubo 块数据进行更新
主要使用到 API glBufferSubData
void UBO_DATA::upload() {
if (_binding_point_idx == -1 || buffer_size == 0 || buffer == NULL) {
// 对应的 shader 没有这个 ubo
return;
}
// 更新缓存
for (size_t i = 0; i < name_map_vec.size(); i++) {
UBO_Name_Ptr_Map names = name_map_vec[i];
memcpy((void*)((size_t)buffer + offset[i]), (void*)names.ptr, real_size[i]);
}
// 当前要设置的 ubo 信息
glBindBuffer(GL_UNIFORM_BUFFER, *ubo);
// 设置 应用层 buffer 缓存指针的 buffer_size 的数据到 当前要设置的 ubo 缓存对象中
glBufferSubData(GL_UNIFORM_BUFFER, 0, buffer_size, buffer);
}
函数声明原型:
void glBufferSubData( GLenum target,
GLintptr offset,
GLsizeiptr size,
const GLvoid * data);
void glNamedBufferSubData( GLuint buffer,
GLintptr offset,
GLsizei size,
const void *data);
- target 传入
GL_UNIFORM_BUFFER
即可 - offset 要设置的缓存的内存地址偏移
- size 要给 offset 位置设置后续的 size 个内存字节
- data 要设置到当前
GL_UNIFORM_BUFFER
目标中的数据指针(将 data 的数据复制过去)
封装 UBO_DATA
// my_ubo_data.h
/* author : jave.lin
uniform block object 数据管理
*/
#ifndef _MY_UBO_DATA__H_
#define _MY_UBO_DATA__H_
#include<algorithm>
#include<unordered_set>
#include"glad/glad.h"
#include"my_gameobject_etc.h"
namespace my_util {
//
// 声明
//
// uniform 块中对应 属性的名字与应用层的数据指针映射
struct UBO_Name_Ptr_Map {
public:
std::string name;
size_t ptr;
};
class UBO_DATA : public Object {
private:
static size_t TypeSize(GLenum type);
static GLuint binding_idx_counter;
public:
UBO_DATA(const char* name);
inline void addNameMap(UBO_Name_Ptr_Map name_map);
inline const GLuint ubo_id() const;
void binding(GLuint shader, size_t object_id);
void upload();
private:
~UBO_DATA(); // 应用 析构,外部统一 DESTROY(Object*)
GLuint _binding_point_idx = -1; // 分配到的 UBO 缓存列表中的 binding point 的索引
GLuint* ubo = NULL; // 该 ubo (uniform block object) 的ID
void* buffer = NULL; // 对应应用层的缓存指针
size_t buffer_size = 0; // 对应应用层的缓存指针数据的大小
GLint* offset = NULL; // 每个 uniform 块中对应成员的内存偏移(字节)
GLint* real_size = NULL; // 每个 uniform 块中对应成员的内存大小(字节)
std::vector<UBO_Name_Ptr_Map> name_map_vec; // 每个 uniform 块名字对应的应用层的数据指针
std::unordered_set<GLuint> binded_shader_obj_id_set; // 该 ubo 已绑定过的 shader id
};
//
// 实现
//
GLuint UBO_DATA::binding_idx_counter = 0;
size_t UBO_DATA::TypeSize(GLenum type) {
size_t size;
#define CASE(Enum, Count, Type) \
case Enum: size = Count * sizeof(Type); break;
switch (type) {
CASE(GL_FLOAT, 1, GLfloat);
CASE(GL_FLOAT_VEC2, 2, GLfloat);
CASE(GL_FLOAT_VEC3, 3, GLfloat);
CASE(GL_FLOAT_VEC4, 4, GLfloat);
CASE(GL_INT, 1, GLint);
CASE(GL_INT_VEC2, 2, GLint);
CASE(GL_INT_VEC3, 3, GLint);
CASE(GL_INT_VEC4, 4, GLint);
CASE(GL_UNSIGNED_INT, 1, GLuint);
CASE(GL_UNSIGNED_INT_VEC2, 2, GLuint);
CASE(GL_UNSIGNED_INT_VEC3, 3, GLuint);
CASE(GL_UNSIGNED_INT_VEC4, 4, GLuint);
CASE(GL_BOOL, 1, GLboolean);
CASE(GL_BOOL_VEC2, 2, GLboolean);
CASE(GL_BOOL_VEC3, 3, GLboolean);
CASE(GL_BOOL_VEC4, 4, GLboolean);
CASE(GL_FLOAT_MAT2, 4, GLfloat);
CASE(GL_FLOAT_MAT2x3, 6, GLfloat);
CASE(GL_FLOAT_MAT2x4, 8, GLfloat);
CASE(GL_FLOAT_MAT3, 9, GLfloat);
CASE(GL_FLOAT_MAT3x2, 6, GLfloat);
CASE(GL_FLOAT_MAT3x4, 12, GLfloat);
CASE(GL_FLOAT_MAT4, 16, GLfloat);
CASE(GL_FLOAT_MAT4x2, 8, GLfloat);
CASE(GL_FLOAT_MAT4x3, 12, GLfloat);
#undef CASE
default:
fprintf(stderr, "%s, %d, Unknown type : 0x%x\n", __FILE__, __LINE__, type);
exit(-1);
break;
}
return size;
}
UBO_DATA::UBO_DATA(const char* name) : Object(name) {
}
UBO_DATA::~UBO_DATA() {
if (ubo != NULL) {
glDeleteBuffers(1, ubo);
delete ubo;
ubo = NULL;
}
if (buffer != NULL) {
free(buffer);
buffer = NULL;
}
if (offset != NULL) {
free(offset);
offset = NULL;
}
if (real_size != NULL) {
free(real_size);
real_size = NULL;
}
name_map_vec.clear();
binded_shader_obj_id_set.clear();
}
inline void UBO_DATA::addNameMap(UBO_Name_Ptr_Map name_map) {
name_map_vec.push_back(name_map);
}
inline const GLuint UBO_DATA::ubo_id() const {
return *ubo;
}
void UBO_DATA::binding(GLuint shader, size_t object_id) {
if (binded_shader_obj_id_set.find(object_id) != binded_shader_obj_id_set.end()) {
// 如果绑定过,则跳过
return;
}
// 记录该 shader 绑定过
binded_shader_obj_id_set.insert(object_id);
// 获取指定 name 的 shader 中的 ubo 索引
GLuint ubo_idx = glGetUniformBlockIndex(shader, name);
if (ubo_idx == -1) {
// noops
std::cout << "Shader(" << shader << ") have no ubo(" << name << "\n";
return;
}
// 判断是否初始化过
if (ubo == NULL) {
const size_t count = name_map_vec.size();
const char** names = (const char**)malloc(sizeof(size_t) * count);
for (size_t i = 0; i < count; i++) {
*(names + i) = name_map_vec[i].name.c_str();
}
GLuint* indices = (GLuint*)malloc(sizeof(GLuint) * count); // 每个 uniform 在 ubo 中的索引值
GLint* size = (GLint*)malloc(sizeof(GLint) * count); // 每个 uniform 的数组元素数量,不是数组则为1
GLint* offset = (GLint*)malloc(sizeof(GLint) * count); // 每个 uniform 在 ubo 中的偏移值
GLint* type = (GLint*)malloc(sizeof(GLint) * count); // 每个 uniform 的类型
// 获取每个 uniform 块中对应 names 属性的索引,并保存到 indices
glGetUniformIndices(shader, count, names, indices);
// 获取 indices 中每个属性索引 对应的地址字节偏移
glGetActiveUniformsiv(shader, count, indices, GL_UNIFORM_OFFSET, offset);
// 获取 indices 中每个属性索引 对应的类型数组长度,如果不是数组,则为 1
glGetActiveUniformsiv(shader, count, indices, GL_UNIFORM_SIZE, size);
// 获取 indices 中每个属性索引 对应的数据类型
glGetActiveUniformsiv(shader, count, indices, GL_UNIFORM_TYPE, type);
GLint* real_size = (GLint*)malloc(sizeof(GLint) * count); // uniform 中每个属性的真实字节大小
for (size_t i = 0; i < count; i++) {
// 根据数组元素的数据类型 与 数组元素个数 相乘,求得真实大小
real_size[i] = size[i] * TypeSize(type[i]);
}
// 如果该 ubo 未分配过 binding point 索引值,则分配一个 ubo binding 索引
if (_binding_point_idx == -1) {
_binding_point_idx = binding_idx_counter++;
}
else {
assert(false);
}
// 创建 ubo
ubo = new GLuint();
glGenBuffers(1, ubo);
// 当前要设置的 ubo 信息
glBindBuffer(GL_UNIFORM_BUFFER, *ubo);
// 将 binding point 的索引值 与 ubo 数据缓存对象
glBindBufferBase(GL_UNIFORM_BUFFER, _binding_point_idx, *ubo);
// 获取 shader 中 ubo 的大小
GLint glsl_uboSize = 0;
glGetActiveUniformBlockiv(shader, ubo_idx, GL_UNIFORM_BLOCK_DATA_SIZE, &glsl_uboSize);
// 分配 glsl 中当前要配置的 ubo 缓存大小
glBufferData(GL_UNIFORM_BUFFER, glsl_uboSize, NULL, GL_STATIC_DRAW);
// TODO : buffer 可以优化成外部通用的一个缓存
// 分配前应用层的 ubo 缓存大小
buffer = calloc(glsl_uboSize, 1);
buffer_size = glsl_uboSize;
if (buffer == NULL) {
std::cerr << "out of memory!" << __FILE__ << ", " << __LINE__ << "\n";
exit(-1);
}
free(type);
free(size);
free(indices);
free(names);
// 设置、记录上每个 uniform 块中每个属性的内存偏移与大小
this->offset = offset;
this->real_size = real_size;
} // end if (ubo == NULL)
// 将 shader 的第 ubo_idx 个 ubo 绑定到全局 ubo index 索引
glUniformBlockBinding(shader, ubo_idx, _binding_point_idx);
}
void UBO_DATA::upload() {
if (_binding_point_idx == -1 || buffer_size == 0 || buffer == NULL) {
// 对应的 shader 没有这个 ubo
return;
}
// 更新缓存
for (size_t i = 0; i < name_map_vec.size(); i++) {
UBO_Name_Ptr_Map names = name_map_vec[i];
memcpy((void*)((size_t)buffer + offset[i]), (void*)names.ptr, real_size[i]);
}
// 当前要设置的 ubo 信息
glBindBuffer(GL_UNIFORM_BUFFER, *ubo);
// 设置 应用层 buffer 缓存指针的 buffer_size 的数据到 当前要设置的 ubo 缓存对象中
glBufferSubData(GL_UNIFORM_BUFFER, 0, buffer_size, buffer);
}
}
#endif
驱动 UBO 更新
驱动 UBO 的更新思路比较简单:
- 尝试对每个 shader 绑定 ubo(如果有使用该 ubo 的话)
- 然后对每个 ubo 的上述更新后更上传到 GPU
void ShaderProgram::uploadUBO() {
// 驱动 UBO 的更新思路比较简单:
// - 尝试对每个 shader 绑定 ubo(如果有使用该 ubo 的话)
// - 然后对每个 ubo 的上述更新后更上传到 GPU
std::vector<ShaderProgram*>::iterator shader_start_it;
std::vector<ShaderProgram*>::iterator shader_end_it;
std::vector<UBO_DATA*>::iterator ubo_start_it = ubo_vec.begin();
std::vector<UBO_DATA*>::iterator ubo_end_it = ubo_vec.end();
ShaderProgram* shader = NULL;
UBO_DATA* ubo_data = NULL;
for (; ubo_start_it != ubo_end_it; ubo_start_it++) {
ubo_data = *ubo_start_it;
shader_start_it = vec.begin();
shader_end_it = vec.end();
for (; shader_start_it != shader_end_it; shader_start_it++) {
shader = *(shader_start_it);
// - 尝试对每个 shader 绑定 ubo(如果有使用该 ubo 的话)
ubo_data->binding(shader->ShaderProgram::id(), shader->Object::id());
}
// 然后对每个 ubo 的上述更新后更上传到 GPU
ubo_data->upload();
}
}
外部使用接口
经过我们封装后,外部使用起来就变得相当简单了:
Shader
// camera
// layout (std140)
uniform Camera_UBO {
// world pos
vec3 _CamWorldPos; // 镜头世界坐标
// clear flag
int ClearType; // 渲染相机的 clear type
vec3 ClearColor; // 清理的颜色
// camera transform
mat4 vMat; // v 矩阵
mat4 pMat; // p 矩阵
};
// scene
// layout (std140)
uniform Scene_UBO {
vec4 _Ambient; // .xyz 环境光颜色, .w 环境光系数
int AmbientType; // 环境光类别,[测试用]
};
// times
// layout (std140)
uniform Time_UBO {
// .x 程序到现在的时间(无缩放时间影响)
// .y 程序到现在的时间(有缩放时间影响)
// .z 帧间隔时间(无缩放时间影响)
// .w 帧间隔时间(有缩放时间影响)
vec4 _Time;
};
CPP 应用层
{
// camera ubo info
UBO_DATA* ubo_data = new UBO_DATA("Camera_UBO");
ubo_data->addNameMap({ "_CamWorldPos", (size_t)&Camera_UBO_Info::_CamWorldPos });
ubo_data->addNameMap({ "ClearType", (size_t)&Camera_UBO_Info::ClearType });
ubo_data->addNameMap({ "ClearColor", (size_t)&Camera_UBO_Info::ClearColor });
ubo_data->addNameMap({ "vMat", (size_t)&Camera_UBO_Info::vMat });
ubo_data->addNameMap({ "pMat", (size_t)&Camera_UBO_Info::pMat });
ShaderProgram::registerUBO(ubo_data);
}
{
// scene ubo info
UBO_DATA* ubo_data = new UBO_DATA("Scene_UBO");
ubo_data->addNameMap({ "_Ambient", (size_t)&Scene_UBO_Info::_Ambient });
ubo_data->addNameMap({ "AmbientType", (size_t)&Scene_UBO_Info::AmbientType });
ShaderProgram::registerUBO(ubo_data);
}
{
// time ubo info
UBO_DATA* ubo_data = new UBO_DATA("Time_UBO");
ubo_data->addNameMap({ "_Time", (size_t)&Time_UBO_Info::_Time });
ShaderProgram::registerUBO(ubo_data);
}
与 ShaderProgram.setGlobalXXX 的区别
如上,我在 ShaderProgram
类中使用宏定义批量的封装过一些 SetGlobalXXX
的接口
但这与我们上面的 UBO
使用是不同的
因为每一个 ShaderProgram
的创建都会自动存放在 std::vector<ShaderProgram*> ShaderProgram::vec
中
ShaderProgram
的销毁也会自动从 ShaderProgram::vec
移除
我们在调用 ShaderProgram::setGlobalXXX
就会对 ShaderProgram::vec
遍历 设置 setXXX
的接口
咋一看,与我们的 ubo 功能有点混淆吗?
其实不一样,setGlobalXXX
的设置目的是为了方便对一些不便于使用 ubo 不支持的数据类型而添加的,这与 Unity 中的 Material.SetGlobalXXX
或是 Shader.SetGlobalXXX
的功能非常类似。
ubo 块是不支持:不透明 的数据类型的,在 OpenGL - UBO 前置内容 有讲到