我如何在 GLSL 中正确声明一个统一的结构数组,以便我可以将 UBO 指向它?

How do I properly declare a uniform array of structs in GLSL so that I can point a UBO at it?

以下glsl代码出现在我的片段着色器中。结构定义不会导致任何问题,但我尝试将其用作统一数组的类型会导致 "invalid operation" 错误,这并不是特别有用。

struct InstanceData
{
    vec3 rotation;
    vec3 scale;
    mat4 position;
};

layout (std140) uniform InstanceData instances[100];

如何正确构建此代码,使其编译无误,从而为我填充数据做好准备?请注意,我使用的是核心配置文件版本 4.5。

编辑: 似乎与layout (std140)的使用有关。删除该部分允许编译代码,但我不需要它来确保 glsl 编译器以可预测的方式打包结构数据吗?

编辑: 仍然无法正常工作。我的整个顶点着色器代码如下所示:

#version 450

layout(location=0) in vec4 in_Position;
layout(location=1) in vec4 in_Color;
out vec4 ex_Color;
flat out int ex_Instance;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

// ------ preliminary addition of uniform block to be used soon ------
struct layout (std140) InstanceData
{
    vec3 rotation;
    vec3 scale;
    mat4 position;
};

layout (std140, binding = 0) uniform InstanceData
{
    InstanceData instances[100];
};
// -------------------------------------------------------------------

void main(void)
{
    gl_Position = (projectionMatrix * viewMatrix * modelMatrix) * in_Position;
    ex_Color = in_Color;
}

请注意,我还没有在外部编写任何代码来填充统一缓冲区,正如您在上面看到的那样,我也没有调整我的代码来使用数据。我只是希望它最初按原样编译和工作(即已声明但未使用),此时我将添加额外的代码以开始使用它。如果程序不喜欢开头的声明,那么走那么远也没有意义。

编辑: 最后使用着色器信息日志解决了问题,如下所示:

GLint infoLogLength;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar* strInfoLog = new GLchar[infoLogLength + 1];
glGetShaderInfoLog(id, infoLogLength, NULL, strInfoLog);

简而言之,正如 Anton 所说,我使用 layout 不正确,我的统一块与我的结构同名,给编译器造成了各种混乱。

要以可移植的方式执行此操作,您应该使用统一缓冲区对象。

目前,您的结构使用 3+3+16=22 个浮点组件,您正试图构建一个包含 100 个这样的组件的数组。 OpenGL 实现只需要在任何阶段支持 1024 个浮点统一组件,而你的数组需要 2200 个。

统一缓冲区对象将允许您存储多达 64 KiB(最小)的数据;远远超过上述限制。但是,在使用 UBO 时需要注意数据对齐,这就是您尝试使用的 layout (std140) 限定符的目的。

struct InstanceData
{
    vec3 rotation;
    vec3 scale;
    mat4 position;
};

// Uniform block named InstanceBlock, follows std140 alignment rules
layout (std140, binding = 0) uniform InstanceBlock {
  InstanceData instances [100];
};

上面的结构未正确对齐std140,使用时需要小心。

这是数据的实际布局方式:

struct InstanceData
{
  vec3  rotation;  // 0,1,2
  float padding03; // 3

  vec3  scale;     // 4,5,6
  float padding07; // 7

  mat4  position;  // 8-23
} // Size: 24 * sizeof (float)

vec3 类型在 GLSL 中被视为与 vec4 相同,而 mat4 实际上是一个包含 4 vec4 的数组,因此这意味着它们都需要从4-浮动边界。 GLSL 自动插入填充以满足这些对齐规则;我在上面所做的更改是为了向您展示在 C 中编写此数据结构的正确方法。您必须在结构中考虑 2 个浮点数的隐式填充。


关于您的编辑,在您开始使用缓冲区对象之前,您不必担心 GLSL 如何打包 struct

如果不使用统一缓冲区对象,则必须使用 glUniform3f (...) 之类的函数来设置统一。这些函数不会直接向您公开数据结构,因此打包并不重要。要设置实例 N 的值,您可以调用 glUniform3f (...) 使用“instances [N].rotation”和“instances [N].scale”的位置以及“instances”上的 glUniformMatrix4fv (...) [N].position.

这将需要 300 API 次调用来初始化结构的所有 100 个实例,因此您可以了解为什么 UBO 更实用(甚至忽略上述 1024 限制)。