Deferred ShadingをUnityで使う上での基礎知識をまとめました。
注意点として、この記事はあくまでUnityにおけるDeferred Shadingに焦点を絞った内容となります。
またScriptable Render Pipelineは考慮していません。
- Deferred Shadingとは?
- Deferred Shadingを行うための設定
- メリット1 : 多数の光源の影響を正確に描画できる
- メリット2 : G-Bufferをポストエフェクトで使える
- デメリット1 : 半透明のオブジェクトを処理できない
- デメリット2 : MSAAが使えない
- デメリット3 : レンダラごとに影を受ける設定ができない
- 影響するライトが多いほど負荷がかかるので注意
- モバイルでも使えるのか?
- ライティング用のシェーダを差し替える
- 関連
- 参考
Unity2018.4.0
Deferred Shadingとは?
まずDeferred Shadingの概念を簡単にまとめます。
最初にForward Renderingでは、描画対象のメッシュ一つ一つに対して、
頂点シェーダで座標変換を行い、フラグメントシェーダでライティング計算を行うことで
レンダーターゲットに結果を出力します。
Deferred Shadingにおいても、まずは描画対象を頂点シェーダとフラグメントシェーダで処理します。
ただしこのとき、Forward Renderingと異なりフラグメントシェーダでライティング計算をしません。
その代わり、ライティング計算に必要な情報だけをレンダーターゲットに描き込みます。
これはすなわちDiffuse色やスペキュラ色、法線などといった情報です。
これらのジオメトリ情報は別々のバッファに書き込むのでレンダーターゲットが複数必要になります。
これはMRT(Multiple Render Targets)という機能により実現されています。
またDeferred ShadingではこれらのバッファのことをG-Buffers(Geometry Buffers)と呼びます。
次にG-Bufferの情報を使ってLightingパスと呼ばれるパスで専用のシェーダを使ってライティング計算を行います。
この計算はライトの数だけ行われ、またライトの影響範囲内にあるピクセルだけが処理されます。
この部分の詳細は本記事では省きますが、Frame Debuggerを見ることでライトの形に応じたメッシュがシェーダで処理されていることが確認できます。
まとめると、Deferred ShadingではまずG-Bufferパスでライティングに必要な情報をG-Bufferに書き込み、
その情報とライティング用シェーダを使ってライトごとにライティング計算を行います。
以上がDeferred Shadingの概要です。
Deferred Shadingを行うための設定
それではUnityでDeferred Shadingを行う設定をしてみます。
まず、検証用に適当なシーンを用意します。
次に以下の手順で設定を行います。
- Edit > Project Settings...を選択
- Graphicsを選択
- Tier Settingsの各TierのUse Defaultsチェックボックスを外す
- 各TierのRendering PathをDeferredに変更する
これでレンダリングパスはDeferred Shadingに切り替わりました。
今回のシーンではレンダリング結果を見ても特に変化は見られません。
しかしFrame Debuggerを確認すると、しっかりDeferred用の処理が呼ばれていることが確認できます。
メリット1 : 多数の光源の影響を正確に描画できる
さて前節のように、Deferred Shadingに切り替えただけでは大きな変化はありませんでした。
ではなぜわざわざDeferred Shadingを使うのでしょうか。
その答えとして、「一つのオブジェクトに対してより多くのライトの影響を比較的低負荷で与えることができる」ということがあります。
例として、前節のシーンに多くのライトを配置してみます。
まずこれをDeferred Shadingで描画してみます。
各オブジェクトがきれいにライトの影響を受けていることがわかります。
次にレンダリングパスをForward Renderingに切り替えます。
かなりイマイチな結果になりました。
Forward Renderingでは一つのオブジェクトに対して正しく影響させられるライトの数が少ないので、
この例の床のようにいろんな光源からの影響を受けるオブジェクトの色がおかしくなります。
このForward Renderingにおけるライトの取り扱い方は以下の記事にまとめていますので必要に応じて参照してください。
Deferred Shadingにはこのように、多数の光源の影響を正確に描画できるというメリットがあります。
メリット2 : G-Bufferをポストエフェクトで使える
二つ目のメリットとして、G-Bufferをポストエフェクトに使えるということがあります。
Forward Renderingではレンダリング結果以外をポストエフェクトに使おうとするとその情報を別途描画する必要がありますが、
Deferred Shadingでは法線などG-Bufferに書き込まれている情報をそのままポストエフェクトに使用できます。
これにより、例えばPost ProcessingではFXAAやSSRといったポストエフェクトが使えるようになります。
デメリット1 : 半透明のオブジェクトを処理できない
さてDeferred Shadingには多数の光源の影響を正確に描画できるという大きなメリットがある一方、いくつかのデメリットも存在します。
まず大きめのデメリットとして、半透明のオブジェクトを処理できないということがあります。
これはG-Bufferパスでジオメトリ情報を二次元のテクスチャに描き込んでしまうことによりオブジェクト同士の重なりの情報が失われるためです。
UnityではレンダリングパスをDeferred Shadingに設定していても、半透明描画だけはForward Renderingで描画されます。
この挙動を確認するために先ほどのシーンの手前に半透明のCubeを配置してみます。
これをFrame Debuggerで見ると以下のようになります。
不透明描画がDeferred Shadingによって行われ、半透明描画がForward Renderingによって行われていることが確認できました。
デメリット2 : MSAAが使えない
デメリットの二つ目として、MSAAが使えないということがあります。
レンダリングパスをDeferredに設定するとCameraコンポーネントにこの旨の警告が出ます。
MSAAについて詳しくは以下の記事にまとめていますので、必要に応じて参照してください。
上記のことから、Deferred Shadingでアンチエイリアスを使いたい場合にはポストエフェクトを使うことになりそうです。
Post-Processingでアンチエイリアスを扱う方法は以下の記事にまとめています。
デメリット3 : レンダラごとに影を受ける設定ができない
三つ目のデメリットとして、レンダラごとに影を受けるかどうかの設定ができないということがあります。
例えばMesh RendererのReceive Shadowsは、Deferred Shadingを有効にしていると非アクティブになります。
影響するライトが多いほど負荷がかかるので注意
Deferred Shadingを使うときのパフォーマンス上の注意点として、
ピクセルに影響するライトが多いほど処理負荷が大きくなるということがあります。
例えば、画面全体に影響するライトが多数存在していると処理負荷が大きくなってしまいます。
そのため、ディレクショナル以外のライトは影響する距離に特に気を配って設定するべきです。
モバイルでも使えるのか?
次にDeferred Shadingのモバイルにおけるハードウェア要件について考えます。
マニュアルによると、モバイルではOpenGL ES 3.0であれば動作するようです。
そしてこのOpenGL ES 3.0は大体のモバイル端末で動きます。
以上のことから、これからの時代は普通にモバイルでDeferred Shadingを使えるといってもいいのではないかと思います。
ライティング用のシェーダを差し替える
さてG-Buffersに書き込むためのシェーダはもちろん各レンダラのマテリアルに設定されています。
が、Lightingパスに使うためのシェーダはどこにあるのでしょうか?
これは、Project SettingsのGraphics設定下部のBuilt-in Shader Settingsから設定できます。
デフォルトではUnityが内部的に用意しているシェーダが使われています。
もし独自のライティング用シェーダを使いたければ、このDeferredをCustomにしてシェーダを設定します。
なお、Unityが用意しているビルトインシェーダはDownloadページからダウンロードできます。
ビルトインシェーダをダウンロードしたフォルダの中にあるInternal-DeferredShading.shaderという名前のものがそれです。