テクスチャとして読み込むデプスバッファと、アウトプットマージャーにセットしたデプスバッファでデプステストを使っての事例として、ソフトパーティクルがお題に合致している感じだったので試してみました。
ようやく読み込み専用 DepthStencilView を意味あるものとして使えたかなと思います。
ソフトパーティクル
ソフトパーティクルとは、パーティクルに使用するビルボードとモデルや背景の境界部分をぼかすことによって、ハードなエッジを見せないようにするための技法です。
ハードなエッジとは以下のようなものです。ソフトパーティクルを無効化して描画したものになります。
特にトーラスの部分や床の部分にパーティクルで使っているビルボードの境目が見られると思います。
実装方法
既に描画されているデプスバッファの値と、パーティクルを描画する場所のデプス値が一定範囲内のときに適当にアルファ値を調整します。
既に描画されているデプス値は、前の記事で説明したように取得してきます。ただ今回はパーティクルの頂点シェーダーでビュープロジェクション後の位置情報を出力しています。
出力した位置座標は w で除算してピクセルシェーダーで使用するようにします。
この時の x, y の値はプロジェクション空間のものなので、これを 0~1の値へ変換してデプスバッファをフェッチすることになります。
そうしてフェッチしたZ値と、計算したZ値を比較して、一定範囲だったらアルファチャンネルを操作します。
シェーダー例
プログラムでは以下のようにしてみました。
float4 main( VS_OUTPUT _In ) : SV_TARGET0 { float2 fetchUV = _In.PosProjected.xy; fetchUV.xy /= _In.PosProjected.w; fetchUV.x = 0.5*(fetchUV.x + 1.0); fetchUV.y = -0.5*(fetchUV.y - 1.0); float vz = _In.PosProjected.z / _In.PosProjected.w; float z = texDepth.Sample(samplerDepth, fetchUV.xy); float d = abs(vz - z); float threshold = 0.00075; if (d < threshold) d = d / threshold; else d = 1.0; float4 color = texSmoke.Sample(samplerSmoke, _In.UV.xy); color.a *= d; return color; }
結果
今回のソフトパーティクルは以前のディファードレンダリングサンプルでライティング処理を終わった後に入れてみました。
このタイミングにした理由は、そのまま描画してもデプステストが有効で、不透明なオブジェクトの手前にのみ描画するといったことが出来たためです。またテストではパーティクルにはライティング適用させる必要もなかったので、こうなっています。
ただ実際には、ちゃんとレンダリングのパスや、パーティクルとライティング結果のコンポジットなど考慮すべき点は多くあると思いますので、ご注意願います。
描画結果としてはうまくモデルや背景となじんでいるのではと思います。少なくともハードなエッジは軽減されました。gif アニメにするとその印象は分かりにくくなっているように思いますが、静止画で見るとエッジの部分はマシになったのが分かるのではないでしょうか。
今回どのくらい近くなったらアルファ値操作をするかですが、適当に値を選びました。ただしこれはZバッファに入っている値の単位で適当です。
ワールド空間やビュー空間でどのくらい近くなったらアルファを操作するか、といったほうがもしかすると扱いやすいかもしれません。