このエントリはUnity #3 Advent Calendar 2020の20日目の記事です。
qiita.com
はじめに(宣伝)
筆者は「Unityシェーダープログラミングの教科書」という同人誌シリーズを頒布しています。最新の4巻ではSRP/URP(UniversalRP)についての技術的な解説と、URPの標準シェーダーであるLitシェーダーが実装している物理ベースレンダリングのコードを解説しています。この辺の資料を探している方は是非ご検討下さい。
「Unityシェーダープログラミングの教科書4 SRP[1]UniversalRP/Litシェーダー解説編」
s-games.booth.pm
この記事の目的
上記のシェーダー本4では実験的実装だったために取り扱わなかった「Renderer Feature」という機能が、URP10リリースによってSSAOを有効化させるインターフェイスとして部分的に正式化され、ドキュメントにも掲載されることになりました。
そこで、まずUnity2020.2リリースとURP10の関係について説明した上で、Renderer Featureの意図と役割について解説し、URPのSSAOを実行してみたいと思います。
なお、Renderer Feature自体はまだ実験的機能なので、その点は踏まえておいてください。
Unity2020.2リリース!&URP10リリース!
12月15日、待望のUnity2020.2がリリースされました(いやあ、長かったね!)
blogs.unity3d.com
このリリースによって、URP(Universal Render Pipeline)のメジャーバージョンが10(最新は10.2.2)になりました。
docs.unity3d.com
URPはPacakgeManagerで管理されています。ただし安定性確保のため、現在のリリースサイクルではUnityとURPのバージョンを対応させるルールになっています。詳細は以下のリンクを参照してください。
docs.unity3d.com
実質的には、URPは8.x系(Unity2019.3~2019.4用)、9.x系(Unity2020.1用)、10.x系(Unity2020.2用)で平行してブランチが用意され、互いにバックポートしながら開発を進めているようです*1。
その上で、9.x系は現在もプレビューリリースでして(最新はpreview72?)、Unity2020.1までは8.2系が使用されていました。なので、URP10は久々のメジャーアップデートと言って良いでしょう。
URP10の新機能であるSSAOとRenderer Featureについて
URP10では様々な新機能が追加されています。その大半はビルトインパイプライン(≒Standardシェーダー)との互換性向上を目的とした物で、ようやくまともに移行を検討できる状態になりつつあるかもしれません。
その中で、URP10の新機能として大きく紹介されているのがSSAOの採用です。URP10ではSSAOをRenderer Featureという機能を介して実装しています。このRenderer Featureは、9.x以前は実験的機能としてUIがあるだけで、実質的に使い道がありませんでした。URP10でようやく、SSAOを有効化させるという役割が与えられたのです*2。
Renderer Feartureについて
Renderer FeartureはURPの機能の一つで、レンダリングパイプラインの中にユーザーカスタムなレンダーパス(Render pass)を追加する物です*3。
docs.unity3d.com
シェーダープログラミングでは一つの頂点、あるいは一つのピクセル毎に、複数のレンダーパス(以下pass)が実行されます。各passにはユニークなタグが設定されていて、C#側からタグに紐付いたシェーダーコードが実行されます。
シェーダープログラミングでは、通常のレンダリングに前処理/後処理を行う為に、追加のカスタムpassを設定することがあります。このカスタムpassを実行するためには、これまではC#コードを直接改造する必要がありました。これをインスペクタ上で指定できるようにしようとしているのが、Renderer Feartureなのです*4。
SSAOについて
SSAO(スクリーンスペースアンビエントオクルージョン)は、リアルタイムレンダリングでAO(アンビエントオクルージョン)を実現するポストエフェクト処理のことです。AOについてはこちらも参照してください。
someiyoshino.info
旧ビルトインパイプラインではポストプロセス処理の一つとしてSSAOが用意されており、それがようやくURPでも使えるようになったわけです。
docs.unity3d.com
通常、URPのポストエフェクト処理は、HDRPと同じくVolumeコンポーネントを通じて設定します。しかし、SSAOの実現には簡易的なG-Bufferをレンダリングするレンダーパスを追加する必要があるため、URP10で採用されたバージョンでは、Renderer Feartureを介して実現することになったのだろうと思われます。
Renderer Feartureに至るまで
まず前提から。あるUnityプロジェクトで使用されるレンダーパイプラインは、Project Settingsウィンドウの"Graphics>Scriptable RenderPipeline"で設定されるRender Pipeline Assetファイルによって既定されます(設定されていない場合はビルトインパイプラインが適用されます)。URPであればUniversal Render Pipeline Assetファイルが設定されています。
Universal Render Pipeline AssetはURP全体の設定を管理するアセットファイルです。その中の"General>RendererList"に登録されたScriptableRendererData派生クラスのアセットがレンダラー、つまりレンダーパイプラインの実装本体になります。
現時点でのURPのレンダラーはForwardRendererアセット(ForwardRendererDataクラス)のみが提供されています。将来的に遅延シェーディングのレンダラーが追加されるなどした場合はここで選択することになるでしょう。また、RendererListには複数のアセットが登録可能で、Cameraオブジェクトごとに使用するレンダラーを指定できます。
こちらがForwardRendererアセットのインスペクタになります。一番下にあるのが、Renderer Featureです(ようやく到着した!)。
SSAOを有効化させてみる
"Add Renderer Feature"をクリックすると、"Screen Space Ambient Occlusion"と"Render Objects(Experimental)"の2個が表示されます。
"Screen Space Ambient Occlusion"を選択すると、SSAOの機能が有効になり、以下の設定画面が追加されます。
また、プロジェクトウィンドウのForwardRendererアセット配下にも項目が追加されます*5。
ただし、これはForwardRenderer.assetファイルのデータが更新されただけで、新規のassetファイルが追加された訳ではないのでご注意ください。
SSAOを有効化させてみる
SSAOを有効化すると以下のように描画が変化します。
SSAO無し
SSAOあり
オブジェクト同士の境界が自然に暗くなっているのが分かります。これがSSAOの威力です。リアルタイム描画でこの表現ができるのは素晴らしいですね。
Renderer Featureの内部ロジック(では残念ながらなく)
で、この「カスタムレンダーパスを追加する」というのは内部的にどうやって実現しているのかというと、土屋が確認した限りは、実はまだ実現できていません(!)。これ土屋の勘違いだったら是非指摘してもらえると嬉しいです*6。
コードを見ると、ShaderPreprocessor.csで、現在使用出来るレンダーパスが列挙体のビットフラグとしてハードコーディングされてます*7。
内部的にはこのフラグを見て、SSAOが有効なら簡易的なG-Bufferをレンダリングし、さらに_SCREEN_SPACE_OCCLUSIONキーワードを有効にして、Lighting.hlsl内で必要な演算を実行しているようです。
というわけで、Renderer Feartureでユーザーカスタムなレンダーパスを追加できるようになるのは、まだしばらくかかるかと思われます。みんなで楽しみに待ちましょうね。
*1:それ本当に安定開発に貢献しているの?とも思いますが
*2:というか、Renderer FeatureはURP10現在でもSSAOを有効化させる以外に(恐らく)使い道がありません。これについては後述しています
*3:ただし、"Renderer Fearture(レンダラー機能)"という名称の通り、実際には単にレンダーパスを追加するだけではなく、「レンダーパスの追加を伴うカスタム機能をレンダラーに追加する」が主目的なのだと思われます
*4:現時点では実験的機能であり、正式には使用できません
*5:リフレッシュタイミングが微妙で、場合によってはUnityを再起動しないと反映されないかもです
*6:早く使いたいので
*7:SSAOはScreenSpaceOcclusionが該当。ただし、これがpassの名前という訳ではありません。