如何将四元组的大型数组转换为三角形基元?

时间:2018-03-06 18:17:02

标签: opengl glsl

我有一个提供3D网格的现有系统。提供的数据是具有3个分量(x,y,z)和索引列表的顶点坐标数组。 问题是索引列表是四元组的连续数组 我知道所有的四边形都有相同的绕线顺序(逆时针),但我没有关于四边形的更多信息。我对他们的关系或邻接并不了解。

由于我想使用核心配置文件进行渲染,因此我无法使用GL_QUAD原始类型。我必须将四边形转换为tringles。

当然,四元索引数组可以很容易地转换为三角形索引数组:

std::vector<unsigned int> triangles;
triangles.reserve( no_of_indices * 6 / 4 );
for ( int i = 0; i < no_of_indices; i += 4 )
{
    int tri[] = { quad[i], quad[i+1], quad[i+2], quad[i], quad[i+2], quad[i+3] };
    triangles.insert(triangles.end(), tri, tri+6 );
}

如果必须只做一次,那就是解决方案。但是,meash数据并不是静态的。数据可以动态变化。 数据不会每次都连续变化,但数据会不可预测地随机变化。

另一个简单的解决方案是创建一个顶点数组对象,该对象直接引用带有四边形的元素数组缓冲区,并在具有GL_TRIANGLE_FAN基元类型的循环中绘制它们:

for ( int i = 0; i < no_of_indices; i += 4 )
    glDrawElements( GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, (void*)(sizeof(unsigned int) * 4) );

但我希望有更好的解决方案。我正在寻找一种可能用一次绘制调用绘制四边形,或者将四边形转换为GPU上的三角形。

2 个答案:

答案 0 :(得分:5)

如果该操作仅需执行一次,则可以解决。但是网格数据不是静态的。

网格数据可能是动态的,但该列表的拓扑相同。每4个顶点是一个四边形,因此每4个顶点代表三角形(0,1,2)和(0,2,3)。

因此,您可以构建任意大的 static 索引缓冲区,其中包含这些数字不断增加的系列(0、1、2、0、2、3、4、5、6、4、6 ,7等)。您甚至可以use baseVertex rendering使它们偏移以使用相同的索引缓冲区渲染不同的四边形序列。

我的建议是使索引缓冲区使用GLushort作为索引类型。这样,您的索引数据每象限仅占用12个字节。使用短裤可以在一个绘图命令中提供16384个四边形的限制,但是您可以重用相同的索引缓冲区以baseVertex渲染来绘制多个四边形:

constexpr GLushort batchSize = 16384;
constexpr unsigned int vertsPerQuad = 6;
void drawQuads(GLuint quadCount)
{
  //Assume VAO is set up.
  int baseVertex = 0;
  while(quadCount > batchSize)
  {
    glDrawElementsBaseVertex(GL_TRIANGLES​, batchSize * vertsPerQuad, GL_UNSIGNED_SHORT, 0, baseVertex​ * 4);
    baseVertex += batchSize;
    quadCount -= batchSize;
  }
  glDrawElementsBaseVertex(GL_TRIANGLES​, quadCount * vertsPerQuad, GL_UNSIGNED_SHORT, 0, baseVertex​ * 4);
}

如果您希望索引数据少一点,可以使用primitive restart indices。这使您可以指定一个索引来表示“重新启动原语”。这样,您就可以使用GL_TRIANGLE_STRIP基元并将基元分解为多个片段,而同时仍然只有一个draw调用。因此,您有5个索引,而不是每个四边形6个索引,第5个是重新启动索引。因此,现在您的GLushort索引每象限仅占用10个字节。但是,batchSize现在必须为16383,因为保留索引0xFFFF以便重新启动。并且vertsPerQuad必须为5。

当然,baseVertex渲染在原始重启时也可以正常工作,因此上述代码也可以工作。

答案 1 :(得分:0)

首先,我想提一下,这是这个我想自己回答的问题,但我想提供我目前解决这个问题的方法。 这意味着,我仍在寻找&#34;&#34;&#34;解决方案,完全可以接受的解决方案。

在我的解决方案中,我决定使用Te I绘制大小为4的补丁:

glPatchParameteri( GL_PATCH_VERTICES, self.__patch_vertices )
glDrawElements( GL_PATCHES, no_of_indices, GL_UNSIGNED_INT, 0 )

Tessellation Control Shader有默认行为。补丁数据直接从顶点着色器调用传递到曲面细分基元生成。因此可以完全省略。

Tessellation Evaluation Shader使用四边形补丁(quads)创建2个三角形:

#version 450

layout(quads, ccw) in;

in TInOut
{
    vec3 pos;
} inData[];

out TInOut
{
    vec3 pos;
} outData;

uniform mat4 u_projectionMat44;

void main()
{
    const int inx_map[4] = int[4](0, 1, 3, 2);

    float i_quad = dot( vec2(1.0, 2.0), gl_TessCoord.xy );
    int   inx    = inx_map[int(round(i_quad))];

    outData.pos = inData[inx].pos;
    gl_Position = u_projectionMat44 * vec4( outData.pos, 1.0 );
}

另一种灵魂就是使用Geometry Shader。输入的原始类型lines_adjacency提供了4个顶点,可以映射到2个三角形(triangle_strip)。当然这似乎是一个黑客,因为lines adjacency与四元组完全不同,但无论如何都可以。

glDrawElements( GL_LINES_ADJACENCY, no_of_indices, GL_UNSIGNED_INT, 0 );

几何着色器:

#version 450

layout( lines_adjacency ) in;
layout( triangle_strip, max_vertices = 4 ) out;

in TInOut
{
    vec3 pos;
} inData[];

out TInOut
{
    vec3 col;
} outData;

uniform mat4 u_projectionMat44;

void main()
{
    const int inx_map[4] = int[4](0, 1, 3, 2);
    for ( int i=0; i < 4; ++i )
    {
        outData.pos = inData[inx_map[i]].pos;
        gl_Position = u_projectionMat44 * vec4( outData.pos, 1.0 );
        EmitVertex();
    }
    EndPrimitive();
}