Skinning10 サンプル

このサンプルは、Direct3D 10 ハードウェアでスキニングを実行するためのさまざまな方法を示しています。

Ee416429.d3d10_sample_Skinning10(ja-jp,VS.85).jpg

パス

ソース SDK ルート\Samples\C++\Direct3D10\Skinning10
実行可能ファイル SDK ルート\Samples\C++\Direct3D10\Bin\x86 または x64\Skinning10.exe

サンプルが動作するしくみ

このサンプルは、メッシュ頂点ストリーム内で定義されているボーン インデックスを通じてトランスフォーム行列にインデックスを付ける種々の方法に焦点を当てています。このサンプルでは、骨組アニメーションや階層トランスフォームは扱いません。

Skinning10 サンプルは、GPU スキニングのために、ボーン トランスフォーム行列にインデックスを付ける 4 通りの方法を示します。また、ストリーム アウトを使って (ジオメトリ シェーダーを使用せずに)、スキニングされたモデルを複数のレンダリング パスで使用するときの頂点処理のコストを削減する方法を示します。

定数バッファー スキニング

ここで説明する 4 つのすべての方法の中で、定数バッファー スキニングは、従来の Direct3D 9 スタイルの GPU ベース スキニングに最も近い方法です。トランスフォーム行列がシェーダー定数にロードされ、頂点シェーダーが頂点ストリーム内のボーン インデックスに基づいてそれらにインデックスを付けます。Direct3D 9 の通常の実装と同様に、頂点あたりのボーン インデックスの数は 4 に制限されます。Direct3D 9 の通常の実装とは異なり、このサンプルで使用しているスキニング方法では、行列のパレットを作成せず、また、メッシュを同様のインデックスを共有する頂点によってセクションに分割しません。これは、Direct3D 10 で許容される定数バッファーのサイズが、Direct3D 9 の定数バッファーよりもはるかに大きいからです。数百個のボーンを含むようなメッシュでだけ、ボーン行列のパレットへの分割が必要になります。シェーダー内のボーンにインデックスを付けるためのコードは次のとおりです。

	mret = g_mConstBoneWorld[ iBone ];

テクスチャー バッファー スキニング

テクスチャー バッファー (または tbuffer) スキニングは、基本的なしくみは定数バッファー スキニングとよく似ています。実際、HLSL でボーンを参照する方法は同じです。

	mret = g_mTexBoneWorld[ iBone ];

唯一の違いは、トランスフォーム行列を保持する定数バッファーが、通常の定数バッファーではなく、テクスチャー バッファーとして宣言されることです。バッファーを tbuffer として宣言すると、テクスチャーのようにアクセスできるようになり、テクスチャーのよりランダムなアクセスを必要とするアプリケーションのパフォーマンスを向上させることができます。

テクスチャー スキニング

tbuffer ベースのスキニングの論理拡張は、ボーンのトランスフォーム行列を浮動小数点テクスチャーに格納することです。ビデオ ハードウェアのテクスチャー フェッチ パフォーマンスに応じて、また、シェーダーが ALU フェッチによる制約を受けるかテクスチャー フェッチによる制約を受けるかに応じて、この方法はスキニングの速度を大幅に向上させることができます。

        iBone *= 4;        float4 row1 = g_txTexBoneWorld.Load( float2(iBone,     0) );        float4 row2 = g_txTexBoneWorld.Load( float2(iBone + 1, 0) );        float4 row3 = g_txTexBoneWorld.Load( float2(iBone + 2, 0) );        float4 row4 = g_txTexBoneWorld.Load( float2(iBone + 3, 0) );                mret = float4x4( row1, row2, row3, row4 );

ここでわかるように、4 つのテクスチャー フェッチによるトランスフォームのフェッチ動作は、ハードウェアによっては、同じトランスフォームを定数バッファーからフェッチするよりも高速になる場合があります。

バッファー スキニング

トランスフォーム行列にインデックスを付ける最後の方法は、バッファー スキニングによるものです。バッファー スキニングでは、行列は ID3D10Buffer オブジェクトにロードされ、次にこのオブジェクトがシェーダー リソースとしてバインドされます。行列データは、Load コマンドを使ってバッファーからロードされます。

	iBone *= 4;        float4 row1 = g_BufferBoneWorld.Load( iBone );        float4 row2 = g_BufferBoneWorld.Load( iBone + 1 );        float4 row3 = g_BufferBoneWorld.Load( iBone + 2 );        float4 row4 = g_BufferBoneWorld.Load( iBone + 3 );                mret = float4x4( row1, row2, row3, row4 );

ストリーム アウトによる複数パスの高速化処理

GPU スキニングに関係する従来からの問題の 1 つは、1 つのオブジェクトに対して複数のパスを実行する必要がある場合に、パスごとにオブジェクトをスキニングする必要があることです。たとえば、シーンに 3 つのシャドウマップされた光源とスキニング済みのキャラクターがある場合に、各ライトのシャドウを生成するために、キャラクターを 3 回 (各ライトにつき 1 回) 描画する必要があります(ジオメトリ シェーダーを使ってジオメトリを増幅し、複数のレンダー ターゲットにストリーミングすることによって、この問題を軽減することもできます)。そのためには、頂点シェーダーがキャラクターを 3 回スキニングする必要があります。実際にはこのうち 2 回は余分です — キャラクターを一度スキニングし、このトランスフォーム済みのキャラクターを保存してフレームの残りの部分に使用できれば効率的です。そうすれば、10 回のパスを必要とするシーンに対して、キャラクターを 10 回スキニングする必要はありません。

ストリーム アウトによってそれが可能になります。Direct3D 10 では、サンプルがキャラクターを一度スキニングし、その結果をビデオ メモリー内のバッファーにストリーム出力できます。その後、残りのパスでは、アプリケーションはスキニング済みの頂点を使用するだけで済みます。キャラクターのレンダリングのために 10 回のパスが必要である場合には、この方法で頂点処理を 10 分の 1 に減らすことができます。

このサンプルでは複数のパスをレンダリングせず、代わりにキャラクターを複数回レンダリングします。これは、同じ問題を別の方法で表しています。ストリーム アウトが無効にされているとき、アプリケーションはキャラクターをレンダリングするたびにそれをスキニングします。しかし、ストリーム アウトを有効にすると、アプリケーションはキャラクターを一度スキニングし、その結果をストリーム アウトを通じて保存し、そのスキニング済みのジオメトリを使って複数のキャラクターをレンダリングします。