URP源码学习(三)渲染管线的默认实现,forward
https://zhuanlan.zhihu.com/p/157473939
整体理解
这部分算是URP的核心了,可编程管线,说的就是这个,有能力的项目,可以根据需要,做出更适合项目的管线。
unity提供了两个默认实现,一个是forward,一个2D,2D有时间再细看(原谅我的懒),forward这部分还是有些东西可看的,因为默认管线,这部分是完全看不到的,只能通过framedebugger看个流程,现在有机会看看内部实现,对理解管线也是很有好处的。
核心部分,pass
ForwardRenderer可以简单理解成驱动各个pass执行的一个管理者,pass则实现了具体的渲染逻辑。
pass的功能,分为两部分,配置rt和执行最终渲染。
pass-事件
基类文件中定义了一系列的渲染事件,RenderPassEvent。每个pass,在初始化的时候,都定义了一个event,这个event用于pass的排序。id之间间隔50,可加offet以添加额外的event。
pass-配置rt
pass在渲染前需要先配置渲染目标,renderer基类调用pass的Configure抽象函数。
renderPass.Configure(cmd, cameraData.cameraTargetDescriptor);
每个pass实现具体逻辑,pass子类调用基类的ConfigureTarget方法,配置渲染目标和clear方法,子类没实现则渲染到相机的目标。
渲染目标分两个,color和depth,depth只有一个,color是个数组,默认第一个是相机目标,最大值在SystemInfo.supportedRenderTargetCount定义。
注意这个步骤只是设置了pass内部的数据,并没有真的通知到管线。
RenderTargetIdentifier[] m_ColorAttachments = new RenderTargetIdentifier[]{BuiltinRenderTextureType.CameraTarget};
RenderTargetIdentifier m_DepthAttachment = BuiltinRenderTextureType.CameraTarget;
真正设置渲染目标,是通过CommandBuffer的SetRenderTarget方法,URP在CoreUtils类封装了一个静态函数SetRenderTarget。ScriptableRenderer类在ExecuteRenderPass方法中,先调用pass的Config函数,然后取pass的color和depth数据,设置为真正的渲染目标。
梳理一下这个流程
forward逻辑
forward-初始化
做了以下几件事
- 创建几个特殊材质,用的是配置里的shader,这几个材质会传给对应的pass。
Material blitMaterial = CoreUtils.CreateEngineMaterial(data.shaders.blitPS);
Material copyDepthMaterial = CoreUtils.CreateEngineMaterial(data.shaders.copyDepthPS);
Material samplingMaterial = CoreUtils.CreateEngineMaterial(data.shaders.samplingPS);
Material screenspaceShadowsMaterial = CoreUtils.CreateEngineMaterial(data.shaders.screenSpaceShadowPS);
- 设置模板测试StencilState结构体。
- 创建用到的每个pass,指定渲染RenderPassEvent。
- 设置各个rt
- 创建ForwardLights实例,用于光源的相关计算。
- 创建RenderingFeatures,这个类里只有cameraStacking一个bool值,像是个没开发完的功能。
forward-Setup
简单说这个函数的作用就是把一个个pass加到个list里,供后边执行每个pass。
对于只渲染深度的相机,只需要添加3个pass,opaque、skybox、transparent。
一些重要的判断
- 后处理是否开启,URP把后处理分成了两步,一个是实现常规特效的后处理,一个是抗锯齿这种,具体逻辑在PostProcessPass内部区分。
- 是否需要深度图。首先判断相机配置,然后区分scene和game相机,scene相机之外,检测一下是否可以从opaque pass拷贝过来,以提升性能。具体判断在CanCopyDepth函数。
对一些特殊pass的说明,按代码顺序,没有特殊操作的略过
- DepthOnlyPass
- 开启条件:scene相机一定开启。game相机首先读取管线配置,同时CanCopyDepth为false,也就是说要注意本来不想开depth,但是开了抗锯齿等后处理效果,depth也会开启。
- 用法:shader要有DepthOnly pass,渲染所有的DepthOnly Pass到指定texture,shader中通过_CameraDepthTexture获取。
- CopyColorPass
- 复制指定颜色buffer到目标颜色buffer,可以复制不透明物的渲染结果,用于扭曲特效。
- 降采样可作为优化。
- shader中通过_CameraOpaqueTexture获取。
- CopyDepth和DepthOnly是互斥的,只需要用到一个,shader都是从_CameraDepthTexture获取。
其他pass还有很多 ,以后看shader的时候再一起细看。
ScriptableRenderer-Execute
unity把这部分放到了渲染管线的基类实现,也就是这部分被定义为通用的框架层,不建议项目扩展。
执行流程
- 获取相机数据,关闭shader关键字,执行一次clear操作
SetCameraRenderState(cmd, ref cameraData); context.ExecuteCommandBuffer(cmd); cmd.Clear();
- 对pass排序,按之前定义的RenderPassEvent。
- 设置shader定义的时间变量,SetShaderTimeValues函数实现。
- 将pass按event顺序,分成四块
- BeforeRendering:用于处理阴影等,不是实际的渲染,只作为功能使用
- MainRenderingOpaque:渲染不透明物体
- MainRenderingTransparent:透明物体
- AfterRendering:在后处理之后,在unity的demo中没看到具体渲染了啥,可能是扩展用的吧。
- 执行SetupLights,设置光照需要的一系列参数,细节在光照部分细说。
- 然后先执行BeforeRendering
- 接着是一个循环,用于处理VR,由于手游中只需要渲染一次,VR部分先不管,只按一个去看。
- 首先设置好相机属性,执行一次commandbuffer的clear指令。
- 然后是不透明物体渲染,depth也在这执行。
- 渲染透明物体,要注意的是透明和不透明物体的渲染,都是DrawObjectsPass实现的,参数不同,设置了RenderQueueRange和layer。
- 渲染AfterRendering
forward大致流程大概就这些,写了好久,大量的细节都没细写,准备放到具体的pass里,结合shader和逻辑细看。
发布于 2020-07-07