结构的 glBufferSubData 偏移量

Posted

技术标签:

【中文标题】结构的 glBufferSubData 偏移量【英文标题】:glBufferSubData offsets for structs 【发布时间】:2018-04-12 13:43:30 【问题描述】:

我实际上正在使用 OpenGL 3.3 渲染引擎,我正试图在我的场景中创建动态数量的灯光。

为此,我正在使用统一缓冲区对象 (UBO),当我尝试通过 UBO 传递数据时遇到问题,该 UBO 将在具有不同类型数据的结构中读取或写入。

我为点光源和定向光源做了这个,一切都很好,因为我只使用vec3 数据。问题是当我定义焦点灯时,结构是:

#version 330 core

#define MAX_NUM_TOTAL_LIGHTS 100
...
struct FocalLight
    vec3 f_light_position;
    vec3 f_light_direction;
    vec3 f_light_diffuse_intensity;
    vec3 f_light_specular_intensity;
    float f_apperture_angle;
    float f_attenuation;
;
layout(std140) uniform focalLights
    FocalLight f_lights[MAX_NUM_TOTAL_LIGHTS];
;

嗯,位置、方向、漫反射强度和镜面反射强度都很好,我的片段从缓冲区正确接收了这些数据。但我无法读写f_apperture_anglef_attenuation 的数据。

这是在 CPU 上执行的代码,我用来写入缓冲区数据,其中 focal_lights 是一个向量,其中包含我的 FocalLight 类 (std::vector<FocalLight> focal_lights) 的实例,我检查的内容是正确的:

if(block_focal_lights_id != -1) 
    glUniformBlockBinding(programId, block_focal_lights_id, 2);
    //Loading from light vectors
    glGenBuffers(1, &buffer_focal_lights_id);
    glBindBuffer(GL_UNIFORM_BUFFER, buffer_focal_lights_id);
    glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * 24 * focal_lights.size(), 0, GL_DYNAMIC_DRAW);
    int offset = 0;
    for (unsigned int i=0; i<focal_lights.size(); i++) 
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].position);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].direction);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].diffuse_intensity);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].specular_intensity);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float), &focal_lights[i].apperture_angle);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float), &focal_lights[i].attenuation);
        offset += 16;
    

我尝试将我的f_apperture_angle 的数据类型更改为vec3,我可以使用之前定义的偏移量读取它,但对于使用简单的浮点数没有任何意义。我确定缓冲区的绑定是正确的,并且我知道问题出在glBufferDataglBufferSubdata 代码上。

有人发现问题了吗?

终于成功了,感谢Rabbid76: 2 focal lights, 2 directional lights and one point light

【问题讨论】:

我做到了,但我认为每个变量使用一个浮点数更干净。 尝试不同的内存布局,通过layout(std430) uniform focalLights而不是std140 【参考方案1】:

在将数据绑定到std140 标准统一块布局时,您必须考虑特殊的对齐规则。

见OpenGL 4.6 API Compatibility Profile Specification; 7.6.2.2 Standard Uniform Block Layout; page 144

当指定std140布局时,可以通过应用下面描述的规则集从统一块的定义中推导出统一块中每个统一块的偏移量。

    如果成员是消耗 N 个基本机器单元的标量,则基本对齐为 N

....

    如果成员是一个三分量向量,其分量消耗 N 基本机器单元,基本对齐为 4N。

....

    如果成员是结构,则结构的基对齐为 N,其中 N 是其任何成员的最大基对齐值,并向上舍入到 vec4 的基对齐。此子结构的各个成员 然后通过递归应用这组规则来分配偏移量,其中子结构的第一个成员的基本偏移量等于结构的对齐偏移量。该结构的末尾可能有填充;子结构后面的成员的基偏移量向上舍入到结构基对齐的下一个倍数。

    如果成员是 S 结构的数组,则数组的 S 元素按照规则(9)按顺序排列。

当您将此规则应用于您的数据结构时,会产生以下偏移量:

struct FocalLight                    // size 80 (rule 9 and 10)

    vec3 f_light_position;           // offset 0  (rule 3 and 10)
    vec3 f_light_direction;          // offset 16 (rule 3)
    vec3 f_light_diffuse_intensity;  // offset 32 (rule 3)
    vec3 f_light_specular_intensity; // offset 48 (rule 3)
    float f_apperture_angle;         // offset 60 (rule 1)
    float f_attenuation;             // offset 64 (rule 1)
;
layout(std140) uniform focalLights
    FocalLight f_lights[MAX_NUM_TOTAL_LIGHTS];
;

绑定数据:

int offset = 0;
for (unsigned int i=0; i<focal_lights.size(); i++) 

    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].position);

    offset += 16; // rule 3
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].direction);

    offset += 16; // rule 3
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].diffuse_intensity);

    offset += 16; // rule 3
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].specular_intensity);

    offset += 12; // rule 1
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float), &focal_lights[i].apperture_angle);

    offset += 4; // rule 1
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float), &focal_lights[i].attenuation);

    offset += 16; // rules 9 and 10

【讨论】:

我稍后再试,非常感谢,我对第 4 个偏移增量有疑问,因为当你有一个浮点数来填充它时,在 GPU 上丢弃这 4 个字节是没有意义的。【参考方案2】:

只需一次调用缓冲数据即可。

glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(FocalLight)*focal_lights.size(), focal_lights);

使用多个小缓冲区数据副本会阻塞 API 和驱动程序,导致性能非常差。

要处理 std140(或者最好是 std430)的对齐,您只需要在 c++ 端的结构中添加填充或重新排序成员。

struct FocalLight
  vec3 f_light_position;
  float f_apperture_angle;
  vec3 f_light_direction;
  float f_attenuation;
  vec3 f_light_diffuse_intensity;
  float pad1;
  vec3 f_light_specular_intensity;
  float pad2;
;

或者,您可以使用编译器内置指令进行对齐 __declspec(align(16))

【讨论】:

以上是关于结构的 glBufferSubData 偏移量的主要内容,如果未能解决你的问题,请参考以下文章

glBufferData 和 glBufferSubData 偏移量

C 语言结构体 ( 结构体偏移量计算 | 代码示例 )

如何获取 Kafka 偏移量以进行结构化查询以进行手动可靠的偏移量管理?

计算结构体内元素的偏移量宏

如果数组大小发生变化以及定义的宏如何在此处计算偏移量,为啥 C 结构中的字符数组的偏移量会有所不同? [复制]

C / C ++编译器可以报告结构成员偏移量[重复]