Uniform Block

Uniform Block 有3种常见的布局规则:

  • packed:不同OpenGL实现存在布局差异,不同program也可能存在布局差异
  • shared (默认布局):不同OpenGL实现存在布局差异,不同program使用相同定义无布局差异
  • std140:均不存在布局差异

std140 内存布局规则

N = 1 个基本机器单位 = 4 字节

类型 对齐要求 对齐要求: 字节数
标量 (float, int…) N 4
vec2 2N 8
vec3 4N 16
vec4 4N 16
数组的每个元素 4N(按 vec4 对齐) 16
矩阵 (列主序 CxR) 等同于 C 个 vec-R 的数组 每列 16
结构体 4N(按 vec4 对齐) 16

偏移计算公式

TEXT
成员偏移 = ceil(上一个成员结束位置 / 本成员对齐值) × 本成员对齐值
点击展开查看更多

实际偏移量 = 上一个成员结束位置向上取整到对齐值的整数倍。Block 第一个成员偏移为 0。


内存布局计算例子

GLSL
layout(std140) uniform ExampleBlock {
    float a;      // offset 0
    vec2 b;       // offset 8
    vec3 c;       // offset 16(实际占 16 字节)
    float d;      // offset 32
    mat4 e;       // offset 48(占 64 字节)
};
点击展开查看更多
成员 类型 对齐要求 偏移 (offset) 占用字节 填充 (padding)
a float 4 0 4 4 字节 (offset 4-7)
b vec2 8 8 8 0
c vec3 16 16 12 0 (但 offset 28-31 被 d 占用)
d float 4 28 4 0
e mat4 16 32 64 (4×16) 0

总大小 = 96 字节

TEXT
[ 字节偏移 ] : [   0-3   ][   4-7   ][  8-11  ][   12-15  ]
---------------------------------------------------------
Chunk 0    : [ float a ][ PADDING  ][        vec2 b      ]  <- 16 Bytes
Chunk 1    : [         vec3 c                 ][ float d ]  <- 16 Bytes
Chunk 2    : [               mat4 e (列 0)                ]  <- 16 Bytes
Chunk 3    : [               mat4 e (列 1)                ]  <- 16 Bytes
Chunk 4    : [               mat4 e (列 2)                ]  <- 16 Bytes
Chunk 5    : [               mat4 e (列 3)                ]  <- 16 Bytes
---------------------------------------------------------
总计占用 6 个 Chunk = 96 Bytes
点击展开查看更多

C++中声明内存对齐的结构体

CPP
#include <cstddef>      // std::byte

struct ExampleBlock {
    float a;                    // 偏移量 0,大小 4
    std::byte _padding1[4];     // 偏移量 4,大小 4 (显式填充,为了对齐接下来的 vec2)
    
    glm::vec2 b;                // 偏移量 8,大小 8
    
    glm::vec3 c;                // 偏移量 16,大小 12
    float d;                    // 偏移量 28,大小 4 (填补 vec3 的空隙)
    
    glm::mat4 e;                // 偏移量 32,大小 64 (列主序矩阵)
};
点击展开查看更多

UBO(Uniform Buffer Object)

Uniform Buffer Object(UBO) 是一种 GPU 缓冲对象,用于存储 Uniform 块的数据。 ->

创建 UBO 并上传数据

C
GLuint ubo;
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, sizeof(ExampleBlock), &obj, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
点击展开查看更多

绑定 UBO

Block Index:着色器内部的"编号",类似 Uniform 的 Location Binding Point:OpenGL 全局的"插槽编号",类似纹理单元/纹理插槽

使用 Binding Point

GLSL

GLSL
layout(std140, binding = 0) uniform ExampleBlock {
	...
}
点击展开查看更多

C++

CPP
// 绑定到 binding = 0
glBindBufferBase(GL_UNIFORM_BUFFER_OBJECT, 0, ubo);
点击展开查看更多

这种方法非常适合帧常量(在帧内无变化,如LightDirection),设置一次,多个Shader Program 共享。

使用 Block Index

GLSL

GLSL
layout(std140) uniform ExampleBlock {
	...
}
点击展开查看更多

C++

C
// 与纹理单元相同,都需要将UBO绑定到一个指定的插槽上
GLuint bindingPoint = 0;
glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, ubo);
glUniformBlockBinding(programID, blockIndex, bindingPoint);
点击展开查看更多

写入数据

方法一:直接写入整块数据

C
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferSubData(GL_UNIFORM_BUFFER, 0, bufferSize, dataPointer);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
点击展开查看更多

方法二:映射写入(适用于频繁更新)

C
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
void* ptr = glMapBufferRange(GL_UNIFORM_BUFFER, 0, bufferSize, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
memcpy(ptr, dataPointer, bufferSize);
glUnmapBuffer(GL_UNIFORM_BUFFER);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
点击展开查看更多

附:查询 Uniform 块信息

使用以下 API 查询着色器中 Uniform 块的结构和布局信息:

C
GLuint blockIndex = glGetUniformBlockIndex(programID, "ExampleBlock");

// 获取块大小
GLint blockSize;
glGetActiveUniformBlockiv(programID, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);

// 获取块中所有 uniform 的索引
GLint uniformIndices[NUM_UNIFORMS];
glGetUniformIndices(programID, uniformNames, uniformIndices);

// 获取每个 uniform 在块中的偏移
GLint uniformOffsets[NUM_UNIFORMS];
glGetActiveUniformsiv(programID, NUM_UNIFORMS, uniformIndices, GL_UNIFORM_OFFSET, uniformOffsets);
点击展开查看更多

其中 uniformNames 是一个 const char* 数组,列出了 Uniform 名称。

版权声明

作者: Cheyne Xie

链接: https://chaim.eu.org/posts/44914a86/

许可证: CC BY-NC-SA 4.0

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Please attribute the source, use non-commercially, and maintain the same license.

开始搜索

输入关键词搜索文章内容

↑↓
ESC
⌘K 快捷键