Linked Listを利用したOIT - 2
[前回の続き]
今回からはOIT11をどのように改造してLinked List OITを実装するのかを説明していきます。
説明が足りないところは前述のスライドやOIT11サンプルを参照していただけるとわかると思います。特にスライドはアニメーションがあったりするので英語が読めなくてもなんとなくわかります。また、スライドにはシェーダコードも記載されています。実装は非常に簡単なのでスライドが読めればできたも同然です。
1. 描画フローの変更
通常の半透明プリミティブの描画フローでは、
半透明プリミティブの描画時にZテストをパスしたらアルファブレンドしてフレームバッファを更新
という流れですが、
OITではプリミティブの描画時にはZテストは行わず、フレームバッファも更新しません。その代わり、全ての半透明のフラグメントデータを別の大きなバッファに格納します。フラグメントデータは、最低でも深度値とカラーを持ち、スクリーン上のどこかのピクセルに属しています。半透明ポリゴンが重なるところでは、1つのピクセルに複数のフラグメントデータが存在する状態になります。
半透明の描画終了後、各ピクセルごとにフラグメントデータを深度値でソートしてブレンディングを行い、結果のカラーをフレームバッファに描きこみます。
この「別の大きなバッファに格納する」方法がDirectXのOIT11サンプルとLinked List版とで異なります。
OIT11サンプルではフラグメントデータを各ピクセルごとに順番につめていきます。
つまり、スクリーンの左上から
0番目のピクセルに描画されるフラグメントデータ1,2,3…
1番目のピクセルに描画されるフラグメントデータ1,2,…
というようにピクセル順に複数のフラグメントデータをバッファに格納します。
このため、フラグメントデータを保存する前に、あらかじめ各ピクセルに描画されるフラグメントデータの数や、各ピクセルの格納先インデックスがわかっていなければなりません。
OIT::Render関数を見てみると全体の流れがわかります。
// Create a prefix sum of the fragment counts. Each pixel location will hold
// a count of the total number of fragments of every preceding pixel location.
// 2.各ピクセルにおけるはじめのインデックスを計算
CreatePrefixSum( pD3DContext );
// Fill in the deep frame buffer with depth and color values. Use the prefix
// sum to determine where in the deep buffer to place the current fragment.
// 3.フラグメントデータを保存
FillDeepBuffer( pD3DContext, pRTV, pDSV, pScene, mWorldViewProjection );
// Sort and render the fragments. Use the prefix sum to determine where the
// fragments for each pixel reside.
// 4.保存してあるフラグメントデータをピクセルごとにソートして描画
SortAndRenderFragments( pD3DContext, pDevice, pRTV );
1と3はプリミティブを描画するため、ピクセルシェーダで実装されています。その他のパスはCompute Shaderで実装されています。
Linked Listを利用したOITでは各フラグメントデータを描画された順にバッファにつめこんでいきます。なので、上記の1、2の処理は必要ありません。その代わり、フラグメントデータ自体に、そのピクセルにおける次のフラグメントデータのインデックスを含めておき、1つのピクセルに属するフラグメントデータが1つのリストとしてつながるように格納します。
ということで、OIT::Render関数は以下のように書き換えられます。
// Fragment and Link creation.
// 1.Linked Listの作成
CreateFragmentLink( pD3DContext, pRTV, pDSV, pScene, mWorldViewProjection );
// Sort and render the fragments.
// 2.保存してあるフラグメントデータをピクセルごとにソートして描画
SortAndRenderFragments( pD3DContext, pDevice, pRTV );
パス2はスクリーンの各ピクセルごとにシェーダを走らせるのでピクセルシェーダでの実装となります。つまり、Linked List OITではCompute Shaderを使用しません。
[つづく]

// 1.各ピクセルに描画されるフラグメントデータの数を数える