しゅみぷろ読者になるしゅみぷろプログラミングとかこの広告は、90日以上更新していないブログに表示しています。Pipelineまえがきタイトルの通りで、UnityでRaymarchingを試してみました。以下でリポジトリを公開しています。github.com既にやっている方のを真似させて頂いただけですので、詳しく知りたい方はid:i-saintさんとid:hecomiさんの記事を参考にしてください。この記事の目的は、実装してみて必要になった知識を自分なりにまとめてメモしておくことです。i-saint.hatenablog.comtips.hecomi.com今回行うRaymarchingでは、今まで記事として残してきたUnityのレンダリングパイプラインやシェーダーに関する知識が必要です。しゅみぷろUnityCG.cgincの定義値は以下を参考にしました。qiita.com座標変換に関して参考にさせていただいた記事が以下になります。tech-sketch.jp今回の手法ではCommandBufferを用いるのですが、これに関しては以下の記事を参考にさせていただきました。www.gaprot.jpまた、サンプルとして掲載しているものに使用した距離関数はid:i-saintさんの記事で紹介されていた以下のサイトのモノをほぼそのまま使わせていただきました。SandboxRaymarchingについてはid:hecomiさんの記事で紹介されていた以下のサイトを参考にしました。demoscene.jpやったことを大雑把におさらいしてみるここでは順を追って、何から実装していったのかをメモしておきます。大雑把なおさらいなので、詳細は後述してます。Raymarchingで球を描画してみるまずはじめにやったのは、Raymarchingによる球の描画です。細かい注意点などは参考にさせて頂いた記事の方で詳しく書いてくれていたので、そちらを参考に書いていきました。余談というかしょうもない話なのですが、実はこの段階で何故かDeferredShadingでグラフィックスドライバが応答停止してしまうという問題に遭遇しました(4時間持って行かれました...)。本当に単純なミスなのですが、レイを飛ばすループ変数のインクリメント部分で、誤ってループ用の変数ではなくフラグメントシェーダーのパラメーター変数iに対してインクリメントを行ってしまっていたのが原因でした。皆さんもこんなことがないように注意しましょうね(無い)...。距離感数をいじってみる次に、距離関数を色々と弄ってみました。Sandboxのコードをほぼそのまま使っています。シェーダーのリファクタリングとバリアント作成ここまで1つのシェーダーファイルにすべての処理をコリゴリ書き込んでいました。シェーダーをの処理をいくつかのincludeファイルに分割し、距離関数についてはバリアントを定義することで複数のシェーダーファイルに分割させました。KeywordEnum属性でシェーダーの変更ができるようにしてあります。インスペクターからマテリアルのドロップダウンリストを変更することで距離関数のアルゴリズムを変更しています。陰影を確認してみるオブジェクトをシーンに配置し、影の描画処理が行われているかを確かめました。影が描画されているのが確認できると思います。先ほどまでの工程ではCommandBufferをカメラに追加する処理をOnWillRenderObjectによって行っていました。OnWillRenderObjectは、ExecuteInEditMode属性が付加されているスクリプトだとSceneビューを映し出すカメラについても呼び出しを行ってくれているみたいで、参考にしたコードではCommandBufferとCommandBufferを追加したカメラをDictionaryとして保持し、OnWillRenderObjectが呼び出されるすべてのカメラがRaymarchingレンダリングの対象となるように作られていました。Raymarchingによる描画が複雑でなければこのままの方がやりやすくて良いのですが、距離関数によってはライトやその他のオブジェクトの配置が難しくなってしまいました。SceneビューとGameビュー双方を見ながらシーンを構成していけるように、カメラ用のスクリプトの変更と追加を行いました。球の描画についてここでは球を描画するまでの流れと実装について書いていきます。やったことは、Quadを生成し、CommandBufferでG-Bufferパス後にこのQuadを描画させることです。このQuadのレンダリングには独自で定義したMRTによるG-Bufferの生成を行うシェーダーを用います。Lightingパス前にこれを行うことで、ライティングに関してはUnity標準の一貫したライティングが施されるようになります。このQuadのレンダリング工程ではG-Bufferを好きなように創って、あとのライティングなどの処理はUnityに移譲するといったことをやっています。以下のスクリプトをカメラにアタッチすることで実現しています。}}次に、MRTでG-Bufferを出力するシェーダーを書きました。}}省略している部分もありますが、概ねこういった感じでG-Bufferを出力します。注意:ステンシルバッファの7ビット目(128)が立っていない場合、描画は無視されます。描画を行うには、ステンシルテストに合格させ、7ビット目に書き込みを行います。ここまででG-Bufferへの書き込みの準備が整いました。今回はRaymarching法で計算したデータをG-Bufferへの出力にします。書き込むデータは、距離関数を用いて計算される仮想的な空間のモデル情報です。図の灰色で塗りつぶされた空間は距離関数で表現される実世界(Unityで言うScene上)には存在しない空間です。この空間の座標系とUnityの座標系を一致させてあげる必要があります。この部分はid:i-saintさんの記事で詳しく紹介されています。シーンの座標系を一致させる必要があります。結論としては以下のコードで必要な情報を抽出できます。}これらをまとめた定義は以下になります。focalLen));}//ある位置(レイがオブジェクトにヒットした位置)の深度値を取得する。//depthの算出方法はDirect3DとOpenGL系で違う。#endif}//ある位置(レイがオブジェクトにヒットした位置)におけるオブジェクトの法線を取得する。//x,y,zは偏微分によって求められる。*2.0-1.0//してやる必要がある(格納時に負数をなくし、取得時に再現する)。//RAYMARCHINGDistanceFunctionVariant.cgincファイルをincludeしていますが、これには後でDistanceFunctionを定義します。compute_depth関数では、depthの算出方法がDirect3DとOpenGL系で違うため処理の分岐が必要です。4.1)ではSHADER_TARGET_GLSLプリプロセッサーマクロが未定義になり、depthがおかしくなる(Direct3Dでのdepth算出をしてしまう)ようです。わかりませんが、UNITY_UV_STARTS_AT_TOPで判定することで凌いでいます(Mac、Win共に正常な動作をしました)。normal関数ではオブジェクトにヒットした位置の近隣ピクセルの勾配から法線を推測して返しています。距離関数について距離関数は位置を引数にとり、その位置から一番近いオブジェクトへの距離を返す関数です。Rayが接触した)と仮定することができます。引数に指定したある位置において0に近い値が返ってきたら、その位置情報をもとにオブジェクトを描画するようにします。裏を返せば、描画させたい位置で0に近い値を返すような距離関数を作ればゴリ押しで好きなモデルを書くこともできます。距離関数は今後色々と実験的に作っていこうと思っているので、面白いものが出来たら別記事で紹介させていただくかもしれません。今回のサンプルで利用した距離関数のバリアントを定義したファイルは以下のようになりました。//DISTANCE_FUNCTIONシェーダーバリアントの利用に関してDistanceFunctionのシェーダーバリアントですが、このプロジェクトではRaymarchingの確認を行えれば十分で、DistanceFunctionアルコリズムの切り替えに便利だと判断して使っています。今後このバリアントを有効に使っていくために考えていることが、G-Bufferへの出力を決定するアルゴリズムのバリアントを作り、それと組み合わせて使っていくことです。全部Uniteのせい。)はてなブログをはじめよう!es_programさんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。