概述
在百度第一頁,關於這個問題基本上都來自於同一個文章,原文章地址我沒有找到,因此附上其中一篇轉載:在Unity 2019.2中擴展Shader Graph,實現自定義光照
然而和標題一樣,這個實現是基於2019.2版本的,移植到更新的版本時會出各種各樣的問題。
這篇博客記錄了我嘗試把主光源信息 (MainLight) 相關內容移植到目前 Unity 最新版本 (2020.2) 的 URP 渲染管線時遇到的問題與解決方案。
前置問題
這篇文章需要你至少懂得:
- 在 Unity 中制作基於文件的自定義節點 (Custom Node) 與子圖 (Sub Graph)。
同時如果你能夠有下面這些能力,能獲得更通順的閱讀體驗(當然沒有也行):
- 下載了 Unity 2021.2 版本
- 簡單接觸過 Unity 的 URP Shader 代碼編寫,並且使用過 Shader Graph。
- 懂得訪問不存在的網站,或者能夠登錄 Github。
解決方案
文章的解決方案基於 Github 上的項目 Shader Graph Custom Lighting Sample Project 實現,這個項目是基於 Unity 2019.2 的。
直接采用我在概述中提到的鏈接里的解決方案會報各種各樣的錯,甚至無法編譯成功(對百度指指點點)。
這個 Github 倉庫中有用的部分包括下面的路徑,倉庫中的其余部分是作者自己做的示例:
\Assets\Includes\Assets\Sub Graphs
這兩個文件夾下的文件提供了一些光照節點。其中 Includes 文件夾下的 .hlsl 文件是自定義節點的源文件。
直接移植功能基本正常,可能會出現一些小 bug,但只要它不出現,你就可以當它不存在。
這篇博客僅基於這個 Git 倉庫制作 Get Main Light 節點獲取主光源信息,解決了移植到 2021.2 版本時出現的無法接受來自主光源的陰影問題,如果其他版本也出現了這個問題,你也可以嘗試這個博客的解決方案。
同時,稍微修改了一下原倉庫中的節點設置,以提供更容易使用的精度 (Precision) 控制方法。
源文件 (.hlsl)
首先給出自定義主光源節點的源文件 MainLight.hlsl
#ifndef CUSTOM_LIGHTING_INCLUDED
#define CUSTOM_LIGHTING_INCLUDED
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _SHADOWS_SOFT
void MainLight_float(float3 WorldPos, out float3 Direction, out float3 Color, out float DistanceAtten, out float ShadowAtten)
{
#if SHADERGRAPH_PREVIEW
Direction = float3(-0.5, 0.5, 0);
Color = float3(1, 0.95, 0.8);
DistanceAtten = 1;
ShadowAtten = 1;
#else
#if SHADOWS_SCREEN
float4 clipPos = TransformWorldToHClip(WorldPos);
float4 shadowCoord = ComputeScreenPos(clipPos);
#else
float4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
#endif
Light mainLight = GetMainLight(shadowCoord);
Direction = mainLight.direction;
Color = mainLight.color;
DistanceAtten = mainLight.distanceAttenuation;
ShadowAtten = mainLight.shadowAttenuation;
#endif
}
void MainLight_half(float3 WorldPos, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten)
{
#if SHADERGRAPH_PREVIEW
Direction = half3(0.5, 0.5, 0);
Color = half3(1, 0.95, 0.8);
DistanceAtten = 1;
ShadowAtten = 1;
#else
#if SHADOWS_SCREEN
half4 clipPos = TransformWorldToHClip(WorldPos);
half4 shadowCoord = ComputeScreenPos(clipPos);
#else
half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
#endif
Light mainLight = GetMainLight(shadowCoord);
Direction = mainLight.direction;
Color = mainLight.color;
DistanceAtten = mainLight.distanceAttenuation;
ShadowAtten = mainLight.shadowAttenuation;
#endif
}
#endif
這個文件刪除了原倉庫CustomLighting.hlsl中的其余函數,僅保留了 MainLight_float 和 MainLight_half 用於不同精度的運算。
同時,在文件頭部增加了三個 pragma 用來修復陰影問題。(修復之前的 ShadowAtten 沒有辦法正常使用,但其余 Out 參數正常)
其中,第一個 _MAIN_LIGHT_SHADOWS 是產生陰影是才會用到的參數,實際上並不需要(這里我們其實只需要接受陰影),因此刪掉也完全OK。
第二個參數 _MAIN_LIGHT_SHADOWS_CASCADE 是關鍵的參數,用於接受陰影,不能缺少,缺少后就會出現 ShadowAtten 失效的問題。
第三個參數 _SHADOWS_SOFT 可以讓陰影邊緣柔化,如果注釋掉可以得到硬邊緣的陰影,可以根據需要采用或者刪除。
增加的三個 pragma :
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _SHADOWS_SOFT
最后,修改了這兩個函數在 Shader Graph 節點預覽中的初始設置,使得修改是否發揮了作用更容易被檢測到。
具體來說,我使不同精度下的光照方向不同,從而使切換精度時節點預覽中的光照方向發生改變。另外,使光線不是純白色而是微微泛黃(通過修改Color實現),讓光照顏色加入運算時會對預覽圖產生影響。
對於 float 精度:
#if SHADERGRAPH_PREVIEW
Direction = float3(-0.5, 0.5, 0);
Color = float3(1, 0.95, 0.8);
DistanceAtten = 1;
ShadowAtten = 1;
#else
對於 half 精度:
#if SHADERGRAPH_PREVIEW
Direction = half3(0.5, 0.5, 0);
Color = half3(1, 0.95, 0.8);
DistanceAtten = 1;
ShadowAtten = 1;
#else
子圖 (SubGraph)
子圖保持與倉庫相同的名字Get Main Light,在節點連線上與原倉庫相同:
將 MainLight (Custom Fuction) 節點的精度 Precision 設置為 Use Graph Precision :
再在這個子圖的 Graph Settings 中將 Precision 設置為 Switchable ,這樣就可以在使用子圖的時候直接在 Shader Graph 中更換精度了。
需要更換精度時,只需要在使用了這個子圖的地方設置子圖的節點屬性中的精度 Precision 即可:
按照前文的設置,我們在更換精度時,節點預覽界面中的光照方向應該會發生改變,從而讓我們確信調用的函數確實發生了變化。
下載鏈接
鏈接:https://pan.baidu.com/s/1PS94YZHFGnF8GuCqY8GZGQ
提取碼:rfun
文件包含一個 .hlsl 源文件,以及一個 Sub Graph,鏈接掛掉的話請評論或私信告知。
