天天看點

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

該篇是對Catlike Coding這篇文章的概要總結,本人能力有限,如果有不正确的地方歡迎指正  https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/custom-pipeline/

通過這篇文章,你将學習到

  • Create a pipeline asset and instance. 建立一個渲染管線資源和執行個體
  • Cull, filter, sort, render.   剪裁,過濾,排序和渲染
  • Keep memory clean.   記憶體清理優化
  • Provide a good editing experience.  更好的編輯器使用者體驗

This tutorial is made with Unity 2018.3.0f2.  這篇文章的實作基于Unity 2018.3.0f2版本,是在Unity中實作一個自定義渲染管線教程系列的第一篇。

1.建立一個管線

在Unity2018之前,Unity中有Foward 和 Deferred 兩種渲染管線,渲染管線中你可以操控的部分和内容非常少。在Unity2018中,提供了可程式設計渲染管線(SRP),有了它,我們可以自定義管線中的很多内容,雖然大多數步驟是Unity中已經封裝好的功能(比如Culling裁剪)。在2018中,SRP還處于preview階段,但是目前的版本提供的功能和穩定性足以讓我們實作自定義渲染管線。

1.1項目設定

建立一個标準3D項目,移除掉PackageManager中除Package Manager UI外的所有其他内容。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 将Unity2018預設的Gamma空間改成線性空間(Edit / Project Settings / Player  -- Color Space in the Other Settingssection to Linear)

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

建立一些測試用的簡單材質資源:

  • 預設的standard 材質,Rendering Mode 為opaque
  • 預設的standard 材質, Rendering Mode 為Transparent 
  • Unlit/Color shader,   Rendering Mode 為opaque
  • Unlit/Color shader,   Rendering Mode 為Transparent 
Unity SRP自定義渲染管線 -- 1.Custom Pipeline

2.渲染管線資源(Pipeline Asset)

Unity預設使用的是傳統的前向渲染管線,如果要使用我們自定義的,需要在Edit / Project Settings / Graphics中設定

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

想要設定我們自定義的RenderPipelineAsset,我們需要先為我們的自定義渲染管線建立一個RenderPipelineAsset。它繼承于RenderPipelineAsset,我們把自定義的渲染管線命名為MyPipline,是以對應的管線資源檔案就命名為MyPipelineAsset。我們需要使用UnityEngine.Experimental.Rendering命名空間,因為該功能還在preview階段,以後如果正式版本釋出的話可能需要更改成對應的命名空間

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

RenderPipelineAsset的主要目的是幫助Unity建立一個渲染管線執行個體,RenderPipelineAsset自身其實就是存儲各渲染管線的配置。我們可以通過overriding the 

InternalCreatePipeline

 方法來建立一個渲染管線執行個體,因為我們現在還沒定義自己的渲染管線,是以現在先傳回個Null。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 用CreateAssetMenu函數來使得在編輯器中可以建立我們的管線

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

建立出我們自己的管線資源:

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

将建立好的管線Asset指派到SRP Setting

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

建立一個實作了

IRenderPipeline

接口的類,命名為MyPipeline,它将是用于渲染過程的執行個體

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 為了友善,我們直接繼承RenderPipeline類,它是Unity總已經實作

IRenderPipeline

接口基本功能的類

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 現在,我們将之前InternalCreatePipeline函數中傳回Null部分的代碼替換成建立我們自定義好的MyPipeline類

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

2.渲染

pipeline每幀渲染,Unity做的事就是使用context和active狀态的camera作為參數,調用pipeline中的render函數,進行渲染。這個過程對game 視窗,scene視窗和材質預覽視窗都是一樣的。我們現在要做的就是正确的設定好各參數,用正确的順序繪制出應該被渲染的東西。

2.1 Context(上下文)

RenderPipeline需要實作Render這個方法,它的第一個參數是Context,一個Context是一個

ScriptableRenderContext

