Shader篇之GrabTexture
這裡是Shader篇之GrabTexture,主要講一下一些常見的需要捕捉屏幕圖像才能完成的效果。如屏幕扭曲,折射等。系列文章偏向於基礎,主要都是為了記錄工作中的問題,方便自己記憶理解,並非純原創性文章,如有錯誤之處,還望大家指正。本章節主要介紹GrabPass和CommandBuffer兩種技術。
GrabPass
GrabPass可以很方便地捕獲當前渲染時刻的FrameBuffer中的圖像。其原理就是從當前FrameBuffer中copy一份紋理,通過SetTexture的方式設置紋理。至於GrabPass的性能問題,一般認為是對FrameBuffer 進行的一些pixel copy operations造成的,具體Unity是怎麼實現的,不得而知。
這個與後處理的性能消耗似乎是同一回事。淺談後處理技術中的各種坑
用法非常簡單如下:
SubShader { Pass { ...... } GrabPass { "_GrabTexture" } sampler2D _GrabTexture Pass { ...... }}
GrabPass在Shader中可以是任意順序,具體看你想要的效果而定。在GrabPass設置Name命令,可以有效減少GrabTexture的次數。相同Name屬性的shader,使用同一張GrabTexture。
下面是GrabPass的一個實例——屏幕雨滴效果:

實現的方法比較簡單,雨滴的動畫效果可以由粒子系統 + 序列幀動畫完成。這裡主要講解雨滴"折射"效果的思路:捕捉屏幕圖像GrabTexture,用序列幀法線圖計算UV偏移,用偏移後的UV對GrabTexture進行紋理採樣。其實就是一個通用的屏幕扭曲Shader,關鍵代碼如下:
SubShader { GrabPass { "_RaceDropTex" } Pass { CGPROGRAM #include "UnityCG.cginc" #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest sampler2D _RaceDropTex; sampler2D _Distortion; fixed4 _RaceDropTex_TexelSize; fixed _Strength; struct input { fixed4 vertex : POSITION; fixed2 texcoord : TEXCOORD0; }; struct v2f { fixed4 vertex : POSITION; fixed2 texcoord : TEXCOORD0; fixed4 uvgrab : TEXCOORD1; }; v2f vert (input v) { v2f o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.texcoord = v.texcoord; o.uvgrab = ComputeGrabScreenPos(o.vertex); return o; } fixed4 frag (v2f i) : COLOR { fixed2 norm = UnpackNormal(tex2D(_Distortion, i.texcoord)).rg; i.uvgrab.xy -= _Strength * norm.rg * _RaceDropTex_TexelSize.xy; fixed4 col = tex2Dproj(_RaceDropTex, UNITY_PROJ_COORD(i.uvgrab)); return col; } ENDCG }}
ComputeGrabScreenPos內置方法可以在UnityCG等cginc文件中查找。作用是計算頂點在屏幕空間的坐標,計算方法與ComputeScreenPos大致一樣。
這裡有很精闢的分析:Unity Shader中的ComputeScreenPos函數
inline float4 ComputeGrabScreenPos (float4 pos) { #if UNITY_UV_STARTS_AT_TOP float scale = -1.0; #else float scale = 1.0; #endif float4 o = pos * 0.5f; o.xy = float2(o.x, o.y*scale) + o.w; o.zw = pos.zw; return o;}
圖像的flip問題具體分析見上一章:淺談後處理技術中的各種坑。
用tex2Dproj、UNITY_PROJ_COORD等宏可以幫我們處理了跨平台等問題。
fixed4 col = tex2Dproj(_RaceDropTex, UNITY_PROJ_COORD(i.uvgrab));
CommandBuffer
CommandBuffer可以允許我們在Rendering過程中在不同的時間點執行自定義的操作(Set RenderTarget,draw mesh,Blit等)。這讓我們可以實現更多樣化的渲染效果。在介紹CommandBuffer的用法之前,先說一下攝像機的渲染順序。

這裡有兩個令人疑惑的地方:
1:Depth Texture:當你開啟Use Depth Texture,Unity會先渲染一遍場景物體,調用物體shader的中「ShadowCaster」 Pass,所以如果物體shader沒有ShadowCaster pass,將無法寫入深度圖中。渲染深度圖,無疑增加了不少Draw Call。所以我在想,為什麼不能直接從Depth Buffer中Copy一份DepthTexture出來用呢?或許你會說,在渲染物體之前,zbuffer裡面並沒有其對應的值。但是這讓我想到了Early-Z。

之前看API的時候,發現Unity的Depth Test居然是在fragment program之前執行的。這是不是表示這裡的Depth Test就是Early Depth Test技術呢。如果要實現Early-Z,必然需要將場景的物體用非常簡單的shader渲染一遍,以寫入ZBuffer中。如果Unity真的採用這種技術,就表示,在正常渲染場景之前,ZBuffer裡面已經有了整個場景的深度信息,所以Use Depth Texture,直接從Z-Buffer中拷貝出來不就好了么?幹嘛又要重新渲染一遍呢?這讓我很不解,有大佬明白的,求解惑。

2:SkyBox:SkyBox的的渲染順序居然在Opaque與Transparence之間,在FrameDebugger中查看,發現skybox的渲染順序確實如此。但是我們在寫skybox的shader時,明明寫的是"Queue = Background"。google了一番,在網上有人說Unity5為了實現procedural skybox,將其Render Queue固定在2500.5,所以無論你在shader中如何寫「Queue」都沒有作用。

The default skybox for Unity 5.0 is a procedural skybox that does physically based sky color calculations based on the sky materials atmospheric settings and scenes sun direction. However because its more expensive than the old "cube" style skybox having it render first is inefficient as it means the whole screen is calculating the procedural sky color, then getting drawn over, so instead Unity renders the skybox between the opaque queues (<=2500) and the transparent queues (>2500), effectively at queue "2500.5", regardless of the queue set in the shader or on the material. Its also rendered with a special high poly sphere mesh, designed to work with the procedural sky, and special transform matrix, designed to render the sphere such that its centered on the camera and scaled to the far draw distance. This means the skybox will render only where nothing has been written in the depth buffer.
The side effect of this is a lot of effects you could do with Unity 4 dont work with Unity 5 without some extra work. So while your observations of the behavior are accurate, this isnt a bug but intentional, even if it still isnt documented.
關於CommandBuffer的實現,後面有做出自己原創的效果再補充。
推薦閱讀:
※用頂點Shader實現的實時陰影
※計算機圖形學常用術語整理
※Procedure Cloud
※讓角色半透明:後期模糊(二)
※關於雜訊...
TAG:計算機圖形學 |
