URP学习之四--各类Pass
上次我们了解了URP大致的框架,接下来看看每个Pass都是做什么的,先看DrawObjectPass:
从截图中我们可以看到这个Pass主要用于渲染不透明物体和半透明物体(ForwardPath)。
首先我们看一下这个Pass的构造:
public DrawObjectsPass( string profilerTag, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference) { this.m_ProfilerTag = profilerTag; this.m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward")); this.m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward")); this.m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit")); this.renderPassEvent = evt; this.m_FilteringSettings = new FilteringSettings(new RenderQueueRange?(renderQueueRange), (int) layerMask, uint.MaxValue, 0); this.m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); this.m_IsOpaque = opaque; if (!stencilState.enabled) return; this.m_RenderStateBlock.stencilReference = stencilReference; this.m_RenderStateBlock.mask = RenderStateMask.Stencil; this.m_RenderStateBlock.stencilState = stencilState; }
这个Pass会执行标有以上三个tag(UniversalForward、LightweightForward、SRPDefaultUnlit)的shaderPass。通过是否是Opaque决定这个Pass是用来渲染不透明物体还是半透明物体,最后初始化了模板测试相关数据。
接下来是执行(Execute)方法:
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer commandBuffer = CommandBufferPool.Get(this.m_ProfilerTag); using (new ProfilingSample(commandBuffer, this.m_ProfilerTag, (CustomSampler) null)) { context.ExecuteCommandBuffer(commandBuffer); commandBuffer.Clear(); Camera camera = renderingData.cameraData.camera; SortingCriteria sortingCriteria = this.m_IsOpaque ? renderingData.cameraData.defaultOpaqueSortFlags : SortingCriteria.CommonTransparent; DrawingSettings drawingSettings = this.CreateDrawingSettings(this.m_ShaderTagIdList, ref renderingData, sortingCriteria); context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref this.m_FilteringSettings, ref this.m_RenderStateBlock); RenderingUtils.RenderObjectsWithError(context, ref renderingData.cullResults, camera, this.m_FilteringSettings, SortingCriteria.None); } context.ExecuteCommandBuffer(commandBuffer); CommandBufferPool.Release(commandBuffer); }
这个方法和我们写SRP时基本一样,排序设置是根据Opaque字段决定,DrawSettings通过CreateDrawingSettings方法生成:
public DrawingSettings CreateDrawingSettings( List<ShaderTagId> shaderTagIdList, ref RenderingData renderingData, SortingCriteria sortingCriteria) { if (shaderTagIdList == null || shaderTagIdList.Count == 0) { Debug.LogWarning((object) "ShaderTagId list is invalid. DrawingSettings is created with default pipeline ShaderTagId"); return this.CreateDrawingSettings(new ShaderTagId("UniversalPipeline"), ref renderingData, sortingCriteria); } DrawingSettings drawingSettings = this.CreateDrawingSettings(shaderTagIdList[0], ref renderingData, sortingCriteria); for (int index = 1; index < shaderTagIdList.Count; ++index) drawingSettings.SetShaderPassName(index, shaderTagIdList[index]); return drawingSettings; }
最后DrawRenderers,但是我们发现后面还有个RenderObjects方法,这个方法是对以前一些TagId做的兼容,有以下Tag:
private static List<ShaderTagId> m_LegacyShaderPassNames = new List<ShaderTagId>() { new ShaderTagId("Always"), new ShaderTagId("ForwardBase"), new ShaderTagId("PrepassBase"), new ShaderTagId("Vertex"), new ShaderTagId("VertexLMRGBM"), new ShaderTagId("VertexLM") };
我们会发现非常简短的代码,一个Pass就完成了,可见我们如果要扩展Pass也是方便的很(其实最关键是核心方法都被封装了,不需要我们操心,哈哈哈~)
接下来我们看一下DepthOnlyPass,构造函数确定了FilterSettings和RenderEvent(过于简单,就不贴代码了),Setup函数如下:
public void Setup( RenderTextureDescriptor baseDescriptor, RenderTargetHandle depthAttachmentHandle) { this.depthAttachmentHandle = depthAttachmentHandle; baseDescriptor.colorFormat = RenderTextureFormat.Depth; baseDescriptor.depthBufferBits = this.kDepthBufferBits; baseDescriptor.msaaSamples = 1; this.descriptor = baseDescriptor; }
这里不了解RenderTextureDescriptor 结构的小伙伴可以去官方api查一下,里面记录的是对于RT的一些描述信息,depthBufferBits默认给的32bit。RenderTargetHandle结构主要记录了一个shader property id。
接下来是一个Configure方法:
public override void Configure( CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { cmd.GetTemporaryRT(this.depthAttachmentHandle.id, this.descriptor, FilterMode.Point); this.ConfigureTarget(this.depthAttachmentHandle.Identifier()); this.ConfigureClear(ClearFlag.All, Color.black); }
这个方法是获取RT,所有Pass的Configure方法都是在Pass的Execute方法之前执行,说到这里,笔者忽然发现这些Pass过于简单,没有什么好说的,于是大概看了看所有的Pass,挑笔者觉得重要的东西说一下吧:
首先我们看一下Pass的执行顺序:
MainLightShadowCasterPass、AdditionalLightsShadowCasterPass、DepthOnlyPass、ScreenSpaceShadowResolvePass、ColorGradingLutPass、OpaqueForwardPass、CopyDepthPass、DrawSkyboxPass、CopyColorPass、TransparentForwardPass、InvokeOnRenderObjectCallbackPass、PostProcessPass、CapturePass、FinalBlitPass。
需要注意的是比默认管线多了CopyColor和CopyDepth两个步骤,这两个步骤对于我们做水面需要抓取color做折射的效果很有帮助,但是缺陷是无法多重折射,因为Color只有Opaque信息。
ShadowCasterPass和ScreenSpaceShadowResolvePass熟悉屏幕空间阴影的实现的伙伴都很熟悉了,在这里就不赘述了。
DepthOnlyPass主要绘制了有“DepthOnly” Tag的Pass,需要注意的是DepthOnly是可以AlphaClip的。
CapturePass时所有渲染行为结束时的Pass,目前还不知道可以用来干什么。
其他pass基本上见名知意。接下来的几节笔者会挑URP的几个shader一起学习一下,通过学习官方提供的shader,增加对URP渲染代码编写的熟练度,避免踩一些坑,也可以学习到不同问题下Unity官方的解决思路。