为什么我的索引生成函数不能正确构建三角形基元?

Why is my Index Generation Function not correctly building the triangle primitives?

提问人:ModernEraCaveman 提问时间:1/3/2023 更新时间:1/3/2023 访问量:59

问:

我正在尝试编写一个函数,该函数会自动填充网格的索引向量容器。从理论上讲,该函数应该可以毫无问题地工作,因为它会以正确的顺序生成适当的索引;但是,三角形不会形成!相反,我只剩下一行。

我的网格生成代码应该构建一个八面体,然后在主游戏循环中渲染它。网格类完整如下所示:

struct vertex
{
    glm::vec3 position;
    glm::vec3 color;
};

class Mesh
{
public:
    GLuint VAO, VBO, EBO;
    std::vector <vertex> vtx;
    std::vector <glm::vec3> idx;
    glm::mat4 modelMatrix = glm::mat4(1.f); 

    Mesh(glm::vec3 position, glm::vec3 scale)
    {
        vertexGen(6);
        idx = indexGen(6);
        modelMatrix = glm::scale(glm::translate(modelMatrix, position), scale);
        initMesh();
    };
    void Render(Shader shaderProgram, Camera camera, bool wireframe)
    {
        glUseProgram(shaderProgram.ID);
        glPatchParameteri(GL_PATCH_VERTICES, 3); // Indicates to the VAO that each group of three vertices is one patch (triangles)
        glProgramUniformMatrix4fv(shaderProgram.ID, 0, 1, GL_FALSE, glm::value_ptr(modelMatrix));
        glProgramUniformMatrix4fv(shaderProgram.ID, 1, 1, GL_FALSE, glm::value_ptr(camera.camMatrix));
        glProgramUniform3fv(shaderProgram.ID, 2, 1, glm::value_ptr(camera.Position));

        glBindVertexArray(VAO); // Binds the VAO to the shader program
        if (wireframe)
        {
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            glDisable(GL_CULL_FACE);
        }
        else
        {
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            //glEnable(GL_CULL_FACE);
        }
        glDrawElements(GL_PATCHES, idx.size(), GL_UNSIGNED_INT, 0); // Tells the shader program how to draw the primitives
    }
private:
    void vertexGen(int n) {
        // Populate the base six vertices
        vtx.push_back(vertex{ glm::vec3( 0.0f, 0.5f,  0.0f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3(-0.5f, 0.0f,  0.0f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3( 0.0f, 0.0f, -0.5f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3( 0.5f, 0.0f,  0.0f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3( 0.0f, 0.0f,  0.5f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3( 0.0f,-0.5f,  0.0f), glm::vec3(0.f, 1.f, 0.f) });
    }
    std::vector<glm::vec3> indexGen(int n) {
        std::vector<glm::vec3> indices;

        // Calculate the indices for the top 4 triangles
        indices.push_back(glm::vec3( 0, n - 5, n - 4 ));
        indices.push_back(glm::vec3( 0, n - 4, n - 3 ));
        indices.push_back(glm::vec3( 0, n - 3, n - 2 ));
        indices.push_back(glm::vec3( 0, n - 2, n - 5 ));

        // Calculate the indices for the bottom 4 triangles
        indices.push_back(glm::vec3( 5, n - 5, n - 4)); 
        indices.push_back(glm::vec3( 5, n - 4, n - 3));
        indices.push_back(glm::vec3( 5, n - 3, n - 2));
        indices.push_back(glm::vec3( 5, n - 2, n - 5));

        return indices;
    }
    void initMesh()
    {
        glCreateVertexArrays(1, &VAO); // Sets the address of the uint VAO as the location of a gl vertex array object
        glCreateBuffers(1, &VBO);      // Sets the address of the uint VBO as the location of a gl buffer object
        glCreateBuffers(1, &EBO);      // Sets the address of the uint EBO as the location of a gl buffer object

        glNamedBufferData(VBO, vtx.size() * sizeof(vtx[0]), vtx.data(), GL_STATIC_DRAW); // Sets the data of the buffer named VBO
        glNamedBufferData(EBO, idx.size() * sizeof(idx[0]), idx.data(), GL_STATIC_DRAW); // Sets the data of the buffer named EBO

        glEnableVertexArrayAttrib(VAO, 0); // Enables an attribute of the VAO in location 0
        glEnableVertexArrayAttrib(VAO, 1); // Enables an attribute of the VAO in location 1

        glVertexArrayAttribBinding(VAO, 0, 0); // Layout Location of Position Vectors
        glVertexArrayAttribBinding(VAO, 1, 0); // Layout Location of Color Values

        glVertexArrayAttribFormat(VAO, 0, 3, GL_FLOAT, GL_FALSE, 0); // Size, and Type of Position Vectors
        glVertexArrayAttribFormat(VAO, 1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat)); // For the Color Values

        glVertexArrayVertexBuffer(VAO, 0, VBO, 0, 6 * sizeof(GLfloat)); // Sets the VBO to indicate the start, offset, and stride of vertex data in the VAO

        glVertexArrayElementBuffer(VAO, EBO); // Sets the EBO to index the VAO vertex connections
    }
};

我一步一步地回答了这个问题,并在纸上完成了所有的基本数学运算。索引生成函数以正确的顺序返回预期的索引,就像只写出索引一样,但它的不同之处在于,写出的索引生成所需的结果,而生成函数在呈现时只生成一行:

Two opposing vertical lines which should have rendered as two octahedra.

我怀疑问题出在我的网格初始化函数 (initMesh) 上,特别是在 glNamedBufferData 或 glVertexArrayVertexBuffer 中,但我对这些函数的了解非常有限。我尝试将 glNamedBufferData 函数的参数更改为 idx.size()*sizeof(idx[0].x) 的不同变体,但这产生了相同的结果,所以我不知所措。有人可以帮我解决这个问题吗?

C++ OpenGL 渲染 网格

评论

0赞 ModernEraCaveman 1/3/2023
澄清一下,我应该生成两个八面体(一个黑色和一个白色),但我只为每个八面体生成一条线。

答:

1赞 user253751 1/3/2023 #1

glm::vec3 是 s 的向量(我认为),但您告诉 OpenGL 将它们读取为无符号整数。float

浮点 0.0 是 0x00000000(即与 int 0 相同),但浮点 1.0 是 0x3f800000(与 int 1065353216 相同)。它们不是存储数字的兼容方式。您可以尝试哪个是 s 的向量,但我认为大多数人会使用(或)并为每个三角形使用 3 个条目。glm::ivec3intstd::vector<int>unsigned int

我认为在这种情况下没关系,但我不喜欢使用类型,比如我的意思是有 3 个单独的 s 并不总是一个好的做法,因为编译器可以在意想不到的地方插入填充。在某些平台上,可能是 3 秒和额外的 4 个字节的填充,总共 16 个字节,而额外的填充字节会抛弃您所依赖的布局。 不会在每 3 个索引之后跳过填充,也没有办法告诉它这样做。顶点是可以的,因为你可以告诉 OpenGL 数据的确切位置。ivec3intivec3intglDrawArrays

评论

0赞 ModernEraCaveman 1/3/2023
谢谢你,朋友!从字面上看,使用 ivec3 可以让我完成四分之一的工作。现在,我渲染的不是一条线,而是八面体的上后四分之一。我尝试将类型切换到 std::vector<std::array<int, 3>> 作为 ivec3s 向量的替代,但我得到了相同的四分之一形状。我真的需要使用 std::vector<int>吗?我不太喜欢将我需要调用的回击次数增加两倍的想法。
1赞 user253751 1/3/2023
@ModernEraCaveman如果你担心性能,只要告诉矢量保留你需要的项目数量,那么push_back基本上是免费的。也许您的系统确实有 4 个字节的填充 (idk) - 和 是多少?sizeof(glm::ivec3)alignof(glm::ivec3)
0赞 ModernEraCaveman 1/3/2023
sizeof(glm::ivec3)是 12 岁,是 4 岁。我对 c++ 编码很陌生,所以我不确定我该如何告诉向量保留我会保留的项目数量。它是地址/指针调用的某种变体吗?alignof(glm::ivec3)
1赞 user253751 1/3/2023
@ModernEraCaveman 事实上,我确实知道——我只是注意到了。你告诉 OpenGL 索引的数量是,但这实际上是三角形的数量。指数的数量是这个数字的 3 倍。idx.size()
1赞 user253751 1/3/2023
@ModernEraCaveman 至于保留,你可以直接做,然后索引一次为 24 个项目分配空间,所以当你调用push_back时,只要有 24 个项目或更少,它就不必再分配任何空间。indices.reserve(24);