Uniform 块

std140 内存布局规则

OpenGL 提供了不同的 layout 策略来定义 Uniform 块的内存布局。最常用的是 std140,其规则如下:


Uniform 块声明示例

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
a float 4 4 0
b vec2 8 8 8
c vec3 12 16 16
d float 4 4 32
e mat4 64 16 48

总大小 = 48 + 64 = 112 字节


查询 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 名称。


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

CPP
#include <glm/glm.hpp>
#include <cstddef>  // for std::byte
#include <cstdint>  // for fixed width types

struct alignas(16) ExampleBlockData {
    float a;                   // offset = 0
    std::byte pad0[12];        // 填充到 offset 16

    glm::vec2 b;               // offset = 16
    std::byte pad1[8];         // 填充到 offset 32

    glm::vec3 c;               // offset = 32
    std::byte pad2[4];         // 填充到 offset 48

    float d;                   // offset = 48
    std::byte pad3[12];        // 填充到 offset 64

    glm::mat4 e;               // offset = 64,占用 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, bufferSize, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
点击展开查看更多

绑定 UBO

绑定 UBO 到 Uniform Block Binding Point:

C
GLuint bindingPoint = 0;
glUniformBlockBinding(programID, blockIndex, bindingPoint);
glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, ubo);
点击展开查看更多

注意


写入数据

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

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);
点击展开查看更多

版权声明

作者: Chaim

链接: https://chaim.eu.org/posts/uniform%E5%9D%97-%E5%92%8C-ubo/

许可证: 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 快捷键