4

そのため、現在、複雑なモデルを適切な速度でレンダリングしようとしていますが、問題が発生しています。単一のモデルをレンダリングすると、プログラムに追加の作業を行わなくても、フレームレートが低下します。私のモデル(シーンに1つしかない)は大きすぎるようです。バッファにアップロードする頂点配列には444384のfloatがあります(したがって、モデルには24688の三角形があります)。

//Create vertex buffers
glGenBuffers(1, &m_Buffer);
glBindBuffer(GL_ARRAY_BUFFER, m_Buffer);    
int SizeInBytes = m_ArraySize * 6 * sizeof(float);
glBufferData(GL_ARRAY_BUFFER, SizeInBytes, NULL, GL_DYNAMIC_DRAW);

//Upload buffer data
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * VertArray.size(), &VertArray[0]);

A)サイズを小さくするとパフォーマンスが向上し、B)レンダリングコードをコメントアウトするため、VBOのサイズが違いを生むことを私は知っています。

glPushMatrix();

//Translate
glTranslatef(m_Position.x, m_Position.y, m_Position.z);

glMultMatrixf(m_RotationMatrix);

//Bind buffers for vertex and index arrays
glBindBuffer(GL_ARRAY_BUFFER, m_Buffer);

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), 0);
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 6 * sizeof(float), (void*)12);

//Draw
glDrawArrays(GL_TRIANGLES, 0, m_ArraySize);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);

//Unbind the buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);

glPopMatrix();

約2000〜2500 FPSが残りますが、このコードのコメントを外すと、約130FPS、つまり8ms /フレームになります(これだけで十分ですが、プログラムで他のこともできる必要があります。これはCPUを集中的に使用する可能性があります)。85kの三角形を持つより複雑なモデルでは、50 FPS未満、つまり約20ms /フレームになり、その時点でプログラムは目に見えて途切れます。

私が使用している1組のシェーダーは、現時点ではごくわずかです。それが問題だとは思えません。念のため、ここにあります。最初に頂点シェーダー:

void main()
{
    vec3 normal, lightDir;
    vec4 diffuse;
    float NdotL;
    /* first transform the normal into eye space and normalize the result */

    normal = normalize(gl_NormalMatrix * gl_Normal);
    /* now normalize the light's direction. Note that according to the

    OpenGL specification, the light is stored in eye space. Also since
    we're talking about a directional light, the position field is actually
    direction */
    lightDir = normalize(vec3(gl_LightSource[0].position));
    /* compute the cos of the angle between the normal and lights direction.

    The light is directional so the direction is constant for every vertex.
    Since these two are normalized the cosine is the dot product. We also
    need to clamp the result to the [0,1] range. */
    NdotL = max(dot(normal, lightDir), 0.0);
    /* Compute the diffuse term */

    diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
    gl_FrontColor =  NdotL * diffuse;

    gl_Position = ftransform();
} 

そしてフラグメントシェーダー:

void main()
{
    gl_FragColor = gl_Color;
}

グラフィックカードとしてGTX660Mを使用してプログラムを実行しています。

私の知る限り、VBOはOpenGLで大量のポリゴンをレンダリングする最速の方法であり、インターネットは多くのマシンが一度に数百万のポリゴンを計算して表示できることを示唆しているようです。比較的わずかな27kの三角形のレンダリングを最適化するため。将来、大量のコードを書き直して再構築するよりも、今それを実行したいと思います。

裏面カリングを有効にしました。モデルのすべてまたはほとんどが画面に表示されることがあるため、fustrumカリングが役立つかどうかはわかりません(現在、オブジェクトをカリングしていますが、個々のオブジェクト内の三角形はカリングしていません)。カメラに面していないビューポートの面をカリングすると少し役立つかもしれませんが、その方法がわかりません。それを超えて、レンダリングを最適化するために何をすべきかわかりません。頂点バッファはまだ実装していませんが、速度が10%程度しか向上しない可能性があることを読みました。

他の処理が行われている状態で、許容可能なフレームレートで画面上に一度に数万または数十万の三角形を達成するにはどうすればよいでしょうか。VBOレンダリングのパフォーマンスを向上させるために何ができますか?

更新:以下のコメントに従って、次のように配列の半分だけを描画しました。

glDrawArrays(GL_TRIANGLES、0、m_ArraySize / 2);

そして、アレイの4分の1:

glDrawArrays(GL_TRIANGLES、0、m_ArraySize / 4);

毎回描画されるアレイの量を減らすと、文字通り速度が2倍になります(それぞれ12ミリ秒から6ミリ秒と3ミリ秒に)が、モデルは完全に無傷で、何も欠けていませんでした。これは、私がどこかで何か間違ったことをしていることを示唆しているようですが、何が起こっているのかわかりません。モデルを作成するときに同じ三角形を4回以上追加しないとかなり確信しているので、他に何ができるでしょうか。どういうわけか、バッファを複数回アップロードしている可能性がありますか?

