Unity Shader之磨砂玻璃与水雾玻璃效果
導讀
玻璃效果是游戲場景中常見的效果之一,除卻普通的透明玻璃外,磨砂玻璃也是較為常見的效果。玻璃與場景中的其他物體也會有交互,例如,浴室中的玻璃、雨天的窗戶會在水汽的作用下帶有一定差別的霧效。本文以Unity Frosted Glass項目與開源庫中相關項目為例,介紹磨砂玻璃的做法和在移動端運行的性能。
開源庫鏈接:https://lab.uwa4d.com/lab/5b5613a3d7f10a201fd80bbb
模糊效果
磨砂玻璃的效果特點是模糊與半透明,該項目通過自定義的卷積實現來達到模糊效果。具體代碼實現在FrostedGlass.shader中。
//vertex to fragment struct v2f {float4 pos : POSITION;float2 uv : TEXCOORD0;float4 uv01 : TEXCOORD1;float4 uv23 : TEXCOORD2;float4 uv45 : TEXCOORD3; };v2f vert (appdata_img v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv.xy = v.texcoord.xy;o.uv01 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1);o.uv23 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 2.0;o.uv45 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 3.0;return o; }(頂點著色器及相關數據結構SeparableGlassBlur.shader)
offsets是一個float4類型的定值,表示偏移量。通過頂點著色器的運算,輸出的v2f結構體中,pos存儲了該頂點從Object Space轉換到相機裁剪空間中的齊次坐標。uv、uv01、uv23、uv45分別儲存了該頂點、偏移量為offsets的兩個頂點、偏移量為2offsets的兩個頂點、偏移量為3offsets的兩個頂點的uv坐標。
half4 frag (v2f i) : COLOR {half4 color = float4 (0,0,0,0);color += 0.40 * tex2D (_MainTex, i.uv);color += 0.15 * tex2D (_MainTex, i.uv01.xy);color += 0.15 * tex2D (_MainTex, i.uv01.zw);color += 0.10 * tex2D (_MainTex, i.uv23.xy);color += 0.10 * tex2D (_MainTex, i.uv23.zw);color += 0.05 * tex2D (_MainTex, i.uv45.xy);color += 0.05 * tex2D (_MainTex, i.uv45.zw);return color; }(片元著色器 SeparableGlassBlur.shader)
該卷積核的一維權重分布如下表所示:
(進行濾波操作 CommandBufferBlur.cs)_CommandBuffer.SetGlobalVector("offsets", new Vector4(2.0f / sizes[i].x, 0, 0, 0));_CommandBuffer.Blit(blurredID, blurredID2, _Material); _CommandBuffer.SetGlobalVector("offsets", new Vector4(0, 2.0f / sizes[i].y, 0, 0)); _CommandBuffer.Blit(blurredID2, blurredID, _Material);對圖像使用水平方向一維卷積核與豎直方向一維卷積核進行兩次濾波得到最終的圖像。等同于如下圖所示的二維卷積核進行濾波:
使用水平方向的一維卷積核對圖像進行濾波 CommandBufferBlur.cs
?
兩次濾波后得到模糊效果
卷積核的選擇有很多種,其中較為常用的有高斯模糊、kawase Blur,開源庫中有相關的項目實現了相關效果,例如:Blur for Unity、Gaussian Blur、Super Blur。
高斯模糊
這些模糊方式,所采用的卷積核各不相同,有興趣的讀者可以進行相關試驗。
捕捉屏幕紋理
實現模糊效果后,需要捕捉到玻璃后方的屏幕效果圖片交予模糊效果著色器進行處理。在Unity Shader中有一種特殊的Pass:GrabPass,可以很方便地獲取屏幕圖像。但這種方式開銷太大,并不適合在移動端運行。
該項目采取CommandBuffer來達到這一目的。可以節省開銷、提高性能。
//創建名為“Blur screen”的CommandBuffer _CommandBuffer = new CommandBuffer(); _CommandBuffer.name = "Blur screen";int screenCopyID = Shader.PropertyToID("_ScreenCopyTexture"); //新建一個臨時RenderTexture _CommandBuffer.GetTemporaryRT(screenCopyID, -1, -1, 0, FilterMode.Bilinear, _TextureFormat); //獲取當前屏幕圖像 _CommandBuffer.Blit(BuiltinRenderTextureType.CurrentActive, screenCopyID); int blurredID = Shader.PropertyToID("_Grab" + i + "_Temp1"); int blurredID2 = Shader.PropertyToID("_Grab" + i + "_Temp2"); _CommandBuffer.GetTemporaryRT(blurredID, (int)sizes[i].x, (int)sizes[i].y, 0, FilterMode.Bilinear, _TextureFormat); _CommandBuffer.GetTemporaryRT(blurredID2, (int)sizes[i].x, (int)sizes[i].y, 0, FilterMode.Bilinear, _TextureFormat); _CommandBuffer.Blit(screenCopyID, blurredID); _CommandBuffer.ReleaseTemporaryRT(screenCopyID);(獲取屏幕圖像 CommandBufferBlur.cs)
但此時獲取到的圖像是一整張屏幕圖象,而我們需要進行模糊處理的圖像,只有玻璃模型背后的圖像,這時我們需要在頂點著色器中對頂點進行處理。
v2f vert (appdata v) {v2f o;o.vertex = UnityObjectToClipPos(v.vertex);//確保材質球中的縮放和偏移位置正確o.uvfrost = TRANSFORM_TEX(v.uv, _FrostTex);//獲得該頂點在屏幕圖象中正確的紋理坐標o.uvgrab = ComputeGrabScreenPos(o.vertex);return o; }Unity內置的ComputeGrabScreenPos函數,幫助我們完成了坐標轉換,根據o.uvgrab在屏幕圖像中采集到的圖像信息,便是玻璃模型背后的屏幕圖像:
half4 ref00 = tex2Dproj(_GrabBlurTexture_0, i.uvgrab);將CommandBuffer掛載到相機上,實現實時更新渲染。
_Camera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _CommandBuffer);效果圖
水霧玻璃
在磨砂玻璃的基礎上,進一步拓展其他的特殊效果。
浴室中的玻璃、雨天的窗戶會在水汽的作用下帶有一定差別的霧效。這種效果的特點除卻模糊與半透明外,還會有模糊程度的差別。
Vector2[] sizes = {new Vector2(Screen.width, Screen.height),new Vector2(Screen.width / 2, Screen.height / 2),new Vector2(Screen.width / 4, Screen.height / 4),new Vector2(Screen.width / 8, Screen.height / 8), };(定義不同大小的偏移量 CommandBufferBlur.cs)
定義不同大小的偏移量,通過SeparableGlassBlur.shader的運算,得到四張模糊程度不同的屏幕圖像,由0~3模糊程度加深:
sampler2D _GrabBlurTexture_0;sampler2D _GrabBlurTexture_1;sampler2D _GrabBlurTexture_2;sampler2D _GrabBlurTexture_3;制定采樣規則從這四張模糊程度不同的屏幕圖像中進行采集,為了讓過渡圓滑,使用lerp函數進行插值。加載一張效果灰度圖(_FrostTex),確定霧效差別的強度(_FrostIntensity):
fixed4 frag (v2f i) : SV_Target {float surfSmooth = 1 - tex2D(_FrostTex, i.uvfrost)* _FrostIntensity;//如果x 值小于 a,則返回a;如果 x 值大于 b,返回b;否則,返回 xsurfSmooth = clamp(0, 1, surfSmooth);half4 refraction;//二維紋理投影映射half4 ref00 = tex2Dproj(_GrabBlurTexture_0, i.uvgrab);half4 ref01 = tex2Dproj(_GrabBlurTexture_1, i.uvgrab);half4 ref02 = tex2Dproj(_GrabBlurTexture_2, i.uvgrab);half4 ref03 = tex2Dproj(_GrabBlurTexture_3, i.uvgrab);//進行平滑過渡float step00 = smoothstep(0.75, 1.00, surfSmooth);float step01 = smoothstep(0.5, 0.75, surfSmooth);float step02 = smoothstep(0.05, 0.5, surfSmooth);refraction = lerp(lerp(lerp(ref03, ref02, step02), ref01, step01), ref00, step00); return refraction; }(片元著色器 FrostedGlass.shader)
項目中以_FrostTex圖像中r值作為采樣依據。根據1-r值*_FrostIntensity得到的數值(surfSmooth)為權重,分別從上述四張模糊程度不同的屏幕圖像中進行采集,最后得到該頂點最終的顏色。
以下圖作為效果圖,驗證不同的surfSmooth大小,渲染得到不同的模糊程度:
從左至右R值依次為:0,0.25,0.5,0.75,1
效果圖
由此可以模擬出不同的水霧玻璃效果:
有點臟的霧玻璃
覆蓋著一層小水珠
性能測試 (使用UWA GOT Online工具測評)
選擇低端機型紅米4x進行測試(不開啟多線程渲染):
使用水霧玻璃效果:
FPS均值為26幀:
Camera.Render函數耗時11ms左右開銷不大:
可以看到CPU等待GPU渲染完成時間較長,當前渲染壓力在GPU端:
可見實時抓取屏幕圖片并進行渲染操作在移動端開銷還是巨大的。該效果生成四張模糊效果圖片,每張圖片的生成通過了兩次SeparableGlassBlur.shader的計算。最終每個頂點在FrostedGlass.shader中進行運算。GPU計算量過大。
只使用磨砂玻璃效果時,紅米4xFPS均值為42幀左右。
此時只生成一張模糊效果圖片,該圖片的生成通過了兩次SeparableGlassBlur.shader的計算。相比較而言GPU端計算大幅減少,CPU等待時間縮短,性能提升明顯。
故而開發者在實現此效果時,需要在性能與效果之間平衡,盡可能減少計算量,例如可以使用3x3的卷積核,而不是5x5的,可以在采樣之前做判斷,減少采樣次數。
今天的推薦就到這兒啦,或者它可直接使用,或者它需要您的潤色,或者它啟發了您的思路......
總結
以上是生活随笔為你收集整理的Unity Shader之磨砂玻璃与水雾玻璃效果的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html在图片上半透明磨砂,有没有办法在
- 下一篇: 磨砂玻璃效果