坏顶点数据的常见陷阱/原因?

Posted

技术标签:

【中文标题】坏顶点数据的常见陷阱/原因?【英文标题】:Common pitfalls/causes of bad vertex data? 【发布时间】:2013-06-27 03:14:06 【问题描述】:

所以,在过去的一个半星期里,我一直在研究这个 .OBJ/.MTL 网格解析器。在这段时间内,我一直在追踪/修复很多错误,清理代码,记录它等等。

问题是,我修复的每一个错误,仍然会出现这个问题,因为一张图片值一千个字......

使用 GL_LINE_LOOP

(注意:右边的金字塔从球体向外倾斜是这里的问题)

使用 GL_TRIANGLES

更有趣的是,这个“坏”的顶点数据在场景中漂浮时似乎会随着相机移动......除了它会缩放并粘在网格之外。

这里奇怪的是,虽然我确定问题与内存有关,但我一直在检查与解析算法是否正常工作相矛盾的问题。经过一些单元测试,它似乎工作正常。

所以,我认为这可能是 Linux nVidia 驱动程序问题。我将驱动更新到下一个版本,重新启动,仍然没有骰子。

经过一番深思熟虑,我一直在尝试找出以下代码中的错误。

            //! every 3 vertices should represent a triangle, therefore we'll want to
            //! use the indices to grab their corresponding vertices. Since the cross product
            //! of two sides of every triangle (where one side = Vn - Vm, 'n' and 'm' being on the range of 1..3),
            //! we first grab the three vertices, and then compute the normal using the their differences.

            const uInt32 length = mesh->vertices.size();

            //! declare a pointer to the vector so we can perform simple
            //! memory copies to get the indices for each triangle within the
            //! iteration.

            GLuint* const pIndexBuf = &mesh->indices[ 0 ];

            for ( uInt32 i = 0; i < length; i += 3 )
            
                GLuint thisTriIndices[ 3 ];

                memcpy( thisTriIndices, pIndexBuf + i, sizeof( GLuint ) * 3 );

                vec3 vertexOne   = vec3( mesh->vertices[ thisTriIndices[ 0 ] ] );
                vec3 vertexTwo   = vec3( mesh->vertices[ thisTriIndices[ 1 ] ] );
                vec3 vertexThree = vec3( mesh->vertices[ thisTriIndices[ 2 ] ] );

                vec3 sideOne        = vertexTwo - vertexOne;
                vec3 sideTwo        = vertexThree - vertexOne;

                vec3 surfaceNormal  = glm::cross( sideOne, sideTwo );

                mesh->normals.push_back( surfaceNormal );
            

图片中显示的当前甚至没有法线数据,所以想法是为其计算表面法线,因此上面的代码。虽然我做了一些检查以查看索引数据是否在循环中正确加载,但我还没有找到任何东西。

我认为我整理记忆的方式也可能有问题,但我不能完全确定问题出在哪里。万一我错过了什么,我会加入我的 glVertexAttribPointer 调用:

//! Gen some buf handles

    glGenBuffers( NUM_BUFFERS_PER_MESH, mesh->buffers );

    //! Load the respective buffer data for the mesh

    __LoadVec4Buffer( mesh->buffers[ BUFFER_VERTEX ], mesh->vertices );      //! positons
    __LoadVec4Buffer( mesh->buffers[ BUFFER_COLOR ], mesh->colors );         //! material colors
    __LoadVec3Buffer( mesh->buffers[ BUFFER_NORMAL ], mesh->normals );       //! normals
    __LoadIndexBuffer( mesh->buffers[ BUFFER_INDEX ], mesh->indices );       //! indices

    //! assign the vertex array a value

    glGenVertexArrays( 1, &mesh->vertexArray );

    //! Specify the memory layout for each attribute

    glBindVertexArray( mesh->vertexArray );

    //! Position and color are both stored in BUFFER_VERTEX.

    glBindBuffer( GL_ARRAY_BUFFER, mesh->buffers[ BUFFER_VERTEX ] );

    glEnableVertexAttribArray( meshProgram->attributes[ "position" ] );
    glVertexAttribPointer( meshProgram->attributes[ "position" ],               //! index
                           4,                                                   //! num vals
                           GL_FLOAT, GL_FALSE,                                  //! value type, normalized?
                           sizeof( vec4 ),                                      //! number of bytes until next value in the buffer
                           ( void* ) 0 );                                       //! offset of the memory in the buffer

    glBindBuffer( GL_ARRAY_BUFFER, mesh->buffers[ BUFFER_COLOR ] );

    glEnableVertexAttribArray( meshProgram->attributes[ "color" ] );
    glVertexAttribPointer( meshProgram->attributes[ "color" ],
                           4,
                           GL_FLOAT, GL_FALSE,
                           sizeof( vec4 ),
                           ( void* ) 0 );

    //! Now we specify the layout for the normals

    glBindBuffer( GL_ARRAY_BUFFER, mesh->buffers[ BUFFER_NORMAL ] );

    glEnableVertexAttribArray( meshProgram->attributes[ "normal" ] );
    glVertexAttribPointer( meshProgram->attributes[ "normal" ],
                           3,
                           GL_FLOAT, GL_FALSE,
                           sizeof( vec3 ),
                           ( void* )0 );

    //! Include the index buffer within the vertex array

    glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->buffers[ BUFFER_INDEX ] );

    glBindVertexArray( 0 );

