【问题标题】:Passing custom vertex structure to GLSL shaders将自定义顶点结构传递给 GLSL 着色器
【发布时间】:2014-01-12 00:44:11
【问题描述】:

我正在尝试将一个结构传递给一个简单的 GLSL vetrex 着色器。 以下是 C++ 端的结构:

struct Vertex
{
    float position[3];
    char boneIndex[4];
    float weights[4];
    float normals[3];
    float textureCords[2];
};

是否可以将此顶点的数组传递给顶点着色器而不为每个组件创建单独的数组?

我可以这样做吗:

#version 330 core

uniform mat4 MVP;

layout(location = 0) in struct Vertex
{
    vec3 position;
    uint boneIndex;
    vec4 weights;
    vec3 normals;
    vec2 textureCords;
} vertex;

out vec2 UV;

void main()
{
    gl_Position =  MVP * vec4(vertex.position, 1.0f);
    UV = vertex.textureCords;
}

(不要介意并非所有组件都在使用中,这只是示例。)

如果可以的话,如何使用 glVertexAttribPointer() 函数从 C++ 端将数据传递给它? (据我了解,您只能将 1,2,3,4 传递给 size 参数)。

这是正确的做事方式吗? 我是 OpenGL 编程的初学者,所以如果你有答案,请不要犹豫,包括明显的细节。

编辑:

我最终做了这样的事情:

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    glEnableVertexAttribArray(3);
    glEnableVertexAttribArray(4);

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);  //float position[3]
    glVertexAttribPointer(1, 1, GL_INT, GL_FALSE, 12, (void*)0);    //char boneIndex[4]
    glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 16, (void*)0); //float weights[4]
    glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 32, (void*)0); //float normals[3]
    glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, 44, (void*)0); //float textureCords[2]


    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceBuffer);

    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, (void*)0);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    glDisableVertexAttribArray(3);
    glDisableVertexAttribArray(4);

关于 C++ 大小和 GLSL 顶点着色器:

    #version 330 core

uniform mat4 MVP;

layout(location = 0) in vec3 position;
layout(location = 1) in int boneIndex;
layout(location = 2) in vec4 weight;
layout(location = 3) in vec3 normal;
layout(location = 4) in vec2 UVCords;

out vec2 UV;

void main()
{
    gl_Position =  MVP * vec4(position, 1.0f);
    UV = UVCords;
}

但还是不行,模型渲染不正确

【问题讨论】:

  • 使用接口块时,结构的每个字段都分配有顺序位置。任何vec4 或更小的东西都使用单个属性位置,像mat4 这样的东西使用4 个位置(将它们视为4 个vec4s 的数组可能会有所帮助)。所以使用glVertexAttribPointer (...) 分配应该非常简单。
  • 那么实际上我可以使用相同的 C++ 代码将数据传递给这个着色器吗? vec3 位置的布局(位置 = 0); uint boneIndex 中的布局(位置 = 1); C++ OpenGL:glVertexAttribPointer(0, ... glVertexAttribPointer(1, ... 会一样吗?
  • 我还没有看到您为此使用的实际 C++ 代码,但它应该可以工作。我还要指出,骨骼索引应该与 glVertexAttribIPointer (...) 一起传递,因为它是一个整数字段,而所有其他顶点数据都是浮点数。
  • @Andon M. Coleman 请查看更新后的问题,您是否发现该代码有问题?
  • 您必须为属性添加后续偏移量并更正步幅信息。当使用两个大小为n 的属性时,第一个将具有偏移量0 和步幅2n,第二个将具有偏移量n完全相同的步幅。一旦你明白了,就很容易为超过 2 个属性做到这一点。

标签: c++ opengl


【解决方案1】:

您用于传输交错数据的方法是正确的(即使用glVertexAttribPointer,但是您的最后两个参数不正确)。

倒数第二个是stride(对于结构中的每个项目都是相同的,应该是结构的大小),最后是offset,对于每个元素应该是不同的(如它们在结构本身中处于不同的偏移量)。

此外,最好不要在此处使用常量,而是使用sizeof() 运算符,以使您的代码尽可能独立于平台。

它应该是这样的:

glVertexAttribPointer(
    0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr
); //float position[3]

glVertexAttribIPointer(
    1, 1, GL_INT, GL_FALSE, sizeof(Vertex), 
    std::reinterpret_cast<void *>(offsetof(Vertex, boneIndex))
); //char boneIndex[4]

glVertexAttribPointer(
    2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), 
    std::reinterpret_cast<void *>(offsetof(Vertex, weights))
); //float weights[4]

glVertexAttribPointer(
    3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 
    std::reinterpret_cast<void *>(offsetof(Vertex, normals))
); //float normals[3]

glVertexAttribPointer(
    4, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), 
    std::reinterpret_cast<void *>(offsetof(Vertex, textureCoords))
); //float textureCoords[2]

此外,您可能应该确保您的 Vertex 结构没有被打包以适应一个漂亮的单词边界。这是通过使用#pragma pack(0) 来完成的,如果你的编译器支持的话。 (记住在你的结构之后重置它,因为否则编译器将在编译过程的其余部分使用这个指令,并且你所有的聚合数据结构都将以一种不会产生最佳拟合的方式构建到世界对齐。),看起来像这样:

#pragma pack(push, 0)

struct Vertex {
    float position[3];
    char  boneIndex[4];
    float weights[4],
          normals[4],
          textureCoords[2];
};

#pragma pack(pop)

【讨论】:

  • 非常感谢!它解决了我的问题!我在以下位置阅读了此功能的文档:opengl.org/sdk/docs/man/xhtml/glVertexAttribPointer.xml 这让我对这两个参数感到有些困惑。关于 pack 指令,它不是必需的,因为结构的组织方式不需要填充。
  • 很高兴听到它,我刚刚将其更改为使用 offsetof(...) 函数,因为它比硬编码 sizeof() 值更有意义。阅读它here
  • 您无法确定系统将如何打包您的结构,因此指定指令并且没有任何问题总是更安全,而不是当您尝试移植到某些时会出现奇怪的错误具有 3 个单词边界的模糊系统。
  • 为什么编译器会在这个结构中插入填充?他会把它们放在哪里?为什么?
  • 好吧,这样说吧,通过添加#pragma pack(...) 指令,您不会失去任何东西,更重要的是,那些阅读您的代码的人清楚地知道,这些数据不是用来填充的,而如果你添加它们,以后可能会遇到一个晦涩的错误,要么是因为你在结构中添加了一个额外的元素,要么是因为你的编译器填充了它并且你没想到。我不是告诉你编译器 填充你的结构,只是警告你要做好准备。最后,这是您的电话,但是我的建议是:P
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-14
  • 1970-01-01
相关资源
最近更新 更多