結構體,作用是對Native code的橋接。該函數的第二個參數是一個camera數組。

  基類RenderPipeline的RenderPipeline.Render函數并沒有實際繪制任何東西,隻是檢查了管線中的物體是否合法用于渲染,如果不是的話會抛出異常。我們調用積累中的這個函數來保持這個檢查功能。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

我們可以調用command是去控制渲染狀态和繪制各種東西,其中最簡單的一個例子就是繪制天空盒,我們調用DrawSkyBox函數完成這個功能。該函數需要傳入一個錄影機作為參數,為求簡便我們使用Camera數組中的第一個。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

調用了這個函數我們還是看不到任何東西,這是因為我們這是把command傳入了buffer,實際起效需要我們通過submit函數submit 這些commands。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

這回我們能在game視圖中看到skybox了,我們也能在frame debugger中看到。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 2.2 Cameras

場景中可能存在多個錄影機需要挨個渲染,我們可以為單個錄影機寫一個render函數,在我們目前要實作的渲染管線中,主要關注這個函數就可以了。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline
Unity SRP自定義渲染管線 -- 1.Custom Pipeline

想要正确的渲染Skybox和整個場景,我們需要通過Camera的位置和視角設定MVP矩陣,Unity Shader中unity_MatrixVP 就是這個矩陣。我們需要将Camera中的配置資訊通過SetupCameraProperties函數寫入Context中。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

2.3 Command Buffers

Context 直到我們Submit才會進行實際的渲染,在這之前,我們需要配置它并且将comands加入其中等之後執行。一些任務比如繪制Skybox有專門的函數實作,但是很多commands都是用command buffer來完成。

command buffer在UnityEngine.Rendering命名空間中,它是在srp功能之前就有的功能,我們在繪制Skybox之前建立一個command buffer。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

ExecuteCommandBuffer函數将command傳入context的内部,等待submit後執行

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

command buffer會消耗unity native層的記憶體資源,當我們不再需要它的時候要立刻釋放掉。Release函數可以完成這個功能。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

執行一個空的command buffer沒有意義,我們加入command buffer是為了清除render target去保證之前渲染的東西不會影響到目前。我們向buffer中加入一個clear command通過調用ClearRenderTarget函數,它包括三個參數,第一個參數是否清除depth ,第二個參數是否清除color,第三個參數是将緩沖區清除成什麼顔色。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

在frame debugger中我肯能夠看到command buffer執行的結果,清除了Z緩沖和stencil緩沖。 

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

通過Camera中的clear flags和background color參數,我們可以清除掉這個函數的寫死,直接使用配置好的資訊 

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

給Command Buffer設定一個名字,在這裡我們将其設定成camera的name,在frame debugger中就可以看到這個相應資訊。 

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

2.4 Culling

我們目前隻渲染了Skybox還沒有渲染場景中的物體,在渲染物體之前,我們需要先通過錄影機的視椎體對物體進行裁剪剔除,隻渲染那些被我們看見,應該被渲染的物體。

通過ScriptableCullingParameters結構體和 

CullResults.GetCullingParameters

 函數,我們可以針對某個Camera得到裁剪的配置資訊。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

優化一下,可以判斷一下Camera 設定是否valid,如果不是的話,該函數會傳回false,直接不渲染東西return掉。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

得到裁剪的配置資訊後,通弄過CullResults.Cull 函數可以得到最終的裁剪結果 

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

2.5 Drawing

有了場景中的可視資訊後,我們可以通過DrawRenderers函數進行下一步的渲染。該函數用剪裁後的cull.visibleRenderers和

DrawRendererSettings

 and 

FilterRenderersSettings

配置資訊進行渲染。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

到現在我們還是看不到任何物體,這是因為我們需要設定 FilterRenderersSettings資訊,通過置為true,可以渲染everything。·

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