任何指向正确方向的观点都会受到赞赏:我不知道这些问题的常见原因是什么。

编辑:应要求发布绘制代码

glBindVertexArray( mMeshes[ i ]->vertexArray );

UBO::LoadMatrix4( UBO::MATRIX_MODELVIEW, modelView.top() );
UBO::LoadMatrix4( UBO::MATRIX_PROJECTION, camera.projection() );

glDrawElements( GL_TRIANGLES, mMeshes[ i ]->indices.size(), GL_UNSIGNED_INT, ( void* )0 );

glBindVertexArray( 0 );

【问题讨论】:

你实际上是在哪里画的? 已发布。它在绘制循环中,但我已将其他网格初始化代码注释掉,因此目前实际上只有一个绘制调用。 您确定要使用顶点的大小来计算法线,而不是网格中三角形的数量吗?如果 length 不是 3 的倍数,那么您很可能是在读取错误的内存,然后谁知道您使用的是什么值。 (对于奖励积分,将 memcpy 替换为 const 指针)。 @radical7 你的意思是这样的? pastebin.com/cX1DjCHW @blissfreak 指针实现做得很好;满分。但是,仍然没有正常的计算。使用顶点数作为 indexed 渲染的三角形数量的度量是不正确的 - 您需要使用索引数(结合原始类型;这次你没问题,因为GL_TRIANGLES 的顶点/基元数为 3)。我很确定您应该拥有numTris = mesh-&gt;indices.size(),并且您还应该将 numTris 向下舍入到最接近的三的倍数。四舍五入(如您所做的那样)也可以引用未分配的内存。 【参考方案1】:

我找到了最后的罪魁祸首,结合@radical7 的建议,这些解决了大部分问题。

            // round mesh->indices.size() down if it's not already divisible by 3.
            // the rounded value is stored in numTris

        std::vector< vec4 > newVertices;

        uInt32 indicesLen = Math_FloorForMultiple( mesh->indices.size(), 3 );

            // declare a pointer to the vector so we can perform simple
            // memory copies to get the indices for each triangle within the
            // iteration.

        newVertices.reserve( indicesLen );

        const GLuint* const pIndexBuf = &mesh->indices[ 0 ];

        for ( uInt32 i = 0; i < indicesLen; i += 3 )
        
            const GLuint* const thisTriIndices = pIndexBuf + i;

            vec4 vertexOne   = mesh->vertices[ thisTriIndices[ 0 ] - 1 ];
            vec4 vertexTwo   = mesh->vertices[ thisTriIndices[ 1 ] - 1 ];
            vec4 vertexThree = mesh->vertices[ thisTriIndices[ 2 ] - 1 ];

            vec4 sideOne     = vertexTwo - vertexOne;
            vec4 sideTwo     = vertexThree - vertexOne;

            vec3 surfaceNormal = glm::cross( vec3( sideOne ), vec3( sideTwo ) );

            mesh->normals.push_back( surfaceNormal );
            mesh->normals.push_back( surfaceNormal + vec3( sideOne ) );
            mesh->normals.push_back( surfaceNormal + vec3( sideTwo ) );

            newVertices.push_back( vertexOne );
            newVertices.push_back( vertexTwo );
            newVertices.push_back( vertexThree );
        


        mesh->vertices.clear();
        mesh->vertices = newVertices;

请注意,当在循环中通过调用mesh-&gt;vertices[ thisTriIndices[ x ] - 1 ] 获取顶点时,- 1 非常重要:OBJ 网格文件存储它们的面索引从 1...N 索引开始,而不是 0 ....N-1 指数。

索引本身也不应该用于绘制网格,而是从已经临时的顶点缓冲区中获取新的顶点缓冲区的一种方法:您使用索引来访问临时顶点中的元素,然后对于从临时缓冲区获得的每个顶点,您将该顶点添加到一个新缓冲区。这样,您将获得以正确绘制顺序指定的顶点数。因此,您只想使用顶点数组来绘制它们。

【讨论】:

很高兴您发现了问题。

以上是关于坏顶点数据的常见陷阱/原因?的主要内容,如果未能解决你的问题,请参考以下文章

HDU 4616 Game(经典树形dp+最大权值和链)

寻找单模序列的顶点

求单模序列的顶点序号——算法描述与复杂度分析

大集合图中常见子结构的引导挖掘

二分图 and code1170 双栈排序

从顶点数据中传入顶点位置和顶点颜色