将 unsigned int 输入属性传递给顶点着色器

Smo*_*ove 6 c++ opengl glsl vertex-buffer glm-math

在顶点数组缓冲区的典型构建中,我尝试将 unsigned int 属性与其他经典属性(顶点、法线、纹理坐标...)一起传递。然而,该属性的值最终以某种方式错误:我不确定该值是否错误或该属性根本没有设置。

从一个简单的例子开始,假设我定义了以下 C++ 输入结构:

struct buffer_data_t
{
    glm::vec3 vertex;
    glm::vec3 normal;
    glm::vec2 texCoords;
};
Run Code Online (Sandbox Code Playgroud)

准备我的顶点数组如下所示:

// Assume this 'shader.attribute(..)' is working and returns the attribute's position
unsigned int shadInputs[] = {
    (unsigned int)shader.attribute("VS_Vertex"),
    (unsigned int)shader.attribute("VS_Normal"),
    (unsigned int)shader.attribute("VS_TexCoords"),
};

glGenBuffers(1, &glBuffer);
glBindBuffer(GL_ARRAY_BUFFER, glBuffer);
glBufferData(GL_ARRAY_BUFFER, vertice.size() * sizeof(buffer_data_t), &vertice[0], GL_STATIC_DRAW);
glGenVertexArrays(1, &glArray);
glBindVertexArray(glArray);
{
    glBindBuffer(GL_ARRAY_BUFFER, glBuffer);
    glVertexAttribPointer(shadInputs[0], 3, GL_FLOAT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 0));
    glVertexAttribPointer(shadInputs[1], 3, GL_FLOAT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 1));
    glVertexAttribPointer(shadInputs[2], 2, GL_FLOAT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 2));
    glEnableVertexAttribArray(shadInputs[0]);
    glEnableVertexAttribArray(shadInputs[1]);
    glEnableVertexAttribArray(shadInputs[2]);
}
glBindVertexArray(0);
Run Code Online (Sandbox Code Playgroud)

顶点着色器输入的定义如下:

in vec3 VS_Vertex;
in vec3 VS_Normal;
in vec2 VS_TexCoords;
Run Code Online (Sandbox Code Playgroud)

另外,就我的示例而言,假设我的片段着色器中有一个纹理采样器,可以在其中使用这些输入 TexCoords:

uniform sampler2D ColourMap;
Run Code Online (Sandbox Code Playgroud)

介绍一下我的问题

到目前为止一切顺利,使用上面的代码我可以成功渲染纹理图元。现在我想根据渲染的面选择不同的颜色贴图。为此,我想引入索引作为顶点属性的一部分。变化是:

C++数据结构:

struct buffer_data_t
{
    glm::vec3 vertex;
    glm::vec3 normal;
    glm::vec2 texCoords;
    unsigned int textureId; // <---
};
Run Code Online (Sandbox Code Playgroud)

准备顶点数组:

unsigned int shadInputs[] = {
    (unsigned int)shader.attribute("VS_Vertex"),
    (unsigned int)shader.attribute("VS_Normal"),
    (unsigned int)shader.attribute("VS_TexCoords"),
    (unsigned int)shader.attribute("VS_TextureId"),
};

// ...

glBindBuffer(GL_ARRAY_BUFFER, glBuffer);
glVertexAttribPointer(shadInputs[0], 3, GL_FLOAT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 0));
glVertexAttribPointer(shadInputs[1], 3, GL_FLOAT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 1));
glVertexAttribPointer(shadInputs[2], 2, GL_FLOAT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 2));
glVertexAttribPointer(shadInputs[3], 1, GL_UNSIGNED_INT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 2 + sizeof(glm::vec2))); // <---
glEnableVertexAttribArray(shadInputs[0]);
glEnableVertexAttribArray(shadInputs[1]);
glEnableVertexAttribArray(shadInputs[2]);
glEnableVertexAttribArray(shadInputs[3]);
Run Code Online (Sandbox Code Playgroud)

然后顶点着色器定义新的输入,以及“平坦”输出

in vec3 VS_Vertex;
in vec3 VS_Normal;
in vec2 VS_TexCoords;
in unsigned int VS_TextureId;

...

out flat unsigned int FS_TextureId;
Run Code Online (Sandbox Code Playgroud)

调整片段着色器以将输入平坦化,并且(再次为了示例)颜色图现在是我们可以从中选择的数组:

...
uniform sampler2D ColourMaps[2];
in flat unsigned int FS_TextureId;
...
texture2D(ColourMaps[FS_TextureId], ... );
Run Code Online (Sandbox Code Playgroud)

由于顶点着色器输入属性“VS_TextureId”,这些更改不起作用。我能够通过不使用 unsigned int 类型而是诉诸 vec2 (或 vec3,无论哪种方式都有效)来证明这一点(并找到解决方法)。那是:

对比:

in vec2 VS_TextureId;
out flat int FS_TextureId;
FS_TextureId = int(VS_TextureId.x);
Run Code Online (Sandbox Code Playgroud)

FS:

in flat int FS_TextureId;
texture2D(ColourMaps[FS_TextureId], ... );
Run Code Online (Sandbox Code Playgroud)

我的假设

我猜测这是有问题的线路,尽管我无法弄清楚如何/为什么:

glVertexAttribPointer(shadInputs[3], 1, GL_UNSIGNED_INT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 2 + sizeof(glm::vec2)));
Run Code Online (Sandbox Code Playgroud)

注意:我检查了 'shader.attribute("VS_TextureId")' 的结果,它是正确的,这意味着顶点着色器中的属性已被很好地定义和找到。

你能看出问题出在哪里吗?

Rab*_*d76 9

如果您想使用整数数据类型指定属性数组,那么您必须使用glVertexAttribIPointer(重点关注I函数名称的中间),而不是glVertexAttribPointer.

请参阅OpenGL 4.6 API 核心配置文件规范;10.2. 当前顶点属性值;第348页

这些命令指定分别存储为VertexAttribI*有符号或无符号整数的有符号或无符号定点值。此类值称为纯整数。

...

所有其他VertexAttrib*命令指定直接转换为内部浮点表示形式的值。

这意味着顶点属性的规范必须是:

//glVertexAttribPointer(shadInputs[3], 1, GL_UNSIGNED_INT, GL_FALSE, 
//    sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 2 + sizeof(glm::vec2))); 
glVertexAttribIPointer(shadInputs[3], 1, GL_UNSIGNED_INT,
    sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 2 + sizeof(glm::vec2)));
Run Code Online (Sandbox Code Playgroud)

一般来说,你试图实现的目标不会像这样起作用。采样器数组的索引必须是“动态统一的”。这意味着所有片段的索引必须是“相同的”(例如常量或统一变量)。

请参阅GLSL 4.60 规范 - 4.1.7。不透明类型(第 33 页)

纹理组合采样器类型是不透明类型,其声明和行为如上面针对不透明类型所述。当在着色器中聚合为数组时,它们只能使用动态统一积分表达式进行索引,否则结果是未定义的。[...]

我建议使用单个TEXTURE_2D_ARRAY纹理,而不是纹理数组TEXTURE_2D。请参阅纹理
在这种情况下,您可以使用 3 维浮点纹理坐标。