笔记(待完善)
什么是CommandBuffer
用来存储渲染命令的缓冲区。 保存着渲染命令列表,如(set render target, draw mesh等等),可以设置为在摄像机渲染期间的不同点执行。
为什么有CommandBuffer?用来干什么
● 而为了更好扩展unity的渲染管线,unity提供了CommandBuffer,让你根据自己的需求,在不同的渲染阶段插入绘制指令,例如插入DrawRenderer,DrawMesh,DrawProcedure,绘制的时候也可以根据需要设置绘制时材质(Material)的MaterialPropertyBlock更改当前绘制的材质的属性。 ● 这里我用了RenderFeature在BeforeRenderingOpaques的时候用DrawMesh指令渲染了一个Box。
● PS:MaterialPropertyBlock比直接修改Material的优势是:不会创建出新的材质实例。
从自定义RenderPipeline分析ScriptableRenderContext与CommandBuffer
- RenderPipeline结构:
首先先写一个继承自RenderPipeline的自定义管线类,类中有一个CameraRenderer,CameraRenderer主要负责的是渲染的主要逻辑,Render的方法需要重写(Override)参数也是固定的,可以看到这里的Cameras是一个数组,说明了当场景里有多个相机时,需要我们在这个RenderPipeline的Render函数中去遍历所有的Camera进行一个处理操作。 - 在Project中创建出RenderPipeline.Asset:
在Project文件夹下如何创建一个Asset文件,只需要继承RenderPipelineAsset以及在类前添加一个Attribute标签表示创建的资源在在哪一个创建的列表的哪一位置。固然,这个继承RenderPipelineAsset也必须要实现CreatePipeline这个方法。函数的结果也需要是返回一个RenderPipeline的实例。 - 最后,就是负责渲染主要逻辑的Renderer类,直译过来就是渲染器,渲染器的Render方法的参数可以随便设置,因为它目前并没有继承自什么类,所以在上一层的CustomRenderPipeline中,可以根据自己实际需要来处理。
- 可以看到在Render函数里主要的工作就是使用CommandBuffer把渲染过程相关指令写入到ScriptableRenderContext,最后ScriptableRenderContext使用Submit提交指令。
- 为了方便组织渲染的流程和复用,可以把其中渲染的一段流程抽离出来成为一个Pass。例如:图中的DrawSkyboxPass(并不完善)。
ScriptableRenderContext
● 在正式说Commandbuffer的作用之前,必须先理一理ScriptableRenderContext在SRP中的作用。 ScriptableRenderContext:Defines state and drawing commands that custom render pipelines use.use a ScriptableRenderContext to schedule and submit state updates and drawing commands to the GPU. ● 指定自定义渲染管线渲染时的渲染状态和声明绘制指令,ScriptableRenderContext 负责调度和提交渲染状态的更新以及绘制指令到GPU。 ● 说到渲染状态,在使用Opengl的时候,我们有时候也会根据渲染需要使用glEnable(xxxxx)更改渲染状态,使用BindVAO/VBO或者FBO/RBO这些命令,告诉GPU绘制的状态以及需要拿什么数据进行绘制,以及绘制到哪里。
渲染状态的更新(RenderStateBlock)
● 对于渲染状态的更新,体现最明显的是在context.DrawRenderers这个指令,可以看到DrawObjectPass在构造函数中它的RenderStateBlock是Nothing。 ● 所以它对于Opaque pass也好,Transparent pass也好,它渲染状态的更新是跟每个Shader中各自的Depth、ZWrite、Blend等等的状态设置有关。 ● 而下面的StencilState则是与在ForwardRenderer中的ForwardRendererData的具体设置有关。
PS:需要注意的是:DrawObjectPass需要跟RenderObjectPass做下区分,这两还不太一样。具体体现在:SetDetphState和SetStencilState这两个函数,能够更方便Override当前pass在DrawRenderers时的Render state。
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSettings, ref m_RenderStateBlock);这个指令是最常见用于绘制一批物体,在SRP中, 1.CullingResult是记录对物体,灯光,反射探针进行剔除的结果 2.DrawingSetting设置绘制顺序以及绘制时使用哪一个Shader中的Pass。 3.FilteringSetting设置过滤设置渲染指定的Layer,RenderStateBlock更改(StateBlock)来重载深度、模板写入方式。
CommandBuffer能插入的生命周期
● 正常来说一个相机在渲染的流程中会有以下Pass:在前向渲染时,会有预先生成一张Depth或者是DepthNormal的RenderTarget提供给后续渲染物体使用,然后就是渲染ShadowMap阶段,接着就是渲染不透明物体队列,渲染完不透明物体之后,可能会对不透明物体渲染出来的RenderTarget单独做一些后处理,然后再绘制SkyBox,然后再渲染透明物体,然后可能还会对当前渲染出来的RenderTarget做后处理。 ● 在延迟渲染中,会先渲染出不透明物体的GBuffer,重新解算深度,反射层的渲染然后就是自发光以及ShadowMap计算等等,然后在不同的渲染阶段前和后之间会有一些时间节点,可以让我们在不同的渲染阶段插入绘制指令。 ● 需要注意的是,在URP里会有一个RenderPassEvent,这个RenderPassEvent和CameraEvent不太一样,URP里的RenderPassEvent用来标志不同时间节点Pass之间的先后顺序,用于SortStable函数不同Pass根据时间节点的先后进行排序。 ● 在不同的渲染阶段插入绘制指令这一句话具体体现在哪呢? 具体体现在使用ScriptableRenderContext.ExecuteCommandBuffer维护ScriptableRenderContext,所以使用commandbuffer只是提供给我们暂存绘制指令的地方,实际提交到GPU绘制的是位于ScriptableRenderContext列表中的绘制指令。所以在渲染不同的阶段,也就是在不同的pass中,我们可以根据注入点的不同,在不同阶段的ScriptableRenderContext使用ScriptableRenderContext.ExecuteCommandBuffer(commandBuffer)往Context里的命令列表注入若干的Command。执行命令的时间点是Submit。
CommandBuffer常用函数
BeginSample、EndSample
ProfilingScope
SetRenderTarget、 RenderTexture
DrawMesh:绘制网格指令
Blit
综合分析CommandBuffer的使用方法
在URP使用RenderFeature进行扩展之前,首先先简要地介绍URP下渲染流程和ScriptableRenderFeature以及ScriptableRenderPass的原理,以便避开我们平时在用RenderFeature时扩展URP时会遇到的一些坑。
CommandBuffer注意事项
作业
完成洛基中的传送门特效
拉起、背景模糊(毛玻璃)、进入时相交边缘光圈、进入后消失、边缘流光
进入时相交边缘光圈
根据深度差获取相近的关系 PS:移动物体(熊)的shader尾部改成 Fallback “Diffuse”,才能够映射到深度图中
进入后消失
shader实现: 根据世界坐标大小、clip()函数消失掉背面的
毛玻璃
边缘流光
边缘贴图 + 光贴图 + _Time的uv偏移
拉起
C#控制scale
综合结果
还注意 毛玻璃的效果,需要在摄像机渲染移动物体之前,所以需要将移动物体的shader顺序置后。否则会在还没接触到传送门时,就模糊到传送门上。可以看到下图,熊周围的白光。 调整顺序:不含移动物体的颜色tex 》移动物体》含移动物体的深度tex》两个tex给到传送门shader
不含移动物体的颜色tex:command buffer 1 顺序 CameraEvent.AfterForwardOpaque 移动物体shader:“Queue” = “Geometry+1” 含移动物体的深度tex: command buffer 2 顺序 CameraEvent.BeforeForwardAlpha 传送门shader:"“Queue” = “Transparent”"
还留有点问题:用clip处理消失会残留影子。估计还得改下物体shader的fallback?
|