结构的 glBufferSubData 偏移量
glBufferSubData offsets for structs
我实际上正在使用 OpenGL 3.3 渲染引擎,我正试图在我的场景中创建动态数量的灯光。
为此,我正在使用统一缓冲区对象 (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_angle
和 f_attenuation
的数据。
这是在 CPU 上执行的代码,我用它来写入缓冲区数据,其中 focal_lights
是一个包含 FocalLight
class 实例的向量(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
,我可以使用之前定义的偏移量读取它,但与使用简单的浮点数无关。我确定缓冲区的绑定是正确的,我知道问题出在 glBufferData
或 glBufferSubdata
代码上。
有人看到问题了吗?
终于成功了,感谢 Rabbid76:
2 focal lights, 2 directional lights and one point light
在将数据绑定到 std140
标准统一块布局时,您必须考虑特殊的对齐规则。
见OpenGL 4.6 API Compatibility Profile Specification; 7.6.2.2 Standard Uniform Block Layout; page 144
When the std140 layout is specified, the offset of each uniform in a uniform block can be derived from the definition of the uniform block by applying the set of rules described below.
- If the member is a scalar consuming N basic machine units, the base alignment is N
....
- If the member is a three-component vector with components consuming N
basic machine units, the base alignment is 4N.
....
If the member is a structure, the base alignment of the structure is N, where N is the largest base alignment value of any of its members, and rounded up to the base alignment of a vec4. The individual members of this substructure
are then assigned offsets by applying this set of rules recursively, where the base offset of the first member of the sub-structure is equal to the aligned offset of the structure. The structure may have padding at the end; the base offset of the member following the sub-structure is rounded up to the next multiple of the base alignment of the structure.
If the member is an array of S structures, the S elements of the array are laid out in order, according to rule (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
}
只需调用一次缓冲区数据即可。
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))
我实际上正在使用 OpenGL 3.3 渲染引擎,我正试图在我的场景中创建动态数量的灯光。
为此,我正在使用统一缓冲区对象 (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_angle
和 f_attenuation
的数据。
这是在 CPU 上执行的代码,我用它来写入缓冲区数据,其中 focal_lights
是一个包含 FocalLight
class 实例的向量(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
,我可以使用之前定义的偏移量读取它,但与使用简单的浮点数无关。我确定缓冲区的绑定是正确的,我知道问题出在 glBufferData
或 glBufferSubdata
代码上。
有人看到问题了吗?
终于成功了,感谢 Rabbid76: 2 focal lights, 2 directional lights and one point light
在将数据绑定到 std140
标准统一块布局时,您必须考虑特殊的对齐规则。
见OpenGL 4.6 API Compatibility Profile Specification; 7.6.2.2 Standard Uniform Block Layout; page 144
When the std140 layout is specified, the offset of each uniform in a uniform block can be derived from the definition of the uniform block by applying the set of rules described below.
- If the member is a scalar consuming N basic machine units, the base alignment is N
....
- If the member is a three-component vector with components consuming N basic machine units, the base alignment is 4N.
....
If the member is a structure, the base alignment of the structure is N, where N is the largest base alignment value of any of its members, and rounded up to the base alignment of a vec4. The individual members of this substructure are then assigned offsets by applying this set of rules recursively, where the base offset of the first member of the sub-structure is equal to the aligned offset of the structure. The structure may have padding at the end; the base offset of the member following the sub-structure is rounded up to the next multiple of the base alignment of the structure.
If the member is an array of S structures, the S elements of the array are laid out in order, according to rule (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
}
只需调用一次缓冲区数据即可。
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))