在Unity里抄UE的作业

Last modified date

把UE的shading model搬运到Unity里,附赠源码包.zip

UE的渲染框架确实是个PBR的标杆,能把各种渲染feature合理的整合在一起,并且有自己独特的风格(有人说是油腻的风格)。所以很多写实风格项目会以UE的渲染框架为基础。我在以往的公司里干了几次这种“抄UE作业”的工作了,后来就索性花了一点时间整理了一下,把核心的部分代码都拎出来,以后要抄到其他引擎也更方便。

UE Shading Model简介

UE大部分Shading Model的公式都是:

直接光Diffuse + 直接光Specular + 环境光Diffuse + 环境光Specular + 透射散射

而少数特殊的Shading Model要特殊处理,例如头发的Marschner模型。

PBR核心思想是能量守恒,抄作业的时候要注意光照计算要同时考虑直接光和环境光。例如Anisotropy有独特的高光计算函数的,也要用在环境光计算部分。又例如ClearCoat这种需要计算两次光照(表面和内层)的,也同样要在环境光的部分计算两次。具体要去自己看代码部分,抄作业的时候很容易抄漏了。

Anisotropy只是在DefaultLit的基础上改变了Specular的计算,所以Anisotropy可以作为开关放进DefaultLit。

TwoSidedFoliage,Subsurface,SubsurfaceProfile和PreintegratedSkin虽然都用了SubsurfaceColor,但功能各不相同。

  • TwoSidedFoliage模拟薄片的树叶的投射,是在DefaultLit的基础上加了直接光的背光效果
  • Subsurface模拟有厚度的物体的散射效果
  • SubsurfaceProfile提供了更高级的设置,用SubsurfaceProfile资产来配置散射的效果
  • PreintegratedSkin是专门为皮肤定制的,模拟了亮暗边缘的散射效果,颜色来源于一张预积分Lut图。

Hair比较特殊,UE提供了Kajiya-Kay和Marschner方案,既然抄UE我就用了更高级的Marschner方案。

Cloth模拟绒布效果,高光分布在fresnel边缘,且有色散一样的独特高光颜色。

项目文件

下载链接:WFLighting.zip 提取码: skkh

附件中是抄进Unity的方案,已经在Unity6.2版本测试过。

为了方便Shader开发,使用了Amplify shader editor的模板。需要你再装个ASE插件。用ASE是因为Unity的shader graph功能太少无法定制。下载后放在Unity工程的Asset根目录下。

Shading model的主体结构都依照了UE的格式,大部分函数名称都可以在UE源码里找到。部分强依赖于引擎管线的注入信息套用了URP的格式。

灯光数据沿用了Unity的格式。排除了面光源计算。有些函数的分支也没有完全抄过来。

我只做了Forward部分,Deferred也是可以做的,可以在WF_GBuffer.hlsl里整合一下,但改Deferred管线比较麻烦。Unity6新出的Deferred+还没时间看,不知道现在改起来是不是更方便一点。

总之这只是一个基础学习框架。

材质测试场景:Assets/WFLighting/Example/Scene/Demo.unity

Shader graph范例:Assets/WFLighting/Example/MatTest

Shader文件目录:Assets/WFLighting/ShadingModel

  • WF_Lit_ASETemplate.shader——ASE的模板
  • WF_ShadingModel.hlsl——各种ShadingModel的入口
  • 其他include文件大部分是抄UE的内容,少数是兼容Unity的函数,例如反射,GI的来源都是使用Unity的方案。这么做是为了尽量兼容Unity,能跟着官方版本升级。

脚本:Assets/WFLighting/Script

  • PreintegratedGF.cs——生成PreintegratedGF和PreintegratedSkinBRDF,传递SubsurfaceProfile信息。
  • WF_ShadingModelProfile——SubsurfaceProfile的配置文件,可以从Create>Scriptable objects>WF_ShadingModelProfile创建,并且填入PreintegratedGF.cs中(我挂在camera上了)。这里我做的比较粗糙,简化为直接输入信息。UE的SubsurfaceProfile模型还要搭配后处理生成更多的信息。

创建新材质的方式就是创建一个ASE的graph,选择模板为WalkingFat/Lit,再选择Lighting Model。我给大部分模型都加入了Has Anisotropy开关,除了几个没法加的。

验证Unity内的效果跟UE是否一致

首先我们抄的仅仅是ShadingModel部分,所以我们只看直接光和环境光的diffuse和specular即可。在UE里可以在视图里关闭所有后处理,然后切换显示直接光和环境光的的diffuse和specular。而unity端可以直接在shader里输出这些部分来对照。然后可以先在UE和Unity里搭建同样的场景环境和灯光参数,camera也摆成一样的角度。这样就可以进行像素级别的对照了。

遇到差异的时候,最有效的方法是先比对Gbuffer信息是否一致,然后再逐个查看直接光/环境光的diffuse和specular。有时候差异可能来自于UE和Unity的管线里注入的数据来源不一致,这时候就截帧吧。例如我之前发现环境光的部分总有一点差异,截帧后发现是两者的反射捕获确实有一点点差异。

这种随着Roughness变化细微的差别原因是:

  • Unity用反射球的mip当作反射信息随着Roughness变模糊的来源,而UE是用模糊采样,更加精确。
  • 也是基于上述原因,Unity计算出的反射信息在高Roughness的情况下,会使得亮度信息分布更加均匀,说人话就是环境光的diffuse在高Roughness部分会偏亮一点。

说白了就是Unity Low一点,但性能也更好是吧。如果要做的跟UE一毛一样,不如直接用UE啦。

UE和Unity效果参考:

无论如何,Unity还是比不上UE的效果的。做这个工作的意义,是给中低端设备提供了一种性价比方案。平替!平替而已!

以上内容整理的比较糙,很多细节不太可能做的跟UE一样。而且很多细节我也没有完善,只是当作学习参考而已。