DirectX12 でジオメトリシェーダーを使って遊んでみています。前回はポイントスプライトをやってみたので、今回はもう少し遊べるものをやってみました。
ひとつのアイデアとして、カメラのニアクリップに掛かる前に、ジオメトリシェーダーでよろしく処理をするというのを見かけました。通常カメラのニアクリップに掛かると、ポリゴンが欠けるようか割れるというか、あまり芳しい状態で表示されません。
これをマシにする方法として、次のようなものを見かけたのでやってみたいと思います。
- ニアクリップに掛かる手前から、ポリゴンを半透明化
- ニアクリップに掛かる前にポリゴンを分解
ジオメトリシェーダーで半透明化
ジオメトリシェーダーでやってみたときの実験シェーダーコードは次のようになります。固定値が色々と埋め込まれていますので、実際に使うときには定数バッファに逃がしてあげる必要があります。
ジオメトリシェーダーコード
[maxvertexcount(3)]
void main(
triangle GSInput In[3],
inout TriangleStream<GSOutput> stream )
{
float4x4 mtxVP = mul(view, proj);
const float dist = 3;
for (int i = 0; i < 3; ++i)
{
GSOutput v;
float4 viewPos = mul(In[i].Position, view);
v.Color = In[i].Color;
if (viewPos.z/viewPos.w > -dist)
{
v.Color.a = abs(viewPos.z) / dist;
}
v.Position = mul(In[i].Position, mtxVP);
stream.Append(v);
}
stream.RestartStrip();
}
実行結果
これを実行してみると次のようになります。
不透明オブジェクトが、半透明として処理しなくてはならなくなるあたりに課題はありそうですが、通常のニアクリップでポリゴンが切れるよりは綺麗に消えていくので悪くは無いなと思いました。
ジオメトリシェーダーで分解
ジオメトリシェーダーで各ポリゴンをカメラに近づいたときに分解する方法を試してみました。実装方法としては、次のような処理ステップで実現しています。
- ポリゴンの中心を求める
- ポリゴンの中心をキーとして、回転度合いを決定
- 処理範囲内になったら、プリミティブを回転&法線方向への移動
コードは割と長くなるので、上記の処理ステップのみの紹介としています。
実行結果
ティーポットのモデルを、カメラに近づいたときにばらばらになるようにしたものです。
この処理のあとに、法線方向への押し出し率に応じてプリミティブを縮小させたり、半透明の処理を行うともっと見栄えがかっこよくなるように思います。SAO風味のエフェクトとかは、そうやっているように見えます。
まとめ
Unity でこれらの実装例を多く見かけました。それぞれがわかりやすい解説をしてくれており、大変参考になります。一方で、世間的には Unity 使って実装してしまうほうが主流なのだろうなぁと感じました。
そして今回の例で言えば、DirectX12 を直接たたいて実装するのも Unityを使うにしても、結局のところシェーダーの実装だけなのでわざわざ苦労する DirectX12 でやる必要はないかもしれませんね。