4

3 に答える 3

3

glDrawArrays()3番目の引数として、描画するインデックスの数を取ります。インターリーブされた頂点と通常の配列のfloatの数を渡します。これは、インデックスの数の6倍です。バッファの境界外のデータにアクセスするように指示しているため、GPUは遅れています-これが発生すると、最新のGPUは障害を引き起こす可能性があり、古いGPUはシステムをクラッシュさせるだけです:)

次のインターリーブ配列について考えてみます。

vx0 vy0 vz0 nx0 ny0 nz0 vx1 vy1 vz1 nx1 ny1 nz1 vx2 vy2 vz2 nx2 ny2 nz2

この配列には、3つの頂点と3つの法線(1つの三角形)が含まれています。三角形を描画するには3つの頂点が必要であるため、それらを選択するには3つのインデックスが必要です。上記の三角形を描くには、次を使用します。

glDrawArrays(GL_TRIANGLES, 0, 3);

属性が機能する方法(頂点、法線、色、テクスチャなど)では、単一のインデックスが各属性から値を選択します。上記の三角形に色属性を追加した場合でも、3つのインデックスのみを使用します。

于 2013-02-08T03:54:44.787 に答える
3

問題は、モデル内の各三角形に独自の3つの頂点があることだと思います。インデックス付きの三角形(GL_ELEMENT_ARRAY_BUFFER、glDrawElements)を使用していないため、頂点データを三角形間で共有できます。

私の知る限り、あなたの現在のアプローチには2つの問題があります。

  1. 処理する必要のある膨大な量のデータ(ただし、これはインデックス付きの三角形でも問題になる可能性があります)。

  2. glDrawElementsではなくglDrawArrays()を使用する場合、GPUは、頂点処理の量を減らすために使用される変換後のキャッシュを利用できません。

可能であれば、インデックス付きの三角形を使用するようにデータを再配置します。

インデックス付きの三角形を使用する場合、最高のパフォーマンスを得るには、三角形間で頂点データを可能な限り共有していることを確認する必要があるという警告を追加します。それは本当にあなたがあなたのデータをどれだけうまく整理するかについてです。

于 2013-02-04T09:01:41.723 に答える
3

編集:コメントのいくつかを読んでください。以下の回答。


試してみるランダムなことのカップル。

glBufferData(GL_ARRAY_BUFFER, SizeInBytes, NULL, GL_DYNAMIC_DRAW);

試してみてくださいGL_STATIC_DRAW。定常状態ではおそらく役に立ちませんが(頂点バッファーデータの変更がないため、ドライバーは再アップロードの必要がないことに気付くはずなので)、試してみる価値があります。

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);

//Unbind the buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);

必要がない場合は、描画のたびに頂点バッファの状態を変更しないでください。それはただ1つのバッファであり、バインドされたままにしておきます。

   normal = normalize(gl_NormalMatrix * gl_Normal);
    /* now normalize the light's direction. Note that according to the

    OpenGL specification, the light is stored in eye space. Also since
    we're talking about a directional light, the position field is actually
    direction */
    lightDir = normalize(vec3(gl_LightSource[0].position));
    /* compute the cos of the angle between the normal and lights direction.

    The light is directional so the direction is constant for every vertex.
    Since these two are normalized the cosine is the dot product. We also
    need to clamp the result to the [0,1] range. */
    NdotL = max(dot(normal, lightDir), 0.0);

実際にこれを少し最適化して、を節約することができますnormalize()(したがって、半額になりinvsqrtます)。v1ベクトルと、、およびv2スカラーs1s2:の場合は注意してください。

dot(v1 * s1, v2 * s2) == s1 * s2 * dot(v1, v2);

したがって、v1v2が正規化されていない場合は、それらの2乗の大きさを因数分解し、それらを乗算し、合計をinvsqrt1回乗算して、内積を縮小することができます。


85kの三角形、約50 FPS?GTX660Mを使用すると、問題なく実行できます。実行しているハードウェアで大幅に高い数値が得られるとは思いません。

固定機能パイプラインに関しては、今日のすべてのクールな子供たちは完全にプログラム可能なパイプラインを使用しています。ただし、FFによってパフォーマンスが失われることはありません。内部的にドライバーはFF状態をシェーダーのセットにコンパイルするため、とにかくシェーダーとしてGPUで実行されます。

@JamesSteeleが言及しているように、頂点データで参照の局所性を適切に保つことができる場合は、インデックス付きの三角形を使用することをお勧めします。ただし、入力データを再コンパイルするか、再調整する必要がある場合があります。

于 2013-02-04T09:30:25.687 に答える