精選文章

SmallBurger Asset Home

  SmallBurger

2019年3月28日 星期四

如何客製化LWRP渲染流程

由於客製化LWRP渲染流程可以參考的例子並不多,目前是以BoatAttack為主,但這個案例過於複雜,在業界同好的盛情下,寫了一個以簡易水相關的案例,希望達拋專引玉的效果。BoatAttack

為什麼選擇水來當例子,主要是因為水的實作通常跟渲染流程有很大的關係,如同我前一篇文章介紹的反射、折射…等,比單一Shader效果複雜許多。

這裡我們要處理反射(倒影)及折射(水透到下面的地方),如下圖:


所以我們需要的素材有:
  1. 反射貼圖
  2. 水面深度貼圖
  3. 折射貼圖(也就是在畫水之前目前的背景圖)

因此我們需要一個射影機來拍射反射,而反射矩陣(拍射的角度及位置)需要根據目前的射影機來計算,進而拍射出反射貼圖。另外我們還需要在畫水之前,依照水面的深度來決定如何透出水面下的東西,所以也需要以目前的射影機拍射水面深度貼圖。至於折射貼圖,我們只要在畫水之前,將目前的畫面拷貝一份即可。

所以流程就是:
拍射反射貼圖=>拍射水面深度貼圖=>拷貝目前背景當折射貼圖=>渲染水體。

有了這個基本想法後,我們來看看如何在LWRP下,透過他提供的介面,不改其代碼來做到這些事情,另外所有的LWRP interface都要繼承MonoBehaviour,並放在要渲染的射影機下,讓他抓取這些介面跟我們溝通:
  1. IBeforeCameraRender:我寫了一個PlaneReflectionProcessor來繼承IBeforeCameraRender,這樣就可以透過ExecuteBeforeCameraRender這個事件來取得在Camera一開始渲染的時機,並在它傳入的Camera參數中取得目前射影機的矩陣,接下來就可以讓Reflection Camera依照目前的射影機設定好參數,並計算反射矩陣,之後就可以呼叫RenderSingleCamera來渲染反射貼圖,要記得設好過濾掉Water這個layer,因為完全不需要拍射到水。
  2. IRendererSetup:其實一開始並不打算實作這個介面,但因為想利用Multi Render Target的方式來達到渲染水面深度貼圖的目的(其實也可以擺在DepthOnlyPass來一起處理,讀者可以自行調整)。所以這裡寫了一個MySRPRenderSetup,用來處理客製化自己的渲染流程,不走LWRP的DefaultRendererSetup的流程。
  3. RenderOpaqueWithPlaneDepthPass:接下來在MySRPRenderSetup下,加了一個RenderOpaqueWithPlaneDepthPass,來取代RenderOpaqueForwardPass,用一個Pass同時渲染ColorBuffer渲染水面深度貼圖,為了這個目的,我們還要另外提供一個MRT的Shader來渲染物件,細節請參考MRTBase檔裡的CustomLightweight/MRTBase的代碼。
  4. DrawSkyAndCopyBackGroundPass:這裡用這個Pass取代了DrawSkyboxPass,主要的目的就是在畫水之前把目前的渲染的結果copy起來,以提供在畫水的時候可以依照深度攤提折射(透的程度)。
  5. 接下來就可以畫水了,Shader相當簡單,詳情請看代碼WaterPlane.shader。
  6. 水的渲染流程要在所有opaque物件畫完之後,於是擺在Transparent的RenderQueuqe,但為了能讓水也可以接受影子 ,目前DepthOnlyPass只能拍射出opaque的depth,所以寫了一個CaptureAllDepthOnlyPass來取代他。

由左而右,依序為反射,深度,水面深度,折射(copy畫水之前的背景圖)



到了這裡,應該差不多做完了,為了享受LWRP的multi light,特別加了一些點光源,來強化使用LWRP的價值。另外為了完整性,這裡加了一個Grayscale post Effect,讓整個客究化流程更完整,渲染結果如下:



相關GitHub專案連結:
HelloLWRP

Dream continues in...

沒有留言:

張貼留言