您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關怎么利用Stencil來優化局部后處理特效,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
只是例子就不要再和我說什么十字片,廣告牌疊加了。這個效果的實現就是用純色在RT上重繪原模型給一次高斯模糊后疊回原圖。
加了Stencil后計算量下降了一半(為方便對比,我砍掉了后處理流程最后的原圖Blit流程和人物繪制部分,僅顯示了downsample和高斯模糊的量)
原理:
Stencil和ZTest就如同兩兄弟,非常相似,但前者卻常常被忽略。大家都知道,在擁有Early-Z(準確的說是Early-ZS,也就是Z+Stencil)機制的當代GPU里,我們可以通過由前向后繪制,通過ZTest讓被遮擋的物體免于繪制,這是一個最基本的fillrate優化手段。
Stencil本身是一個比較+改寫特定Buff的技術,和ZTest的比較深度+改寫深度其實是一樣的機制,只是擁有更多的變化。所以它也可以通過先改寫Buff,然后讓后面的物體在讀取這個Buff的時候檢測不通過,從而直接跳過像素計算階段。
局部物體使用后處理特效很不合算,就是因為即使只有局部需要計算,GPU也必須計算整個屏幕。如果先用Stencil在屏幕上繪制一個標記區域,就可以將后面的計算限定在小范圍內。
用的是一個簡單的法線延伸OutLine,擴大了棒子的體積。并在繪制過的地方標記Stencil為1
Pass { CULL OFF ZTest OFF COLORMASK 0 Stencil { Ref 1 Comp NotEqual Pass Replace ReadMask 1 WriteMask 1 } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; }; float _Outline; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal); float2 offset = TransformViewToProjection(norm.xy); o.vertex.xy += normalize(offset) * o.vertex.z * _Outline; UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { return 1; } ENDCG }
然后再在之后的后處理流程里,給需要的Pass加上
Stencil { Ref 1 Comp Equal Pass Keep ReadMask 1 WriteMask 1 }
即可。
不過……
1. Stencil只能通過繪制實體來寫入,不能在多張Buff間復制。所以一旦經過downsample就會丟失,而downsample是后處理中非常常見的。
2. 由于這個原因,RT繪制的順序也非常重要,只有作為繪制目標的RT才有獲得Stencil檢測的機會。
3.Stencil在其他RT中的利用,只能通過Graphics.SetRenderTarget(source.colorBuffer, stencil.depthBuffer)來完成,指向一張RT的顏色Buffer,卻同時指向另一張RT的depthBuff,且這兩張RT的分辨率必須完全一樣。
具體代碼(普通的Blit是沒法用的,會強制切換RenderTarget):
private void DepthBlit(RenderTexture source, RenderTexture destination, Material mat, int pass, RenderTexture depth) { if (depth == null) { Graphics.Blit(source, destination, mat, pass); return; } Graphics.SetRenderTarget(destination.colorBuffer, depth.depthBuffer); GL.PushMatrix(); GL.LoadOrtho(); mat.mainTexture = source; mat.SetPass(pass); GL.Begin(GL.QUADS); GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(0.0f, 1.0f, 0.1f); GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(1.0f, 1.0f, 0.1f); GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(1.0f, 0.0f, 0.1f); GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(0.0f, 0.0f, 0.1f); GL.End(); GL.PopMatrix(); }
紋理大小限制是最麻煩的,其它都還好。實際用法就是,保留保存著Stencil的那張RT,然后在需要的時候掛接在目標RT上。
繪制標記是有代價的,但也可以在繪制正常畫面時順帶繪制,這樣代價就小很多。
在可以用到的時候記得使用即可。
好吧,我知道你們可能會問Bloom本身具體是怎么做的。Bloom本身Unity內置后處理有一個,但是是全屏的。想做到局部,確實只能單開一張RT來繪制要泛光的物體。
但這就涉及到保留深度緩沖的問題,否則那個物體就必須顯示在最前面而無法被障礙物遮擋。因為同時還要切換RT繪制,唯一的辦法就是剛才的Graphics.SetRenderTarget(source.colorBuffer, stencil.depthBuffer),繪制新RT,同時使用以前的深度緩沖區。
我為了圖簡單,是直接在后處理階段里切換RenderTarget,然后用Graphics.DrawMeshNow來繪制要泛光的物體的,這會導致Mesh無法Batch。想要Batch,必須讓攝像機來繪制這些物體,應該是必須用到CommandBuffer這些東西。
關于怎么利用Stencil來優化局部后處理特效就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。