【问题标题】:Vbo - drawing normals with obj fileVbo - 使用 obj 文件绘制法线
【发布时间】:2014-08-09 16:21:02
【问题描述】:

一般来说,我的问题是如何为 vbo 提供 2 个索引。一个用于顶点,一个用于法线? 我得到了下一个 Obj 文件:

mtllib cube.mtl

v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 1.000000 0.000000 0.000000
vn -0.000000 -0.000000 1.000000
vn -1.000000 -0.000000 -0.000000
vn 0.000000 0.000000 -1.000000

usemtl Material
f 1//1 2//1 3//1 4//1
f 5//2 8//2 7//2 6//2
f 1//3 5//3 6//3 2//3
f 2//4 6//4 7//4 3//4
f 3//5 7//5 8//5 4//5
f 5//6 1//6 4//6 8//6

如您所见,有 8 个顶点和 6 个法线。在面线上,文件通过索引将每个顶点连接到下一个顶点,并通过不同的索引连接法线。

我正在尝试使用带有 vbo 的立方体模型进行绘制。我写了以下代码:

float vertex[] = {1, -1, -1, 
              1, -1, 1,
             -1, -1, 1,
             -1, -1, -1,
              1, 1, -1,
              1, 1, 1,
             -1, 1, 1,
              -1, 1, -1};
float normals[] = {0, -1, 0,
                   0, 1, 0,
                   1, 0, 0,
                   0, 0, 1,
                   -1, 0, 0,
                   0, 0, -1};
int index[] = {0, 1, 2, 3,
               4, 7, 6, 5,
               0, 4, 5, 1,
               1, 5, 6, 2,
               2, 6, 7, 3,
               4, 0, 3, 8};




GLuint buffer, ind;
int offset = 0;

void vboInit()
{
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertex) + sizeof(normals), 0, GL_STATIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(vertex), vertex);       offset+= sizeof(vertex);
    glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(normals), normals);     offset+= sizeof(normals);


    glGenBuffers(1, &ind);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ind);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), index, GL_STATIC_DRAW);
}

void vboDraw()
{
    glBindBuffer(GL_ARRAY_BUFFER, buffer);  
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ind); 
    glNormalPointer(GL_FLOAT, 0, (GLvoid*)(sizeof(vertex)));
    glVertexPointer(3, GL_FLOAT, 0, 0);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    for (int i = 0; i < 6; i++)
        glDrawElements(GL_TRIANGLE_FAN, 4 + i*4, GL_UNSIGNED_INT, (GLvoid*)(i*4));
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);


    glBindBuffer(GL_ARRAY_BUFFER, NULL);        
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL);    
}

此代码使用法线的顶点索引。因此法线不能很好地加载,我需要不同的法线索引。问题是如何为 vbo 提供 2 个索引。一个用于顶点,一个用于法线?

【问题讨论】:

标签: c++ opengl normalization vbo


【解决方案1】:

简短地回答你不能。但如果你想要长的:

DrawElements 采用顶点索引数组。顶点不是一个位置;它是一次所有属性的组合。当您按索引获取顶点时,您必须从每个属性数组的相同索引中获取它。索引的好处是它们可以使用 TNL 后缓存(但是,缓存的使用取决于实际的索引值)并减少内存使用。

如果您的数据保存在类似 OBJ 的布局中,其中属性数组以独立方式索引,您必须将数组转换为更友好的表示形式。简单的方法是:

  • 分配大数组[s],以便它可以保存所有顶点数据,即使没有索引。
  • 对于每个三角形,使用您的独立索引将数据提取到新数组中。结果,您的顶点现在没有独立的属性索引 - 但它们根本没有索引!不过,您已经可以绘制了 - 使用 DrawArrays()。
  • 如果您需要索引,现在可以重新创建它们。从新顶点数组中删除重复项(重复项的整个 vertices 相等,而不仅仅是位置),并为每个三角形找到新数组中顶点的索引。

【讨论】:

  • 重复是什么意思?
  • 如果特定顶点的所有属性都与其他顶点匹配,则其中一个顶点是重复的,可能会被删除。
  • 虽然建议的方法有效,但通过比较实际顶点数据来删除重复项似乎非常低效。请参阅此处了解更有效的方法:stackoverflow.com/questions/23349080/…
  • @RetoKoradi 我怀疑 STL map 会击败在大型数据集上按散列顶点数据排序的普通数组,因为 sort 只执行一次,而 map 在每次插入时执行 rebalansing;但这肯定不应该在每次加载大量数据时都执行,而只是离线转换。但无论如何,对于 each 顶点属性,您仍然需要一些独特的数据,而不仅仅是位置和法线(例如纹理坐标等);它是实际的属性数据还是索引并不重要。
  • 如果您真的关心最终性能,您很可能会比数据结构的 STL 映射做得更好。这只是如何轻松实现伪代码的一个示例。无论如何,我很想知道您的解决方案如何比较。你可以试试people.sc.fsu.edu/~jburkardt/data/obj/obj.html 的 minicooper.obj 文件吗?它有 44k 顶点、44k 法线、3k 组纹理坐标和 80k 面。在我的 MacBook Pro 上加载和构建具有索引 OpenGL 几何的 VBO 需要 0.2 秒。
猜你喜欢
  • 2016-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-19
  • 1970-01-01
  • 1970-01-01
  • 2018-04-15
  • 2021-08-10
相关资源
最近更新 更多