しゅみぷろ

プログラミングとか

Deferred Shadingについてまとめてみた

Unity5からの(Legacyでない)DeferredShadingについて自分なりにまとめてみます。

docs.unity3d.com

f:id:es_program:20160426003316p:plain

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の生成を行うシェーダー自体を入れ替えたい場合は以下の項目を変更することで可能です。

f:id:es_program:20160426003332p:plain

この際、シェーダーではPass内のTagsに

Tags{"LightMode"="Deferred"}

を記述します。

また、G-Bufferの生成にはMRT(Multiple Render Target)が利用されています。 そのため、DeferredShadingではShader Model 3.0(もしくはそれ以降)を要求されます。

G-Bufferを見てみる

シェーダーの基本については以下を参照してください。

 

esprog.hatenablog.com


DeferredShadingによって生成されたG-Bufferを見ていきます。

sssiii.seesaa.net

こちらを参考にさせていただきました。一枚一枚の画像の上半分がForwardRenderingで描画したものです。


f:id:es_program:20160426003416p:plain

Deferred Shading

f:id:es_program:20160426003430p:plain

Diffuse colors

f:id:es_program:20160426003453p:plain

Specular color

f:id:es_program:20160426003506p:plain

World space normal

f:id:es_program:20160426003522p:plain

Emission + Ambient + Reflections + Lightmaps

f:id:es_program:20160426003542p:plain

Depth + Stencil

コードは以下。

G-Bufferを見てみる

DeferredShadingで生成されたG-BufferをOpaqueImageEffectsフェーズで描画しています。

おまけ

DepthテクスチャはカメラのClipping Planesの範囲を0~1として、その範囲でどのくらい深度値が異なるかで色付けされます。先ほど紹介した例では

  • Near = 0.3
  • Far = 1000

でしたので、くっきりと白黒の画像が描画されてしまいました。これを

  • Near = 0.3
  • Far = 5

とした時の出力は以下になります。

f:id:es_program:20160426003604p:plain

Depth + Stencil (Adjusted camera setting)

また、UnityDeferredLibrary.cgincを見てみたところ

#if defined (SHADOWS_SCREEN)
sampler2D _ShadowMapTexture;
#endif

なる表記がありました。ここからShadowMapを取ってくることができます。

f:id:es_program:20160426003622p:plain

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
        }
    }
}

以下にプロジェクトのサンプルをあげておきますので、必要でしたら見てみてください。

実行中にマテリアルを弄ることで絵が切り替わるはずです。

github.com