【Unity】为ShaderGraph制作自定义Main Light节点
概述
在百度第一页,关于这个问题基本上都来自于同一个文章,原文章地址我没有找到,因此附上其中一篇转载:在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,链接挂掉的话请评论或私信告知。