はじめに
URP向けのシェーダーを書くとき、ライト計算を自前で実装したくなることがあります。
今回は、URPのライティング計算はどこに実装されているのかを調べてみました。
ちなみに、URPのサンプルシーンのオブジェクトにはLit.shaderが使用されており、PBRのライティングが実装されています。
環境
Universal RP 8.2.0
Unity 2020.1.2f1
リアルタイム ライト
Lit.shaderのPBRリアルタイムライト計算は、Lighting.hlsl の UniversalFragmentPBR
メソッドに実装されています。
///////////////////////////////////////////////////////////////////////////////
// Fragment Functions //
// Used by ShaderGraph and others builtin renderers //
///////////////////////////////////////////////////////////////////////////////
half4 UniversalFragmentPBR(InputData inputData, half3 albedo, half metallic, half3 specular,
half smoothness, half occlusion, half3 emission, half alpha)
{
BRDFData brdfData;
InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData);
Light mainLight = GetMainLight(inputData.shadowCoord);
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0));
half3 color = GlobalIllumination(brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS);
color += LightingPhysicallyBased(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS);
# ifdef _ADDITIONAL_LIGHTS
uint pixelLightCount = GetAdditionalLightsCount();
for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex)
{
Light light = GetAdditionalLight(lightIndex, inputData.positionWS);
color += LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS);
}
# endif
# ifdef _ADDITIONAL_LIGHTS_VERTEX
color += inputData.vertexLighting * brdfData.diffuse;
# endif
color += emission;
return half4(color, alpha);
}
ライトの検証
ライトの挙動を検証するため、以下のようなシーンを用意してみました
・Directional Light を配置して、影を出す
、Point Light を1つ配置
・すべてのオブジェクトにはLit.shaderをアタッチ
Main Light
Main Light は以下のように実装されています。(Lighting.hlsl)
Light mainLight = GetMainLight(inputData.shadowCoord);
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0));
half3 color = GlobalIllumination(brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS);
color += LightingPhysicallyBased(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS);
比較
LightingPhysicallyBased()
の呼び出しをコメントアウトして、画面がどのように変化するのかを見ていきます。
変更前
変更後
DirectionalLightによる光が消え、画面全体が暗くなりました。
Main Lightの落ち影を消す
LitForwardPass.hlsl を見ると、シャドウマップ用のテクスチャ座標(shadowCoord
)の計算処理があります。
# if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
inputData.shadowCoord = input.shadowCoord;
# elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
inputData.shadowCoord = TransformWorldToShadowCoord(inputData.positionWS);
# else
inputData.shadowCoord = float4(0, 0, 0, 0);
# endif
このshadowCoord
は Main Light の影に利用されます。
Light GetMainLight(float4 shadowCoord)
{
Light light = GetMainLight();
light.shadowAttenuation = MainLightRealtimeShadow(shadowCoord);
return light;
}
比較
inputData.shadowCoord
の計算を削除したときの画面の変化を見ていきます。
変更前
変更後
落ち影が無くなりました。
Additional Light
ポイントライトなどの追加ライトは以下のコードで実装されています。(Lighting.hlsl)
# ifdef _ADDITIONAL_LIGHTS
uint pixelLightCount = GetAdditionalLightsCount();
for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex)
{
Light light = GetAdditionalLight(lightIndex, inputData.positionWS);
color += LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS);
}
# endif
比較
LightingPhysicallyBased()
の呼び出しをコメントアウトしたとき、画面がどのように変化するのかを見ていきます。
変更前
変更後
ポイントライトが照らされている部分が暗くなりました。
Baked GI (ライトマップ)
ライトのベイク情報の取得は、LitForwardPass.hlsl に実装されています。
実装コードは以下になります。
inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);
比較
SAMPLE_GI()
マクロの呼び出しを削除したときに画面の変化を見ていきます。
変更前
変更後
SAMPLE_GI()の呼び出しを削除すると、ライトがベイクされていない状態と同じ見た目になります。
参考
Unity URP のライティング
https://note.com/npaka/n/n22986e11615e
Unity Learning Materials - ライティングのセッション
https://learning.unity3d.jp/tag/%E3%83%A9%E3%82%A4%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0/