多くの種類のプリミティブを描画できるレンダラーを実装するにはどうすればよいですか?


8

これは、インデックス付きプリミティブの描画に関して以前に私が尋ね質問にいくぶん関連しています

私の問題は、たくさんを描きたかったのに、1つの立方体しか描いていなかったことです。問題は、それぞれの新しいインスタンス化で頂点バッファーとインデックスバッファーを上書きするCubeことであり、代わりに原点に1つ作成してから多数を描画し、変換マトリックスを介してシェーダーに渡し、異なるように見えるようにする必要があるということです。場所。これは美しく機能しました。

しかし、今は新しい問題があります。どのようにして多くの異なるタイプのプリミティブを描画するのでしょうか。

これが前の質問からの私のコードです:

Cube::Cube(D3DXCOLOR colour, D3DXVECTOR3 min, D3DXVECTOR3 max)
{
// create eight vertices to represent the corners of the cube
VERTEX OurVertices[] =
{
    {D3DXVECTOR3(min.x, max.y, max.z), colour},
    {D3DXVECTOR3(min.x, max.y, min.z), colour},
    {D3DXVECTOR3(min.x, min.y, max.z), colour},
    {min, colour},
    {max, colour},
    {D3DXVECTOR3(max.x, max.y, min.z), colour},
    {D3DXVECTOR3(max.x, min.y, max.z), colour},
    {D3DXVECTOR3(max.x, min.y, min.z), colour},
};

// create the vertex buffer
D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(VERTEX) * 8;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
bd.MiscFlags = 0;

device->CreateBuffer(&bd, NULL, &pBuffer);

void* pVoid;    // the void pointer

pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pVoid);    // map the vertex buffer
memcpy(pVoid, OurVertices, sizeof(OurVertices));    // copy the vertices to the buffer
pBuffer->Unmap();

// create the index buffer out of DWORDs
DWORD OurIndices[] = 
{
    0, 1, 2,    // side 1
    2, 1, 3,
    4, 0, 6,    // side 2
    6, 0, 2,
    7, 5, 6,    // side 3
    6, 5, 4,
    3, 1, 7,    // side 4
    7, 1, 5,
    4, 5, 0,    // side 5
    0, 5, 1,
    3, 7, 2,    // side 6
    2, 7, 6,
};

// create the index buffer
// D3D10_BUFFER_DESC bd;    // redefinition
bd.Usage = D3D10_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(DWORD) * 36;
bd.BindFlags = D3D10_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
bd.MiscFlags = 0;

device->CreateBuffer(&bd, NULL, &iBuffer);

iBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pVoid);    // map the index buffer
memcpy(pVoid, OurIndices, sizeof(OurIndices));    // copy the indices to the buffer
iBuffer->Unmap();

//this is simply a single call to the update method that sets up the scale, rotation
//and translation matrices, in case the cubes are static and you don't want to have to 
//call update every frame
Update(D3DXVECTOR3(1, 1, 1), D3DXVECTOR3(0, 0, 0), D3DXVECTOR3(0, 0, 0));
}

明らかに、コードを複製して別のオブジェクトまたはシェイプになるように変更した場合、初期化される最後のシェイプが頂点バッファーを上書きしますね。

複数の頂点バッファーを使用しますか?新しい頂点バッファーを古いものに追加し、適切なインデックスを使用してそれらを描画しますか?どちらでもいいですか?どちらも?

回答:


12

サポートするジオメトリのタイプごとに新しいクラスを作成することはおそらく悪い考えです。それは非常にスケーラブルまたは保守可能ではありません。さらに、必要と思われるクラス設計は、ジオメトリ自体とそのジオメトリのインスタンスデータを管理するタスクを混同しているようです。

ここにあなたが取ることができるアプローチがあります:

