這篇文章將為大家詳細(xì)講解有關(guān)怎么利用Stencil來(lái)優(yōu)化局部后處理特效,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
創(chuàng)新互聯(lián)是網(wǎng)站建設(shè)技術(shù)企業(yè),為成都企業(yè)提供專業(yè)的網(wǎng)站建設(shè)、成都做網(wǎng)站,網(wǎng)站設(shè)計(jì),網(wǎng)站制作,網(wǎng)站改版等技術(shù)服務(wù)。擁有10多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制適合企業(yè)的網(wǎng)站。10多年品質(zhì),值得信賴!
只是例子就不要再和我說(shuō)什么十字片,廣告牌疊加了。這個(gè)效果的實(shí)現(xiàn)就是用純色在RT上重繪原模型給一次高斯模糊后疊回原圖。
加了Stencil后計(jì)算量下降了一半(為方便對(duì)比,我砍掉了后處理流程最后的原圖Blit流程和人物繪制部分,僅顯示了downsample和高斯模糊的量)
原理:
Stencil和ZTest就如同兩兄弟,非常相似,但前者卻常常被忽略。大家都知道,在擁有Early-Z(準(zhǔn)確的說(shuō)是Early-ZS,也就是Z+Stencil)機(jī)制的當(dāng)代GPU里,我們可以通過(guò)由前向后繪制,通過(guò)ZTest讓被遮擋的物體免于繪制,這是一個(gè)最基本的fillrate優(yōu)化手段。
Stencil本身是一個(gè)比較+改寫特定Buff的技術(shù),和ZTest的比較深度+改寫深度其實(shí)是一樣的機(jī)制,只是擁有更多的變化。所以它也可以通過(guò)先改寫B(tài)uff,然后讓后面的物體在讀取這個(gè)Buff的時(shí)候檢測(cè)不通過(guò),從而直接跳過(guò)像素計(jì)算階段。
局部物體使用后處理特效很不合算,就是因?yàn)榧词怪挥芯植啃枰?jì)算,GPU也必須計(jì)算整個(gè)屏幕。如果先用Stencil在屏幕上繪制一個(gè)標(biāo)記區(qū)域,就可以將后面的計(jì)算限定在小范圍內(nèi)。
用的是一個(gè)簡(jiǎn)單的法線延伸OutLine,擴(kuò)大了棒子的體積。并在繪制過(guò)的地方標(biāo)記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 }
即可。
不過(guò)……
1. Stencil只能通過(guò)繪制實(shí)體來(lái)寫入,不能在多張Buff間復(fù)制。所以一旦經(jīng)過(guò)downsample就會(huì)丟失,而downsample是后處理中非常常見的。
2. 由于這個(gè)原因,RT繪制的順序也非常重要,只有作為繪制目標(biāo)的RT才有獲得Stencil檢測(cè)的機(jī)會(huì)。
3.Stencil在其他RT中的利用,只能通過(guò)Graphics.SetRenderTarget(source.colorBuffer, stencil.depthBuffer)來(lái)完成,指向一張RT的顏色Buffer,卻同時(shí)指向另一張RT的depthBuff,且這兩張RT的分辨率必須完全一樣。
具體代碼(普通的Blit是沒(méi)法用的,會(huì)強(qiáng)制切換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(); }
紋理大小限制是最麻煩的,其它都還好。實(shí)際用法就是,保留保存著Stencil的那張RT,然后在需要的時(shí)候掛接在目標(biāo)RT上。
繪制標(biāo)記是有代價(jià)的,但也可以在繪制正常畫面時(shí)順帶繪制,這樣代價(jià)就小很多。
在可以用到的時(shí)候記得使用即可。
好吧,我知道你們可能會(huì)問(wèn)Bloom本身具體是怎么做的。Bloom本身Unity內(nèi)置后處理有一個(gè),但是是全屏的。想做到局部,確實(shí)只能單開一張RT來(lái)繪制要泛光的物體。
但這就涉及到保留深度緩沖的問(wèn)題,否則那個(gè)物體就必須顯示在最前面而無(wú)法被障礙物遮擋。因?yàn)橥瑫r(shí)還要切換RT繪制,唯一的辦法就是剛才的Graphics.SetRenderTarget(source.colorBuffer, stencil.depthBuffer),繪制新RT,同時(shí)使用以前的深度緩沖區(qū)。
我為了圖簡(jiǎn)單,是直接在后處理階段里切換RenderTarget,然后用Graphics.DrawMeshNow來(lái)繪制要泛光的物體的,這會(huì)導(dǎo)致Mesh無(wú)法Batch。想要Batch,必須讓攝像機(jī)來(lái)繪制這些物體,應(yīng)該是必須用到CommandBuffer這些東西。
關(guān)于怎么利用Stencil來(lái)優(yōu)化局部后處理特效就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。