知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UnityShader】Deferred Shading #114

はじめに

Deffered Shadingについて勉強していきます。

Forward Rendering

各オブジェクトを1つ、または複数のパスで描画するものになります。
パスの数は、オブジェクトに作用するライトによって決まります。

docs.unity3d.com

ライティング

Forward Rendertingには以下の3つのライティングが存在します。

ライティング名 意味
ピクセルライティング(Per-pixel) ピクセルごとに計算される高価なもの
頂点ライティング(Per-vertex) 頂点ごとに計算される安価なもの
球面調和ライティング(SH) 頂点で複数のライトの球面調和による計算コストが実質0だが、あくまで近似なので精度は低い

これらをUnityが自動で割り振ってくれます。

パス

パスは以下の2種類があります。

パス名 意味
ベースパス 1つのディレクショナルライトと全ての頂点ライティング、球面調和ライティングでオブジェクトをレンダリングする
加算パス 追加されたピクセルライティングでオブジェクトをレンダリングする

問題点

ディレクショナルライト以外のピクセルライトは、加算パスでレンダリングされます。
これはひとつのライトごとにひとつのパスを使用するものになります。
つまり、1つのオブジェクトに対して複数のライトが当たる場面等で非常に負荷がかかってしまいます。

また、ピクセルライティングから頂点や球面調和ライティングに変更させても、これらは精度が低いものとなるので、不自然な描画結果になってしまう可能性があります。

Differd Rendering

こちらは2パスで描画する手法になります。

docs.unity3d.com

パス

https://docs.unity3d.com/550/Documentation/uploads/SL/CameraRenderFlowCmdBuffers.svg

パスは以下の2種類があります。

パス名 意味
G-Bufferパス 各オブジェクトを1回レンダリングするもの
描画に必要なデータをG-Bufferとしてテクスチャに書き込む
ライティングパス G-Bufferと深度に基づいてライティングを計算するパス

G-Buffer

G-Bufferはテクスチャとして各データを保持しているものになります。
1パス目で、描画に必要なデータをG-Bufferに書き込きます。
2パス目以降で、このテクスチャを使用して描画していきます。
これらのテクスチャはグローバルシェーダーのプロパティとして、設定されます。
詳しくは以下となります。

プロパティ名 意味 備考
_CameraGBufferTexture0 Diffuse Color(RBG), occlusion(A)
_CameraGBufferTexture1 Specular Color(RBG), roughness(A) roughness = 1.0 - smoothness
_CameraGBufferTexture2 法線ベクトル(RBG), 未使用(A)
_CameraGBufferTexture3 Emission + lighting + lightmaps + reflection probes カメラがHDRレンダリングを使用している場合、Emission + lightingのRenderTargetsは生成されない
代わりに、カメラがレンダリングするターゲットがRT3として使用される
_CameraGBufferTexture4 Light occlusion(RGBA) ShadowmaskかDistanceShadowmaskのライトモードを使用している場合に使える
_CameraDepthTexture Depth + Stencil

メリット

ライティングを2パス目でまとめて行うため、ライトを増やしても負荷がかかりにくいことが挙げられます。
また、オブジェクトに影響を与えられるライトの数に制限が無く、ピクセル毎に評価冴えるため正しく描画されます。

デメリット

半透明のオブジェクトを正しく描画できない

これはG-Bufferパスで情報を2次元のテクスチャに書き込んでしまうからになります。
このパス段階ではブレンドができず、G-Bufferには1ピクセルにつき1つのデータしか保存できないため不透明と半透明の情報を同時に保持できません。
これにより、オブジェクト同士の前後関係が無くなってしまい、半透明が正しく描画されなくなってしまいます。

MSAAが使えない

G-Bufferを全てMSAA用に変更する必要があります。
また、各サブピクセルでライティングを行う必要があり結果的にSSAAと負荷がそこまで変わらなくなってしまうからになります。

MSAA

1ピクセルをより小さなサブピクセルと呼ばれるもの二分割します。
この分割数はMSAAの手前に付いている値に応じて変化し、4xなら4分割、8xなら8分割になります。
このサブピクセルの深度を求め、深度の差が大きい箇所をエッジとして色を求める手法になります。

実装

適当にScene上にオブジェクトを配置します。
CameraのRendering PathをDeferredに設定して終了になります。

f:id:soramamenatan:20220213164106p:plain

結果

フレームデバッグ上に各Textureが出ています。

f:id:soramamenatan:20220213164121p:plain

CameraDepthTexture

f:id:soramamenatan:20220213164438p:plain

ShadowMapTexture

f:id:soramamenatan:20220213164455p:plain

GBufferTexture0

f:id:soramamenatan:20220213164510p:plain

GBufferTexture1

f:id:soramamenatan:20220213164520p:plain

GBufferTexture2

f:id:soramamenatan:20220213164530p:plain

参考サイト様

https://esprog.hatenablog.com/entry/2016/03/17/000143

https://light11.hatenadiary.com/entry/2019/09/05/212345