講些什么?
絕大多數的游戲或多或少都會使用些后處理效果.
早期版本中,Unity在提供的接口有限,優化空間不大,屬於放任自流。官方推出了Post-Processing(下文簡稱PP)並在Github上長期維護,很好的將后處理與其提供的一些新的渲染API結合起來。無論是在易用性還是性能上都要比以前好。
本文主要針對於PP的自定義擴展上進行一些討論。只有當需要添加新的自定義效果或者修改PP內置效果時才需要考慮這些問題。順便連帶着提一提涉及到的Unity新的渲染概念。
由PostProcessEvent引出
為了能正確理解下文討論的內容,需閱讀鏈接處Wiki上的文字了解添加自定義后處理的步驟。你會首先看到PostProcessEvent這個枚舉,這個枚舉是貫穿始終的。
1 public enum PostProcessEvent 2 { 3 BeforeTransparent = 0, 4 BeforeStack = 1, 5 AfterStack = 2, 6 } 7 8 ... 9 10 public PostProcessAttribute(Type renderer, PostProcessEvent eventType, string menuItem, bool allowInSceneView = true) 11 { 12 ... 13 builtinEffect = false; 14 } 15 16 internal PostProcessAttribute(Type renderer, string menuItem, bool allowInSceneView = true) 17 { 18 ... 19 builtinEffect = true; 20 }
PP會根據枚舉標記將所有后處理歸類放到不同的序列,序列之間最大差異是執行順序問題(這同引擎渲染的RenderQueue類似)。
問:第一次看到BeforeStack和AfterStack會有疑惑,這里的Before和After是相對於什么的?
答:注意上面這個Attribute的構造函數重載代碼中后面一種重載沒有指定eventType,但是將builtinEffect賦為true.實際上builtinEffect這個布爾變量暗含着一個Built-in Stack的序列(Wiki中解釋了常見的Vignette,Bloom等都屬於這個序列).
為什么要分不同序列
有兩個主要原因:
1.方便擴展:
后處理效果是對執行順序敏感的,比如不想影響特效(大多數為半透明),那就需要標記為BeforeTransparent,以保證后處理發生在所有半透明物體繪制之前。
有了CommandBuffer之后,Unity提供了更多可選擇的渲染時機,PP進行簡化。
如下圖,Unity提供的幾個序列,實際上就是方便你將自己的后處理選擇一個適合的渲染時機。
2.提高性能
同一個序列內的后處理效果大概率對資源存在復用可能,特別是RenderTarget。雖然單像素的計算復雜度對后處理有影響,但頻繁的切換RenderTarget(俗稱翻Buffer)造成更大且不必要的開銷。
用FrameDebugger去查看后處理的各個Pass,會發現一個叫Uber的步驟,Uber不對應某個具體效果,它是個特殊的Pass,通過將之前分散在各個后處理着色器中的渲染邏輯整合到一起,並利用ShaderKeywords來進行代碼段的屏蔽開啟。從而最大程度上提高資源復用,降低DC。
我畫了個圖,看上去更直觀,左側是優化前,右側是優化后的,無論是DrawCall,還是對RenderTexture的使用都由性能提升。
如何整合
打開Uber.shader,會看到里面都是Built-in Stack的內容,如果你自定義的后處理Shader,
如果你自定義的后處理僅需一個DC或存在與內置效果有共用資源的情況,那就盡量將其整合入Uber中.
整合過程中提幾個需要注意的地方:
1.自定義的后處理腳本中要使用正確的PostProcessAttribute重載。
2.將片元着色器邏輯整合進Uber.shader的FragUber函數中時,注意與其它效果間的順序,若不是常駐效果,應該通過Keyword來提供開關。
3.PP提供的Shader中大多引其自帶的cginc文件,很多平時常用的函數都不存在,需要自行添加。
每一個引擎都需要解決如何最合理和高效的提供后處理的接口。Unity提供的PP源代碼值得一看。
提及兩個概念
PP的設計是基於CommandBuffer的,並大量使用了MaterialPropertyBlock。這里略帶提及一下這兩個概念
1.CommandBuffer(CB)
CB就是緩存將一系列渲染命令,在一個合適渲染階段進行執行。這些階段可以查看CameraEvent這個枚舉:可見Unity考慮的很周到,大多數渲染階段都提供了,這個東西還是該點贊的。
重點看CameraEvent,BuiltinRenderTextureType,CommandBuffer這幾個API文檔
2.MaterialPropertyBlock(MPB)
MaterialPropertyBlock is used by Graphics.DrawMesh and Renderer.SetPropertyBlock. Use it in situations where you want to draw multiple objects with the same material, but slightly different properties. For example, if you want to slightly change the color of each mesh drawn. Changing the render state is not supported.
這是文檔的一段說明,Unity建議用MPB的API去替換掉傳統的Material.setXXX,有用戶測試過,提升還是蠻大的。猜測Unity應該是對材質管理和圖形API底層Shader賦值的結構上進行了優化,降低了不必要的查找,拷貝等。
以上是近日來在實際工作上的一點分享,希望自己日后有精力時,在后處理方面有更多分享。如果你有任何不同的理解或意見,歡迎留言。
尊重他人智慧成果,非本人同意,請勿轉載