Command buffer: 用来存储渲染命令的缓冲区。
Command buffer保存着渲染命令列表,如(set render target,draw mesh等等),可以设置在摄像机渲染期间的不同点执行。
利用Command buffer帮助我们告诉管线进行什么样的绘制操作。
在OpenGL中,渲染过程中,出于渲染流程的需要,会频繁绑定VAO,VBO,FBO这类Buffer Object。这样重复性的操作即繁琐也缺乏可扩展性。
因此可以对一些重复性的操作进行提取封装:使用Shader类负责编译,在Model类添加一些读取模型,绑定VAO,VBO操作函数等等。
把渲染流程分为一个一个Pass进行编写,更加容易辨识在每个渲染流程中应该做什么,计算出什么结果。
出于跨平台的需要,unity对底层api进行进一步的处理,就得到了CommandBuffer。
回到Unity中,打开FrameDebugger,可以看到场景的渲染流程。对应ForwardRenderer.cs脚本中的
流程 | 对应Pass |
---|---|
MainLightShadowCasterPass | m_MainLightShadowCasterPass |
DepthOnlyPass | m_DepthPrepass |
ColorGrandingLutPass | m_ColorGradingLutPass |
DrawObjectsPass | m_RenderOpaqueForwardPass |
DrawSkyboxPass | m_DrawSkyboxPass |
CopyColorPass | m_CopyColorPass |
PostProcessPass | m_PostProcessPass |
PostProcessPass | m_FinalPostProcessPass |
上表对应默认的URP管线
为了更好地扩展unity渲染管线,unity提供了CommandBuffer,让你根据自己的需求,在不同的渲染阶段插入绘制指令,例如:DrawRenderer,DrawMesh,DrawProcedure,绘制的时候也可以根据需要设置绘制时材质(Material)的MaterialPropertyBlock更改当前绘制的材质属性。
图中利用RenderFeature在BeforeRenderingOpaques的时候利用DrawMesh指令渲染了一个Box。
MaterialPropertyBlock比直接修改Material的优势是:不会创建出新的材质实例。
本节从最简单的自定义RenderPipeline开始分析如何构造一个Scriptable Render Pipeline。
然后再分析Pass的概念以及CommandBuffer和ScriptableRenderContext之间的关系。
首先先写一个继承自RenderPipeline的自定义管线类,类中有一个CameraRenderer对象,CameraRenderer朱啊哟负责的是渲染的主要逻辑。
继承自RenderPipeline的Render方法需要重写,可以看到这里的Cameras是一个数组,说明了当场景里有多个相机时,需要我们在这个RenderPipeline的Render函数中去遍历所有的Camera进行一个处理操作。
为了创建一个Asset文件,只需要集成RenderPipelineAsset以及在类前面添加一个Attribute标签表示创建的资源在哪一个创建的列表的哪一个位置。
当然,这个继承RenderPipelineAsset也必须要实现CreatePipeline方法,并且返回一个RenderPipeline实例。
最后就是负责主要渲染逻辑的Renderer类,直译过来就是渲染器,渲染器的Render方法的参数可以根据需求进行设置。可以看到在Render函数中朱啊哟的工作就是使用CommandBuffer把渲染过程相关指令写入到ScriptableRenderContext,最后ScriptalbeRenderContext使用Submit提交指令。为了方便组织渲染的流程和复用,可以把其中渲染的一段流程抽离出来单独写作一个Pass。
在正式说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绘制的状态以及需要拿什么数据进行绘制,以及绘制到哪里。
对于渲染状态的更新,体现最明显的是在context.DrawRenderers这个指令,可以看到DrawObjectPass在构造函数中它的RenderStateBlock是Nothing。
所以它对于Opaque pass也好,Transparent pass也好,它渲染状态的更新是跟每个Shader中各自的Depth、ZWrite、Blend等等的状态设置有关。
而下面的StencilState则是与在ForwardRenderer中的ForwardRendererData的具体设置有关。
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSettings, ref m_RenderStateBlock);
这个函数是最常见用于绘制一批物体,在SRP中,
Command Buffers 出现的目的是为了扩展Unity的渲染管线(Rendering Pipeline) ,一个Command Buffer 中存储很多绘制命令(Rendering Command),这些 Rendering Command 其实就是OpenGL或者DirectX 中的一些列指令,比如glDrawElement,glClear等。
Command Buffer 扩展渲染管线的方法是:预定义一系列的渲染指令,然后在特定的时机去执行这些指令。
这些特定的时机是由我们来自定义指定的,下图渲染过程中的绿点说明了这些可以指定的时机:
Skybox的渲染是在不透明图像效果之后,半透明物体之前。
因为不透明物体的绘制是从前往后,而半透明物体的绘制是从后往前的。Command buffers 可以用来代替 Image Effect ,也可以和Image Effect 一起使用。
效果如图:
CommandBufferBlurRefraction.cs
用于渲染毛玻璃的Shader:GlassWithoutGrab.shader
用于模糊的Shader:SeparableBlur.shader
if (!(LightMode == nullLightMode == ForwardBaseLightMode == ForwardAdd)){// Command Buffer 执行的渲染结果会出现问题~;}
百人里面的太干了啃不动,后半部分FrameBuffer的介绍看的框架目录里面提供的另一篇简短的博客。
要点: