欢迎参与讨论,转载请注明出处。
Demo也到了做渲染的时候了,经过一番鏖战后,算是大体完成了。但随着后续需求的到来,发现这套纯代码的Shader方案对于扩展、复用等方面有着诸多不便。于是便打起了Shader Graph的主意……经过一番纠缠,于是有了本篇踩坑实录。另附源码地址,但本篇并不会对其做讲解。基于Unity2020.1.0a20,渲染管线为URP7.18。
首先要明确的是:Shader Graph不支持Builtin渲染管线,且尚未处于彻底成熟的阶段,哪怕是最新版本尚有不少缺陷。但瑕不掩瑜,且说其优劣:
其实一般的浅度Shader Graph使用者并不会如我这般踩这么多坑:直接使用内置的Graph模板进行创作即可。不幸的是,如前文所言:对于创建Pass并不友好,需要修改源码。于是便开始了踩坑之旅……
在默认情况下,Unity的Package将保存在工程的Library/PackageCache目录下,这样子是不能直接修改源码的(Library目录下的东西属于可再生物,随时会被覆盖),需将之搬迁至工程的Packages目录下。
对于考虑到日后Shader Graph的版本升级情况,所以尽可能的不要修改原工程的内容,而是尽量新建文件。但考虑到Shader Graph源码下存在不少inner元素,直接在外面写自己的内容也并非彻底可行。只能直接在Shader Graph包下进行添加文件的方式了,这也是要将之移至Packages目录的原因。
我要做的事情相当明确:新建一个自定义的Graph类型。在URP下已经自带Unlit与PBR两种类型了,于是本人便基于Unlit Graph并结合先前实现的Shader的特性进行新类型的创作。
我们能接触到Unlit Graph创建的起点便是Project区下右键菜单的Create->Shader->Unlit Graph
了,直接在Shader Graph源码包下全局搜索Create/Shader/Unlit Graph
即可找到:
照葫芦画瓢在同目录下弄个新文件实现相同功能即可,这下我们便知道Unlit Graph的正主了:UnlitMasterNode
。经过研究发现,它决定了在编辑器下Unlit Master Node的样式:
但这只是个壳子罢了,根据代码中的IUnlitSubShader
为引,找到了其核心:
这个UniversalUnlitSubShader
的作用相当简单:根据编辑器的设置生成Shader代码。如上图便可看出定义Pass数据结构的行为,这也是诱使我来改源码的直接原因。在里面你将见到形如这般的代码:
var unlitMasterNode = masterNode as UnlitMasterNode;
var subShader = new ShaderGenerator();
subShader.AddShaderChunk("SubShader", true);
subShader.AddShaderChunk("{", true);
subShader.Indent();
{
var surfaceTags = ShaderGenerator.BuildMaterialTags(unlitMasterNode.surfaceType);
var tagsBuilder = new ShaderStringBuilder(0);
surfaceTags.GetTags(tagsBuilder, "UniversalPipeline");
subShader.AddShaderChunk(tagsBuilder.ToString());
GenerateShaderPass(unlitMasterNode, m_UnlitPass, mode, subShader, sourceAssetDependencyPaths);
GenerateShaderPass(unlitMasterNode, m_ShadowCasterPass, mode, subShader, sourceAssetDependencyPaths);
GenerateShaderPass(unlitMasterNode, m_DepthOnlyPass, mode, subShader, sourceAssetDependencyPaths);
}
subShader.Deindent();
subShader.AddShaderChunk("}", true);
如此情况便变得相当清晰了,只要清楚你想生成怎样的Shader代码,在摸熟了生成代码的API,便可自由地进行创作了。通过右键节点可以随时查看生成的代码情况:
也许看似还算简单,但其中坑点还是有不少的:
Preference->Shader Graph
进行修改上限。值得一提的是,全局Keyword与局部Keyword似乎是分别对待的。#if SHADERGRAPH_PREVIEW
分支判定以处理在编辑模式下的情况: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 = 1;
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
}
现在感觉游戏开发的未来方向就是连连看了,从这点来说UE4的确算是时代前沿。在编辑器里加入逻辑控制元素,让更多人能加入创作,尽可能地解放生产力,的确是游戏开发所需要的。