此篇为工作期间的初期工作,很多较为稀碎的优化相关的不会在此涉及,本篇只涉及部分较为通用的流程(部门之后应该会发知乎专栏,但还是在此做个备份)。不过之后或许会借此机会谈些细节的东西,比如后处理Pixel Shader和全屏Compute Shader的性能比较(还有许多相关的可以参考Nv在GDC上的论文)、函数拟合代替贴图、半影mask生成的流程(特定的工程解)。
阴影是画面表现光影的重要元素之一,高质量的阴影会对游戏画面产生不小的贡献。对于大世界来说,高质量的阴影往往意味着低走样和较远的绘制距离。Unity的URP管线中自带了最大级联数为4级的级联阴影,然而4级的级联数在阴影绘制距离较大时,近处的阴影会出现难以接受的锯齿,出现较大的走样。解决走样主要有两个方向,提高采样频率和降低信号频率。在本篇文章中,我们选择了一个较为通用的解决方案。对于前者,我们选择提高级联数,拓展4级到8级,对于后者,我们则选择在采样前进行PCF滤波。同时,也在PCF的基础上做了PCSS,以及通过PCF模拟的软阴影搭配Ramp图实现随TOD变化的阴影着色。
下图采样为8SPP(采样参考使命召唤)、搭配联合双边滤波和Temporal filter。
本篇基于Unity 2021.3.5,URP 12.1.7。
1. 编辑器拓展
在开始对管线进行拓展之前,我们需要大致了解下URP管线的处理流程。URP首先会对UniversalRendererData、UniversalRenderPipelineAsset进行序列化。其中,UniversalRendererData会进行一些资源的加载(Shader、Texture等)和RenderFeature的配置。UniversalRenderPipelineAsset则会配置一系列UniversalRendererData,并设置一些UniversalRenderPipeline所需要的数据。UniversalRenderPipeline继承自RenderPipeline,是组织整体管线逻辑的地方,它会使用UniversalRenderPipelineAsset初始化管线。在初始化CameraData的时候,通过UniversalRendererData创建UniversalRenderer,之后在RenderSingleCamera函数中进行UniversalRenderer的设置(主要是设置一系列将被执行的Pass,如MainLightShadowCasterPass),并将对应Pass添加至管线中。最后,在UniversalRenderPipeline的Render函数中调用相关函数进行渲染。
了解完这些后,我们需要修改的内容就很明确了。首先,我们需要在UniversalRenderPipelineAsset下为8级级联添加相关的变量,并在其Editor中添加对应的UI。URP中,级联阴影相关的数据会由ShadowData的结构体进行组织,因此我们需要修改该结构体(处于UniversalRenderPipelineCore,该文件包含一些与管线相关的结构体、ShaderPropertyId、Keyword,以及一系列对管线数据做处理的函数),并在UniversalRenderPipeline通过UniversalRenderPipelineAsset对ShadowData进行初始化时,添加级联阴影相关的数据。修改完后,我们就可以在主光源阴影的Pass中获取级联阴影的相关数据。