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")' 的结果,它是正确的,这意味着顶点着色器中的属性已被很好地定义和找到。
你能看出问题出在哪里吗?
如果您想使用整数数据类型指定属性数组,那么您必须使用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 维浮点纹理坐标。