【问题标题】:OpenGL - Correct way to load multi facesOpenGL - 加载多面的正确方法
【发布时间】:2014-06-17 17:18:40
【问题描述】:

例如,如果我有一个立方体模型(立方体有 6 个面)。如何用 vbo 画出每张脸?我需要调用 glDrawElements 6 次吗?还是有另一个功能可以一次全部绘制?通常我是这样画的:

for (int i = 0; i < facesNum; i++)
    glDrawElements(GL_TRIANGLE_FAN, 4 + i*4, GL_UNSIGNED_INT, (GLvoid*)(i*4));

这是最好的方法吗?

【问题讨论】:

  • 您的目标是特定的 OpenGL 版本吗?根据需要使用的最低版本,有不同的选项。
  • 我的版本是 Opengl 4.4。但我更喜欢 opengl 4。
  • 您目前如何存储人脸数据?

标签: c++ opengl vbo


【解决方案1】:

您可以在渲染时使用Primitive Restart (OpenGL 3.1+) 重新启动诸如三角形扇形之类的图元,就像您启动了另一个glDraw* 命令一样。

使用glEnable(GL_PRIMITIVE_RESTART) 启用它,然后使用glPrimitiveRestartIndex(restartIndex) 设置一个索引(例如0xFFFF)以用于发出重启信号。那么每当 OpenGL 遇到重启索引时,它就会停止当前绘制的图元并开始另一个图元。

这使您可以使用一个索引缓冲区和绘制命令绘制多个三角形带、扇形、线环或带。只需在每个图元的索引数据之间添加重启索引即可。

【讨论】:

  • 你也可以提glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX),稍微方便一些,可惜4.3才加入。另一方面,它是 ES 3.0 中原始重启的唯一选择。我认为原始重启是最好的方法。当我询问 OpenGL 版本时,我的想法正是如此。
【解决方案2】:

一般情况下,您要做的是将对象绘制为GL_TRIANGLES 而不是GL_TRIANGLE_FAN,这样您只需调用glDrawElements 即可绘制所有12 个三角形(6 个面* 每个面2 个三角形)。

为此,您当然必须重新排列索引缓冲区,以包含每个三角形顶点的信息。这意味着您必须复制一些索引,但这不应该成为问题,因为索引缓冲区的点正是能够做到这一点而不是复制顶点。

假设您的顶面由逆时针顺序的顶点索引 0,1,2,3 组成, 例如,您可以将索引缓冲区的那部分从 0,1,2,3 更改为 0,1,2,0,2,3

通过索引缓冲区的这种更改设置,只需调用

glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, NULL);

(36,因为我们正在为立方体绘制 12 个三角形,每个三角形有 3 个顶点)

【讨论】:

  • 立方体就是一个例子。我有许多具有数千个多边形的模型。我无法一一更改索引。
  • 除了我上面建议的方法之外,我认为没有更好的方法,所以我建议您修改加载/生成模型的代码以生成三角形而不是三角形的索引粉丝。据我所知,这是通过一次调用 glDrawElements 来绘制所有内容的唯一方法,而且我已经习惯于绘制具有数千个顶点的(生成的)球体网格而没有问题。
【解决方案3】:

您应该只需要使用单个 VBO 调用一次 DrawElements。有诸如 IBO 之类的替代方案,但我将为 VBO 提供一个示例,因为这是您所要求的。理想情况下,您应该将所有顶点存储在包含 Vertex 对象的数组或 std::vector 中。将所有立方体面顶点放在此数组或 std::vector 中。您不需要每个面的数组/向量。您将希望保持数据紧密紧凑并避免使用 virtual 关键字,因为它会创建一个非公开指针 (vtptr),这会增加您的类内存占用。您希望保持数据紧凑的原因是当您将其发送到 OpenGL 时,它会期望步长。如果您对某个类的内存占用存有疑问,那么使用 sizeof(ClassName) 函数进行快速输出是没有坏处的。

您的顶点类如下所示;

class Vertex {
public:
    ~Vertex() {}
    Vertex() {} 

    Vector3<float>          vertexPosition;
    Vector4<float>          vertexColor;
    Vector2<float>          vertexTextureCoords;
    Vector3<float>          vertexNormal;
    Vector3<float>          vertexTangent;
    Vector3<float>          vertexBitangent;
    Vector4<int>            vertexBoneIndexes;
    Vector4<float>          vertexBoneWeights;
};

所有向量类都是非虚拟的,唯一的数据成员是向量分量。

以下是我遵循的步骤:

  • 创建顶点对象的 std::vector(或数组)并填充顶点数据
  • 使用适当的 OpenGL 调用生成 VBO(这是我的引擎中的一个示例)。我使用具有包装函数的单例,以便与图形 API 无关。这些参数的顺序与 OpenGL 期望它们的 VBO 生成函数的顺序完全相同。

    cbengine::CBRenderer * sharedRenderer = cbengine::CBRenderer::sharedCBRenderer();
    sharedRenderer->generateVBOBuffers( 1, &entityToCreate->m_VBOBufferID );
    sharedRenderer->bindVBOBuffer( entityToCreate->m_VBOBufferID );
    sharedRenderer->bufferDataForVBO( ( sizeof( cbengine::Vertex ) * entityToCreate-   >m_verts.size() ), &entityToCreate->m_verts.front() );
    
  • 在您的渲染函数中进行以下 OpenGL 调用。这就是为什么我建议尽早保持数据紧密紧凑的原因。 OpenGL 需要知道预期的数据类型、数据类型的大小以及与数据开头的偏移量。

    glEnableVertexAttribArray( m_vertexLocation );
    glVertexAttribPointer( m_vertexLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexPosition ) ); 
    
    glEnableVertexAttribArray( m_colorLocation );
    glVertexAttribPointer( m_colorLocation, 4, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexColor ) );
    
    glEnableVertexAttribArray( m_diffuseTextureCoordLocation );
    glVertexAttribPointer( m_diffuseTextureCoordLocation, 2, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexTextureCoords ) );
    
    glEnableVertexAttribArray( m_normalCoordLocation );
    glVertexAttribPointer( m_normalCoordLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexNormal ) );
    
    glEnableVertexAttribArray( m_tangentLocation );
    glVertexAttribPointer( m_tangentLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexTangent ) );
    
    glEnableVertexAttribArray( m_bitangentLocation );
    glVertexAttribPointer( m_bitangentLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexBitangent ) );
    
    glEnableVertexAttribArray( m_boneIndexesLocation ); // Apparently GL_INT causes issues
    glVertexAttribPointer( m_boneIndexesLocation, 4, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexBoneIndexes ) );
    
    glEnableVertexAttribArray( m_boneWeightsLocation );
    glVertexAttribPointer( m_boneWeightsLocation, 4, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexBoneWeights ) );
    
  • 最后,调用 DrawArrays

    sharedRenderer->drawArrays( drawPrim, 0, verts.size() );
    

这是一个非常简单的示例,说明如何完成您想要的。其他要记住的事情是剔除的缠绕顺序,启用/禁用纹理以及您可能希望发送到着色器的自定义顶点属性数据。如果您的引擎不支持骨骼动画,您可能不想包含骨骼权重和骨骼索引向量。

【讨论】:

    猜你喜欢
    • 2015-12-22
    • 2020-01-15
    • 1970-01-01
    • 2021-02-12
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 2014-06-26
    • 1970-01-01
    相关资源
    最近更新 更多