web-dev-qa-db-ja.com

OpenGL頂点のインデックス付き配列をいつ使用する必要がありますか?

Gl [Multi] DrawElementsなどで描画されたOpenGL頂点のインデックス付き配列を使用する必要がある場合と、gl [Multi] DrawArraysで描画された頂点の連続配列を使用する必要がある場合を明確に理解しようとしています。 。

更新:私が得た回答のコンセンサスは、常にインデックス付きの頂点を使用する必要があるということです。)

私はこの問題について何度か行ったり来たりしたので、誰かが私が最終的に多かれ少なかれ正しいと私に言うか、そうでなければ私の残りの誤解がどこにあるかを指摘できることを期待して、私の現在の理解の概要を説明します。具体的には、太字で3つの結論があります。間違っている場合は修正してください。

単純なケースの1つは、ジオメトリが曲面を形成するメッシュで構成されている場合です。この場合、メッシュの中央にある頂点は、頂点を使用するすべての三角形に対して同じ属性(位置、法線、色、テクスチャ座標など)を持ちます。

これにより、私は次のように結論付けることができます。

1。継ぎ目が少ないジオメトリの場合、インデックス付き配列は大きなメリットです。

以下を除いて、常にルール1に従ってください。

すべてのエッジが継ぎ目を表す非常に「ブロック状」のジオメトリの場合、インデックス付き配列の利点はそれほど明白ではありません。単純な立方体を例にとると、各頂点は3つの異なる面で使用されますが、単一の頂点の場合、サーフェス法線(および色やテクスチャの座標などの他の可能性があるもの)のため、頂点を共有することはできません。 )各面で異なります。したがって、配列に冗長な頂点位置を明示的に導入して、同じ位置を異なる法線などで複数回使用できるようにする必要があります。これは、インデックス付き配列の使用が少ないことを意味します。

例えば立方体の単一の面をレンダリングする場合:

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2

(これは、この面とすべての隣接する面の間の継ぎ目が、これらの頂点のいずれも面間で共有できないことを意味するため、単独で考えることができます)

gL_TRIANGLE_FAN(または_STRIP)を使用してレンダリングする場合、立方体の各面は次のようにレンダリングできます。

verts  = [v0, v1, v2, v3]
colors = [c0, c0, c0, c0]
normal = [n0, n0, n0, n0]

インデックスを追加しても、これを単純化することはできません。

このことから、私は次のように結論付けます。

2。すべての継ぎ目またはほとんどが継ぎ目であるジオメトリをレンダリングする場合、GL_TRIANGLE_STRIPまたは_FANを使用する場合は、インデックス付き配列を使用しないでください。代わりに、常にgl [Multi] DrawArraysを使用する必要があります。

更新:返信は、この結論が間違っていることを示しています。インデックスではここで配列のサイズを縮小することはできませんが、で説明されているように、他のパフォーマンス上の利点があるため、引き続き使用する必要があります。コメント)

ルール2の唯一の例外は次のとおりです。

(ストリップやファンの代わりに)GL_TRIANGLESを使用する場合、各立方体の面が2つの別々の三角形としてレンダリングされるため、頂点の半分を同じ法線や色などで2回再利用できます。繰り返しますが、同じ単一の立方体の面の場合:

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2

インデックスがない場合、GL_TRIANGLESを使用すると、配列は次のようになります。

verts =   [v0, v1, v2,  v2, v3, v0]
normals = [n0, n0, n0,  n0, n0, n0]
colors =  [c0, c0, c0,  c0, c0, c0]

頂点と法線はそれぞれ3つの浮動小数点数であることが多く、色は多くの場合3バイトであるため、各立方体の面について、次のようになります。

verts   = 6 * 3 floats = 18 floats
normals = 6 * 3 floats = 18 floats
colors  = 6 * 3 bytes  = 18 bytes

= 36 floats and 18 bytes per cube face.

(異なるタイプを使用するとバイト数が変わる可能性があることを理解しています。正確な数値は説明のためのものです。)

インデックスを使用すると、これを少し単純化できます。

verts   = [v0, v1, v2, v3]     (4 * 3 = 12 floats)
normals = [n0, n0, n0, n0]     (4 * 3 = 12 floats)
colors  = [c0, c0, c0, c0]     (4 * 3 = 12 bytes)
indices = [0, 1, 2,  2, 3, 0]  (6 shorts)

= 24 floats + 12 bytes, and maybe 6 shorts, per cube face.

後者の場合、頂点0と2が2回使用されますが、頂点、法線、色の配列のそれぞれで1回だけ表されます。これは、すべてのジオメトリのエッジが継ぎ目であるという極端な場合でも、インデックスを使用することの小さな勝利のように聞こえます。

これにより、私は次のように結論付けることができます。

。GL_TRIANGLESを使用する場合、すべての継ぎ目であるジオメトリであっても、常にインデックス付き配列を使用する必要があります。

間違っている場合は、太字で私の結論を修正してください。

41

このことから、すべての継ぎ目またはほとんどが継ぎ目であるジオメトリをレンダリングする場合、GL_TRIANGLE_STRIPまたは_FANを使用する場合は、インデックス付き配列を使用せず、代わりに常にgl [Multi] DrawArraysを使用する必要があると結論付けます。

いいえ、理由は非常に単純です。

あなたの結論は、2つの三角形で構成される単一のクワッドを分析したという事実に基づいています。三角形のファン/ストリップを使用して描画されたこれらの2つの三角形は、インデックス付き配列を使用して簡略化することはできません。

しかし、大きな地形の形状について考えてみてください。各テレインブロックは、三角形のファン/ストリッププリミティブを使用して、クワッドとして描画されます。例えば:

図の各三角ストリップには、隣接する三角ストリップを持つすべての頂点が共通してあり、インデックスを使用すると、各三角ストリップの頂点を繰り返す代わりに、ジオメトリ定義を圧縮できます。


基本的に、インデックスを使用してプリミティブ(三角形、ファン、ストリップ)を描画すると、1つのプリミティブのほとんどの頂点を別のプリミティブと共有できる場合に役立ちます。

情報を共有することで情報伝送帯域幅を節約できますが、それだけが利点ではありません。実際にインデックス付けされた配列では、次のことが可能です。

  • 何度も指定された同じ「概念的な」頂点に属する情報の同期を避けます
  • 頂点の複製ごとに1回ずつ、何度も実行する代わりに、単一の頂点で同じシェーダー操作を実行できるようにします。
  • さらに、三角ストリップ/ファンとインデックスの使用を組み合わせると、ストリップ/ファンの仕様に必要なインデックスが少なくなるため、アプリケーションでインデックスバッファを圧縮できます(三角形では、各面に常に3つのインデックスが必要です)。

指定したように、頂点がそれに関連するすべての情報(色、テクスチャ座標など)を別の一致する頂点と共有できない場合は、インデックス付き配列を使用できません。


完全を期すために、ジオメトリの仕様に必要な情報のサイズだけが、最適なレンダリング操作を決定する要因ではありません。

実際、プリミティブレンダリングのもう1つの基本的な要素は、データのキャッシュローカリゼーションです。不適切に指定されたジオメトリデータ(インターリーブされていないバッファオブジェクト、長い三角ストリップなど)は、多くのキャッシュミスを引き起こし、グラフィックカードのパフォーマンスを低下させます。

レンダリング操作を最適化するために、頂点の指定は、以前に指定された頂点を最も可能性の高い方法で再利用する方法で並べ替える必要があります。このようにして、グラフィックカードのキャッシュラインは、メモリからフェッチすることなく、以前に指定された頂点を再利用できます。

33
Luca