简介: 感谢最近的工作需求,好好翻了一下 Unity URP UniversalRenderPipeline,继 URP 管线主要类 分析之后,来仔细看一下 UniversalRenderPipeline 吧
软件: unity 2022.3.14f1c1
管线:com.unity.render-pipelines.universal@14.0.9
背景
由上篇文章 URP 管线主要类 分析之后 可知
- UniversalRenderPipeline 继承自 RenderPipeline
- UniversalRenderer 继承自 ScriptableRenderer
- UniversalRenderer 的数据类为 UniversalRendererData,用于保存 UniversalRenderer 的配置和创建 UniversalRenderer
- UniversalRenderer 的构造函数强制需要传入 UniversalRendererData
- UniversalRendererData 有一个 Create 函数用来创建 UniversalRenderer
-
UniversalRenderPipelineAsset
- 提供 UniversalRenderPipeline 的创建方法
- 创建 UniversalRenderPipeline 时强制传入UniversalRenderPipelineAsset
- 内部有一个 ScriptableRendererData 列表,用来保存当前管线可以用到的 RendererData , 然后还是用一个 ScriptableRenderer[] m_Renderers 来缓存生成的 scriptableRenderer
- URP 中的相机默认都必须有一个 UniversalAdditionalCamera 组件,这个组件保存了当前要用的 UniversalRenderer 的索引,提供一个 scriptableRenderer 属性用来获取当前相机的 Renderer。
- scriptableRenderer 属性通过 UniversalRenderPipeline.asset 拿到管线配置文件,然后使用管线配置文件提供的 GetRenderer(int index) 方法获取对应的 scriptableRenderer。
Pipline 渲染步骤
除去我们不好找的引擎内部逻辑,我们就以引擎调用到 UniversalRenderPipeline 的 Render(ScriptableRenderContext renderContext, List<Camera> cameras) 开始往后推。
Render 方法
- Pipline 的 Render,接受一个渲染上下文和一个相机列表。
- 开始部分会做一个 ProfilingScope 标记,然后设置一些色彩空间,渲染层级,MSAA 级别等。
- 遍历所有的相机
- 如果是 Game 相机则调用 RenderCameraStack(renderContext, camera) 渲染。我们主要关注这个
- 如果不是 Game 相机则再做一个 BeginCameraRendering 标记,然后更新 Volume框架,然后调用用 RenderSingleCameraInternal(renderContext, camera) 进行渲染。然后关闭标记
- 关闭对应的 ProfilingScope 标记
RenderCameraStack 方法
渲染 UPR 中的相机Stack
- 起手添加一个 ProfilingScope 标记
- 从相机对象上获取 UniversalAdditionalCameraData 组件,
- 通过该组件就可以获得相机上的 scriptableRenderer
- 通过该组件就可以获得相机上的 Stack 列表
- 遍历 Stack 列表,对相机数据进行预处理
- 剔除已经删除的或未激活的相机,
- 剔除和base相机不是同一个 scriptableRenderer 类型的相机
- 收集堆中的相机是否有开启后处理的
- 结束 Stack 列表的遍历后,如果有相机不生效了,则更新相机的 stack
- 然后引入了 XRSsytem 系统, 向baske相机添加至其中,我不做 AR VR 所以,这里对我意义不大,当然 base 相机之后也是从这里取出,这里的放入、取出,我就当其没有发生过,管线这里这样写应该也是为了兼容 AR VR
- InitializeCameraData 方法初始化相机数据到 baseCameraData ,具体内容下面单写
- InitializeAdditionalCameraData 方法初始化相机数据到 baseCameraData , 具体内容下面单写
- 设置 baseCameraData 的一些属性
- RenderSingleCamera 调用单相机渲染 basecamera
- 遍历 cameraStack ,每个相机调用 RenderSingleCamera 进行渲染
- 获取 cameradata
- 调用 InitializeAdditionalCameraData 初始化 overlayCameraData
- 设置 overlayCameraData
- 调用 RenderSingleCamera 逐相机渲染
- 一些其它 XR 的设置和调用
上述的步骤中基本上每个相机都会一个 ProfilingScope 标记对,用来便于debug,辅助功能所以没有全部写在上述的步骤中
InitializeCameraData
顾名思义,初始化相机数据。这个方法会将数据返回给传入的 cameraData 对象,InitializeAdditionalCameraData 也是这个操作。
- 设置 cameraData 的一些属性
- 创建 cameraData 对象
- 调用 InitializeStackedCameraData 初始化相机堆栈数据
- 设置 cameraData.camera
- 设置相机的 MSAA、 XR 、HDR、cameraTargetDescriptor信息
- 设置相机的堆栈数据 InitializeStackedCameraData
- 设置 cameraData.targetTexture
- 设置 cameraData.cameraType
- 根据相机类型设置 环境和后处理设置,主要分为,场景相机数据、UPR的baseAdditionalCameraData数据、默认相机数据
- 设置输出设置,HDR 、size、renderScale、upscalingFilter、imageScalingMode、fsrOverrideSharpness、defaultOpaqueSortFlags、captureActions
InitializeAdditionalCameraData
初始化 additionalCameraData 相机数据到 cameraData ,这里的 cameraData 和 InitializeCameraData 里的是同一个对象(结构体 struct)
- 设置 cameraData 的各种属性对象
- 具体的属性对象就翻一下类看吧,就是相机场景的属性
- 设置一些 debug 属性的 override
- 设置了 SetViewProjectionAndJitterMatrix
RenderSingleCamera
主要的渲染逻辑在这里,主要是对renderer的设置,从这里也可以看出,我们的 UniversalRender 是和相机一一对应的,这个 renderer 对象就保存为 CameraData 的 renderer 属性,即 cameraData.renderer
- 设置从 cameraData 获取 renderer 和 camera
- 将 ScriptableRenderer.current=renderer ; 进行全局设置。ScriptableRenderer.current 是一个静态属性
- 从 CommandBufferPool 里获取一个 CommandBuffer cmd
- 打 ProfilingScope 标记
- 调用 renderer.OnPreCullRenderPasses函数
- 计算一次 cmd ,context.ExecuteCommandBuffer(cmd)
- 设置相机相关的 shader 全局变量
- 环境色
- 环境光 等
- 一些特殊相机的预处理,如 CameraType.Reflection、CameraType.Preview、SceneViewCamera
- cameraData 设置 MotionVector 相关
-
cameraData 设置相机输出 Handle 的尺寸
11.context 获取剔除结果 - renderer 初始化 RenderingData
- renderer 添加渲染Pass
- 判断是否使用 useRenderGraph
- 使用 则调用RecordAndExecuteRenderGraph 和 renderer.FinishRenderGraphRendering
- 不使用则调用 renderer.Setup 和 renderer.Execute* ,我们一般的渲染都在 renderer.Execute 里面执行
- 结束标记
- context.ExecuteCommandBuffer(cmd) 执行 cmd,结束
流程完成
基本上上面的步骤走完,就开始使用 相机的 Renderer 进行渲染了。
在 pipeline 中主要做的就是,
- 相机的设置
- 根据相机的 UniversalAdditionalCameraData 组件对象、相机本身的设置,管线的设置,来生成渲染要使用的 RenderingData。所以在后面的具体渲染中我们基本上方法接受的都是 ScriptableRenderContext 和 RenderingData,这两个对象
- 相机的 cull 操作
- 相机类型的区分
- 遍历相机,逐一设置后,调用正确的渲染方法。抛去 useRenderGraph,应该调用的是 renderer.Setup 和 renderer.Execute。
- renderer.Execute(context, ref renderingData); 接受 contex 和 renderingData
网友评论