上次我們了解了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官方的解決思路。