Deferred Shadingについてまとめてみた
Unity5からの(Legacyでない)DeferredShadingについて自分なりにまとめてみます。
Deferred レンダリングは、Orthographic プロジェクションを使用している場合はサポートされません。カメラのプロジェクション モードが Orthographic に設定されている場合は、その値は上書きされカメラは常に Forward rendering を使用します。
DeferredShadingで実行されるパス
DeferredShadingでは以下2つのパスが実行されます。
- G-Bufferパス
- G-Bufferの作成
- Lightingパス
- Emissionバッファにライティングを反映させる
それぞれのパスが実行されることにより、G-Bufferが生成されます。 以後のレンダリングではこのG-Bufferを用いてポストプロセス的に描画が可能です。
G-Buffer
G-Bufferはテクスチャとして格納されるデータ(Geometry Buffer)のことです。 DeferredShadingは「遅延シェーディング」の名の通り、実際の描画は遅延されます。
つまり、1パス目のG-Bufferパスで今後必要になるデータをいくつかのテクスチャバッファに書き込みます。 2パス目以降では、このG-Bufferを用いてレンダリングしていきます。 実際のレンダリングが2パス目以降に遅延されることが名前の由来です。
UnityがデフォルトのG-Bufferパスで生成してくれるデータ群は以下になります。
- Diffuse colors
- Specular color
- World space normal
- Emission + (Ambient + Reflections + Lightmaps)
- Depth + Stencil
(Ambient + Reflections + Lightmaps)はLightingパスでEmissionに加算される情報です。EmissionバッファはG-Bufferパスで生成されます。
これらの情報にはシェーダーから
- _CameraGBufferTexture0: RGB 拡散反射色、Aは未使用
- _CameraGBufferTexture1: RGB 鏡面反射色、Aは粗さ
- _CameraGBufferTexture2: RGB 法線ベクトル(0~1)、Aは未使用
- _CameraGBufferTexture3: Emission + Ambient + Reflections + Lightmaps の情報
- _CameraDepthTexture: R 深度、それ以外は未使用(?)
でアクセスすることが可能です。 ただし、カメラはデフォルトの設定で深度テクスチャを生成してくれません。もし_CameraDepthTextureを使用する場合はスクリプトから
_camera.depthTextureMode |= DepthTextureMode.Depth;
のように、カメラのdepthTextureModeを明示的に指定してあげる必要があります。
また、G-Bufferの生成を行うシェーダー自体を入れ替えたい場合は以下の項目を変更することで可能です。
この際、シェーダーではPass内のTagsに
Tags{"LightMode"="Deferred"}
を記述します。
また、G-Bufferの生成にはMRT(Multiple Render Target)が利用されています。 そのため、DeferredShadingではShader Model 3.0(もしくはそれ以降)を要求されます。
G-Bufferを見てみる
シェーダーの基本については以下を参照してください。
DeferredShadingによって生成されたG-Bufferを見ていきます。
こちらを参考にさせていただきました。一枚一枚の画像の上半分がForwardRenderingで描画したものです。
コードは以下。
DeferredShadingで生成されたG-BufferをOpaqueImageEffectsフェーズで描画しています。
おまけ
DepthテクスチャはカメラのClipping Planesの範囲を0~1として、その範囲でどのくらい深度値が異なるかで色付けされます。先ほど紹介した例では
- Near = 0.3
- Far = 1000
でしたので、くっきりと白黒の画像が描画されてしまいました。これを
- Near = 0.3
- Far = 5
とした時の出力は以下になります。
また、UnityDeferredLibrary.cgincを見てみたところ
#if defined (SHADOWS_SCREEN) sampler2D _ShadowMapTexture; #endif
なる表記がありました。ここからShadowMapを取ってくることができます。
先ほどのシェーダーコードを以下のように手直ししました。
Shader "DeferedShadingTest/ShowGBuffer" { Properties { [KeywordEnum(DIFF, SPEC, NORM, LIGHT, DEPTH, SHADOW_MAP)] _GB("G-Buffer Selecter", Float) = 0 } SubShader { Cull Off ZWrite Off ZTest Always Pass { CGPROGRAM #pragma vertex vert_img #pragma fragment frag #pragma shader_feature _GB_DIFF _GB_SPEC _GB_NORM _GB_LIGHT _GB_DEPTH _GB_SHADOW_MAP #define SHADOWS_SCREEN #include "UnityCG.cginc" #include "GBufferVariant.cginc" #include "UnityDeferredLibrary.cginc" fixed4 frag (v2f_img i) : SV_Target { #ifdef _GB_DIFF return tex2D(_CameraGBufferTexture0, i.uv); #elif _GB_SPEC return tex2D(_CameraGBufferTexture1, i.uv); #elif _GB_NORM return tex2D(_CameraGBufferTexture2, i.uv); #elif _GB_LIGHT return tex2D(_CameraGBufferTexture3, i.uv); #elif _GB_DEPTH return Linear01Depth(tex2D(_CameraDepthTexture, i.uv).r); #else return tex2D(_ShadowMapTexture, i.uv); #endif } ENDCG } } }
以下にプロジェクトのサンプルをあげておきますので、必要でしたら見てみてください。
実行中にマテリアルを弄ることで絵が切り替わるはずです。