通過設定DrawRendererSettings資訊,我們設定用于渲染的shader pass,在這裡我們使用SRPDefaultUnlit Shader Pass。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

到這步在場景中我們就可以看到不透明的物體了 

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

在frame debugger中可以看到透明物體也被渲染了,但是在game視圖中沒看到,這是因為渲染順序的問題,透明物體渲染時不會寫入深度緩沖,應該在最後再渲染。當透明物體被渲染後,再渲染skybox就會導緻透明物體渲染不正确。 

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

要解決這個問題,我們需要調整渲染順序,先隻渲染不透明物體。 

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 之後渲染skybox,最後再渲染透明物體

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 現在不透明物體,透明物體和skybox都可以正常渲染了。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 我們先渲染不透明物體,後渲染skybox的原因是因為不透明物體一定在skybox之前,會擋住skybox,這樣的話就可以減少over draw。同樣的原理,也可以用于不透明物體之間的遮擋,我們可以先對不透明物體進行排序,明确前後關系後按順序渲染,這樣被擋住物體的部分就不會參與渲染,可以增加渲染效率。

通過設定SortFlags.CommonOpaque,可以從前向後的順序渲染不透明物體

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

為了渲染透明物體,我們要将渲染順序改回從後到前,通過設定SortFlags.CommonTransparent,可以實作該功能

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 現在,我們的自定義渲染管線可以正确的渲染不透明和透明物體了。

3.Polishing

能夠正确的渲染隻是渲染管線一小部分功能,我們還要考慮很多其他東西,比如記憶體的配置設定,和Editor是否結合的更好等等問題。

3.1 Memory Allocations

不要每幀都建立CullResults結構體,因為它裡面有3個List,都需要配置設定記憶體

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

去掉Camera.name的調用,每次調用這個,都會新生成一個string,消耗記憶體

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

緩存command Buffer,不要每幀都配置設定,使用clear進行重置 

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

3.2 Frame Debugger Sampling

通過BeginSample 和 EndSample,我們可以控制frame debugger中顯示的層級,便于調試

Unity SRP自定義渲染管線 -- 1.Custom Pipeline
Unity SRP自定義渲染管線 -- 1.Custom Pipeline

3.3 Rendering the Default Pipeline

因為我們自定義的這個渲染管線隻支援unlit shaders,當物體使用其他shader的時候就渲染不出來了。我們可以使用一個Unity的error shader,當物體不能正确渲染的時候可以顯示其形狀,通過使用DrawDefaultPipeline函數來實作這個功能。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

 我們使用Unity預設的ForwardBase pass,我們不用考慮剪裁排序這些,因為他們本來就不該被正确的渲染出來

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

因為我們的管線不支援ForwardBase,是以這些物體渲染的并不正确,我們用Unity中的ErrorShader生成一個material,用于渲染,渲染結果就是粉粉的我們熟悉的那種效果了。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline
Unity SRP自定義渲染管線 -- 1.Custom Pipeline

目前是隻有使用了ForwardPass的shader會得到這種效果,我們通過設定drawSettings,可以将Unity中其他shader pass加入其中。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

3.4 Conditional Code Execution

通過Conditional功能,我們可以控制函數隻在development版本和Editor中編譯運作,在正式build版本中會被删掉不進行編譯

Unity SRP自定義渲染管線 -- 1.Custom Pipeline

3.5 UI in Scene Window

我們不用做任何處理,就可以在game視圖中正确顯示UI,這些Unity已經為我們做好了。但是想在Scene視圖中看到UI,我們需要調用ScriptableRenderContext.EmitWorldGeometryForSceneView函數。但是要注意的是調用這個函數會導緻UI在game視圖中再渲染一遍,是以我們要根據CameraType.SceneView區分是否是在scene視圖中,并且用#if UNITY_EDITOR來保證隻在Editor模式下編譯這段代碼。

Unity SRP自定義渲染管線 -- 1.Custom Pipeline