はじめに
以前、id:i-saintさんとid:hecomiさんの記事を参考にUnityでCommandBufferを使ったRaymarchを試しました。 こちらの記事で紹介させていただいています。
今回はCommandBufferを使わないRaymarchingについてです。 きっかけはこちらのリプライ。
@hecomi @Es_Program CommandBuffer なしでレイマーチで G-Buffer 生成することも可能で、新しい方 ( https://t.co/ozxNPAu1cr ) ではそうなっています。これだと Scene ビューにも出るし影出すのも簡単なはずです。
— i-saint (@i_saint) March 22, 2016
この後も数件とても参考になるリプライを頂きました。ありがたや・・・
ということで、CommandBufferを使わないオブジェクトスペースのRaymarchingを試してみました。 id:i-saintさんの以下の記事を参考にさせて頂きました。
本記事では、以前作ったCommandBufferを使ったRaymarchingを元にどういった部分に手を入れたのかをメモしていこうと思います。 リポジトリは以下で公開しています。
オブジェクトスペースのRaymarching実装
カメラに描画されるメッシュ(オブジェクト)の表面位置からRayを飛ばすことで実現します。
まずは頂点シェーダーを以下のように修正し、通常のオブジェクトを描画するのと同じように座標変換するようにしました。
v2f vert(appdata v)
{
v2f o;
#if OBJECT_SPACE_RAYMARCH
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
#else
o.vertex = v.vertex;
#endif
o.screen = o.vertex;
return o;
プリプロセッサディレクティブで分岐しているのですが、「OBJECT_SPACE_RAYMARCH」が定義されている場合、オブジェクトスペースでRaymarchを行うようにしてあります。
これはPropertiesで次のように定義しているため、マテリアルから変更が可能です。
Properties{
.....
[Toggle(OBJECT_SPACE_RAYMARCH)]
_ObjectSpaceRaymarch("Object Space Raymarch", Float) = 0
}
1つだけ実装で躓いたのが、なぜか発生する「歪み」です。

レンダリングされた画像が歪んでいるのがわかるでしょうか?
原因は視錐台内部の頂点を正規化デバイス座標の範囲内に移す際の、w成分による除算タイミングを勘違いしていたことでした。
射影空間にある頂点座標をwで割ることにより「頂点をスクリーンに投影するための立方体の領域(-1≦x≦1、-1≦y≦1、0≦z≦1)に納める」事ができます。つまり、wは「視錐台の拡大率」を表しています
重要なのは「頂点シェーダーを抜けた頂点はそこで初めてwで割り算され、スクリーンの投影位置が確定する」ということです。 頂点シェーダー部分は
v2f vert(appdata v)
{
v2f o;
#if OBJECT_SPACE_RAYMARCH
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
#else
o.vertex = v.vertex;
#endif
o.screen = o.vertex;
return o;
}
となっています。出力されたscreenをRayの方向を決めるために使っているのですが、この値はデバイス座標に正規化されていません。別途この処理を行う必要があります。
そのため、フラグメントシェーダーで以下のように記述しました。
i.screen.xy /= i.screen.w;
これで歪みが消えました。
