将无符号整数输入属性传递给顶点着色器
Passing unsigned int input attribute to vertex shader
在典型的顶点数组缓冲区构建中,我试图将一个无符号整数属性与其他经典属性(顶点、法线、纹理坐标...)一起传递。然而,此属性的值最终以某种方式出错:我不确定是值错误还是属性未设置。
从一个简单的例子开始,假设我定义了以下 C++ 输入结构:
struct buffer_data_t
{
glm::vec3 vertex;
glm::vec3 normal;
glm::vec2 texCoords;
};
准备我的顶点数组如下所示:
// 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);
顶点着色器输入将定义如下:
in vec3 VS_Vertex;
in vec3 VS_Normal;
in vec2 VS_TexCoords;
此外,为了我的示例,假设我的片段着色器中有一个纹理采样器,在其上使用那些输入 TexCoords:
uniform sampler2D ColourMap;
介绍我的问题
到目前为止一切顺利,使用上面的代码我可以成功渲染带纹理的图元。现在我想 select 不同的颜色贴图取决于被渲染的脸。为此,我想引入一个 index 作为顶点属性的一部分。变化是:
C++ 数据结构:
struct buffer_data_t
{
glm::vec3 vertex;
glm::vec3 normal;
glm::vec2 texCoords;
unsigned int textureId; // <---
};
准备顶点数组:
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]);
然后顶点着色器定义新的输入,还有一个'flat'输出
in vec3 VS_Vertex;
in vec3 VS_Normal;
in vec2 VS_TexCoords;
in unsigned int VS_TextureId;
...
out flat unsigned int FS_TextureId;
调整片段着色器使输入变平(同样为了示例)颜色图现在是一个我们可以从中选择的数组:
...
uniform sampler2D ColourMaps[2];
in flat unsigned int FS_TextureId;
...
texture2D(ColourMaps[FS_TextureId], ... );
这些更改不起作用 特别是因为顶点着色器输入属性 'VS_TextureId'。我能够通过不使用 unsigned int 类型而是求助于 vec2(或 vec3,无论哪种方式都有效)来证明这一点(并找到解决方法)。即:
对比:
in vec2 VS_TextureId;
out flat int FS_TextureId;
FS_TextureId = int(VS_TextureId.x);
FS:
in flat int FS_TextureId;
texture2D(ColourMaps[FS_TextureId], ... );
我的假设
我猜这是错误的线路,虽然我想不通 how/why:
glVertexAttribPointer(shadInputs[3], 1, GL_UNSIGNED_INT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 2 + sizeof(glm::vec2)));
注意:我检查了'shader.attribute("VS_TextureId")'的结果是正确的,这意味着顶点着色器中的属性定义明确并找到了。
你能看出问题所在吗?
如果你想指定属性数组,具有整数数据类型,那么你必须使用glVertexAttribIPointer
(关注I
函数名的中间),而不是 glVertexAttribPointer
.
见OpenGL 4.6 API Core Profile Specification; 10.2. CURRENT VERTEX ATTRIBUTE VALUES; page 348
The VertexAttribI*
commands specify signed or unsigned fixed-point values
that are stored as signed or unsigned integers, respectively. Such values are referred to as pure integers.
...
All other VertexAttrib*
commands specify values that are converted directly to the internal floating-point representation.
这意味着顶点属性的规范必须是:
//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)));
一般来说,您尝试实现的目标并非如此。采样器数组的索引必须是“动态统一的”。这意味着所有片段的索引必须“相同”(例如常量或统一变量)。
参见 GLSL 4.60 规范 - 4.1.7。不透明类型
(第 33 页)
Texture-combined sampler types are opaque types, declared and behaving as described above for opaque types. When aggregated into arrays within a shader, they can only be indexed with a dynamically uniform integral expression, otherwise results are undefined. [...]
我建议使用单个 TEXTURE_2D_ARRAY
纹理,而不是一组 TEXTURE_2D
纹理。参见 Texture。
在这种情况下,您可以使用 3 维浮点纹理坐标。
在典型的顶点数组缓冲区构建中,我试图将一个无符号整数属性与其他经典属性(顶点、法线、纹理坐标...)一起传递。然而,此属性的值最终以某种方式出错:我不确定是值错误还是属性未设置。
从一个简单的例子开始,假设我定义了以下 C++ 输入结构:
struct buffer_data_t
{
glm::vec3 vertex;
glm::vec3 normal;
glm::vec2 texCoords;
};
准备我的顶点数组如下所示:
// 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);
顶点着色器输入将定义如下:
in vec3 VS_Vertex;
in vec3 VS_Normal;
in vec2 VS_TexCoords;
此外,为了我的示例,假设我的片段着色器中有一个纹理采样器,在其上使用那些输入 TexCoords:
uniform sampler2D ColourMap;
介绍我的问题
到目前为止一切顺利,使用上面的代码我可以成功渲染带纹理的图元。现在我想 select 不同的颜色贴图取决于被渲染的脸。为此,我想引入一个 index 作为顶点属性的一部分。变化是:
C++ 数据结构:
struct buffer_data_t
{
glm::vec3 vertex;
glm::vec3 normal;
glm::vec2 texCoords;
unsigned int textureId; // <---
};
准备顶点数组:
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]);
然后顶点着色器定义新的输入,还有一个'flat'输出
in vec3 VS_Vertex;
in vec3 VS_Normal;
in vec2 VS_TexCoords;
in unsigned int VS_TextureId;
...
out flat unsigned int FS_TextureId;
调整片段着色器使输入变平(同样为了示例)颜色图现在是一个我们可以从中选择的数组:
...
uniform sampler2D ColourMaps[2];
in flat unsigned int FS_TextureId;
...
texture2D(ColourMaps[FS_TextureId], ... );
这些更改不起作用 特别是因为顶点着色器输入属性 'VS_TextureId'。我能够通过不使用 unsigned int 类型而是求助于 vec2(或 vec3,无论哪种方式都有效)来证明这一点(并找到解决方法)。即:
对比:
in vec2 VS_TextureId;
out flat int FS_TextureId;
FS_TextureId = int(VS_TextureId.x);
FS:
in flat int FS_TextureId;
texture2D(ColourMaps[FS_TextureId], ... );
我的假设
我猜这是错误的线路,虽然我想不通 how/why:
glVertexAttribPointer(shadInputs[3], 1, GL_UNSIGNED_INT, GL_FALSE, sizeof(buffer_data_t), (void*)(sizeof(glm::vec3) * 2 + sizeof(glm::vec2)));
注意:我检查了'shader.attribute("VS_TextureId")'的结果是正确的,这意味着顶点着色器中的属性定义明确并找到了。
你能看出问题所在吗?
如果你想指定属性数组,具有整数数据类型,那么你必须使用glVertexAttribIPointer
(关注I
函数名的中间),而不是 glVertexAttribPointer
.
见OpenGL 4.6 API Core Profile Specification; 10.2. CURRENT VERTEX ATTRIBUTE VALUES; page 348
The
VertexAttribI*
commands specify signed or unsigned fixed-point values that are stored as signed or unsigned integers, respectively. Such values are referred to as pure integers.
...
All other
VertexAttrib*
commands specify values that are converted directly to the internal floating-point representation.
这意味着顶点属性的规范必须是:
//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)));
一般来说,您尝试实现的目标并非如此。采样器数组的索引必须是“动态统一的”。这意味着所有片段的索引必须“相同”(例如常量或统一变量)。
参见 GLSL 4.60 规范 - 4.1.7。不透明类型 (第 33 页)
Texture-combined sampler types are opaque types, declared and behaving as described above for opaque types. When aggregated into arrays within a shader, they can only be indexed with a dynamically uniform integral expression, otherwise results are undefined. [...]
我建议使用单个 TEXTURE_2D_ARRAY
纹理,而不是一组 TEXTURE_2D
纹理。参见 Texture。
在这种情况下,您可以使用 3 维浮点纹理坐标。