Shadow Volume(1)

次はGeometry Shaderの性能評価するぞ〜と思って、何か良い応用は無いかなと言うことで西川さんの本*1を読んだら、Shadow VolumeをGeometry Shaderでやるのが良さそうなのでコードを書こうとしたんだけど、文献が基本英語で読む気が起らずしばらく放置状態…(ステンシルバッファの使い方的なところは日本語サイトでもいくつかあるんだけど、エッジ検出のアルゴリズムとか実際に実装する際に気になる部分について書いてある情報がほとんど見つからないんだよね)

んで、そろそろ調べたことがたまってきたのでいったんメモ。


とりあえず影の書き方としては

  • 先に物体どもを普通にレンダリングする(不透明、半透明モデル両方とも)
  • 影用のパイプラインを走らせて物体のレンダリング結果と影を合成する

って感じかな。

んで、影用のパイプラインは

  • Vertex Shader … 頂点どもをワールド座標に変換する(ビュー座標でもよさげ)
  • Geometry Shader … エッジ判定をしてエッジの頂点から光源と逆方向へ影用頂点(Shadow Volume)を生成する
  • Pixel Shader … 影の色を決める

って感じで。
影用パイプラインには影のもとになる物体の頂点データを放り込んで、Geometry ShaderでShadow Volumeの頂点データを作る感じか。影以外の頂点は邪魔だから破棄するようにやればよいかと。(たぶんGeometry Shaderで生成した頂点だけがPixel Shaderまで行くんじゃないかなと)
んで、本当に影なのかどうかをステンシルバッファに記録して、ステンシルテストに合格した本当に影の部分だけ影色合成するって感じなのかな。

図にするときっとこんな感じかと。

[2011/12/29 18:30]追記
図を差し替え。
DirectXのサンプルだと2パス目が見あたらなかったので1パスで書いてみたけど、実際にPIX先生で追いかけてみたら2パスに分けて処理をしていた。自分でもコーディングしてみたけど、1パス目で何か書き出すとShadow Volumeそのものに色がついちゃう感じでダメだった。まぁデバッグには使えそうなんだけど、影を出そうとすると1パス目はステンシルだけ更新してRender Targetには何も出さないか、あるいはテクスチャに何か書き込むかするくらいにして2パス目で影に色を塗る感じになるみたい。2パス目はステンシルテストに合格した領域だけPixel Shaderが動けば良いって感じなので別にShadow Volumeの頂点をもう一度作る必要はないっぽい。DirectXのサンプルだと画面全体を覆う四角形で処理してるっぽいね。いっそのこと他のPost ProcessとセットにしてCompute Shaderで処理しても良いかも。
とりあえず図は2パス目も同じ頂点データを使って、Pixel Shaderだけ変える方式で書き直してみた。1パス目はテクスチャに光源からの距離を書き込んでみて2パス目で距離を見ながら影をつけてみる、的な。でも1パス目で書き込まれる距離値がステンシルテストを通過してないデータだから微妙かも。




ちなみにShadow Volumeには特許*2が取られているようなので、引っかからないように回避が必要だね。Wikipediaの「Shadow Volume」ページ*3を読むとどうやらDepth fail法に関する特許みたいなんだけど、つたない英語力で気合いでclaim欄を読むと

  • 法線ベクトルと光線ベクトルの内積が0より大きいときにエッジと判定する
  • ステンシルへの記録方法としてz-testに失敗したときに記録する(Depth fail)

あたりが特許としてのキモっぽく見える。(特許の内容は「Google Patent」とか「Google Scholar」とかからたどれる)
少なくともこれを両方とも備えるコードはアウトだと思う。エッジ判定アルゴリズムは単品回避でいけるか微妙だね。この項だけを回避してもDepth failを使ってたらダメかもしれない。Wikipediaの「特許請求の範囲」ページ*4を読むと、均等論って考え方で「エッジ判定ごときを変えてもこの特許はDepth failのつもりで出したからアウト」って言われるかもしれない。
ま、どっちのつもりで出してるか微妙だから両方回避が賢いかと。



てゅか、このエッジ判定アルゴリズム、かなりおおざっぱだと思うのだが。
光源を向いてる頂点なんていっぱいあるわけで。

例えば球体のモデルで考えると図の1〜6の頂点のうち、1と6は内積が0に近いかもしれないけど、2〜5は確実に内積が0より大きいと思う(図は平面で考えてるけど3次元でも理屈は同じ)。
でも、Shadow Volumeにしたいエッジの頂点って実は1と6だけじゃないのって思う。このあたりは他のアルゴリズムと一緒に整理して後で書こうと思う。

*1:西川 善司「ゲーム制作者になるための3Dグラフィックス技術」インプレスジャパン (2009/9/11) pp.117-118

*2:"Method for rendering shadows using a shadow volume and a stencil buffer" William Bilodeau, Boulder Creek;Michael Songy, Sunnyvale, both ol CA (US), US6384822 B1, May 7,2002

*3:http://en.wikipedia.org/wiki/Shadow_volume

*4:http://ja.wikipedia.org/wiki/%E7%89%B9%E8%A8%B1%E8%AB%8B%E6%B1%82%E3%81%AE%E7%AF%84%E5%9B%B2