2つのクラスを作成し、MeshそしてMeshInstance。A Meshには、共有ジオメトリのすべてのプロパティが含まれます。基本的には、頂点バッファーとインデックスバッファーです。必要に応じて、立方体の頂点データ(または球体の頂点データなど)を含むメッシュを作成するヘルパー関数を作成できます。そのMeshようなヘルパー関数を非メンバー、非フレンド関数として実装できるように、クラスのパブリックインターフェイスを調整する必要があります。

MeshInstance一方、はを参照して構築する必要がありますMesh。にMeshInstanceは、個々のオブジェクトのプロパティが含まれています。これは、ワールドトランスフォーメーションであり、レンダリングに使用されるシェーダーオーバーライドなどです。

このようにして、新しい立方体を作成する場合は、最初にMesh、起動時に作成したプリミティブメッシュオブジェクトの共有ライブラリから立方体を表すオブジェクトを取得します。次に、新しいMeshInstanceキューブを作成し、そのキューブに割り当てMeshます。

レンダリングするとき、描画するすべてのMeshInstanceのリストを作成し、それらを送信します。Meshまたはテクスチャでグループ化すると、状態変化のオーバーヘッドを最適化できます(つまり、キューブメッシュに対応するすべてのメッシュインスタンスを一度に描画し、次に球体メッシュに対応するすべてのメッシュインスタンスを描画するため、SetVertexBuffer呼び出し回数が少なくなります。 D3Dデバイス)。テクスチャやシェーダなど、他の状態でグループ化することもできます。

このようにして、頂点データを複製するメモリの浪費を回避し、新しい(a)プログラムでメッシュを作成する関数または(b)のファイルからメッシュをロードする関数を実装するだけで、レンダリングシステムが任意のプリミティブセットにスケーリングできるようにします。特定のフォーマット。

レンダーパイプラインが一般化されたMeshオブジェクトの観点から機能すると、新しい手法や最適化にグローバルに適応させるのがはるかに簡単になります。

具体的なコメント:

明らかに、コードを複製して別のオブジェクトまたは形状になるように変更した場合、初期化される最後の形状が頂点バッファーを上書きします。ね?

いいえ。投稿したコードpBufferでは、静的メンバー変数である場合にのみ上書きされます。クラスをコピーして(たとえば)クラスを作成した場合Sphere、それは新しい静的変数になります。これはまだ悪い考えです。

複数の頂点バッファーを使用しますか?新しい頂点バッファーを古いものに追加し、適切なインデックスを使用してそれらを描画しますか?どちらでもいいですか?どちらも?

上で説明したテクニックの素朴な実装には、複数のバッファが含まれます(共有ジオメトリの各セットに1つ)。そのジオメトリが静的である場合は、すべてを1つ(またはバッファサイズに実用的な最適な制限があるため複数)バッファに格納して、バッファの状態変化をさらに最小限に抑えることができます。これは最適化と考える必要があり、読者のための演習として残しておきます。最初にそれを動作させ、次にそれを速くすることを心配します。


「ありがとう」コメントを投稿するつもりはないが、これは非常に役立つ。ありがとうございました!
SirYakalot、2011年

現時点では、pBufferとiBufferはexternです。これらのインスタンスメンバーを各Meshオブジェクトのメンバーにする必要がありますか?
SirYakalot、2011年

1
はい、それは始めるのに良い場所です。

これを実装するようになったので、その方法について少し考えていません。「必要に応じて、キューブ頂点データ(または球頂点データ)を含むメッシュを作成するヘルパー関数を作成できます。そのようなヘルパー関数を非メンバー、非フレンド関数として実装できるように、Meshクラスのパブリックインターフェイスを調整する必要があります。」非ヘルパー関数、非フレンド関数とは、具体的にどういう意味ですか?
サーヤカロット

クラスのメンバーではない関数(つまりグローバル関数)も、クラスのフレンドとして宣言されていない。言い換えると、「通常の」関数-公開APIを介してのみメッシュオブジェクトを操作します。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.