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的兩個頂點、偏移量為2*offsets的兩個頂點、偏移量為3*offsets的兩個頂點的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;否則,返回 x
- surfSmooth = 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的,可以在取樣之前做判斷,減少取樣次數。
快用UWA Lab合輯Mark好專案!
今天的推薦就到這兒啦,或者它可直接使用,或者它需要您的潤色,或者它啟發了您的思路......
請不要吝嗇您的點贊和轉發,讓我們知道我們在做對的事。當然如果您可以留言給出寶貴的意見,我們會越做越好。
來源:UWA
原地址:https://blog.uwa4d.com/archives/UWALab_UnityShader.html
相關文章
- 線上直播原始碼,CSS磨砂玻璃效果和漸變主題色文字原始碼CSS
- Unity——ShaderLab實現玻璃和鏡子效果Unity
- Unity Shader 實現雨天的水面漣漪效果Unity
- Unity Shader 00 - 梳理 Unity Shader 的基本結構Unity
- Unity Shader基於視差對映的雲海效果Unity
- 使用Unity製作起霧的窗戶效果著色器Unity
- 毛玻璃效果
- 聊天平臺原始碼,背景顯示使用仿磨砂玻璃樣式原始碼
- CSS:遮罩效果、陰影效果、毛玻璃效果CSS遮罩
- Unity Shader之雙面材質和多Pass渲染Unity
- 純CSS 毛玻璃效果CSS
- unity 統一替換shaderUnity
- Re:《Unity Shader入門精要》13.3全域性霧效--如何從深度紋理重構世界座標Unity
- 《Unity Shader入門精要》自學筆記(五)第八章 透明效果Unity筆記
- unity shader 溶解,上下左右,cutoffUnity
- 【Unity Shaders】Mobile Shader Adjustment—— 什麼是高效的ShaderUnity
- 【Unity3D Shader程式設計】之二 雪山飛狐篇:Unity的基本Shader框架寫法&顏色、光照與材質Unity3D程式設計框架
- 用ps做毛玻璃穿透效果穿透
- 鄭州iOS·點-毛玻璃效果iOS
- Unity Shader 入門精要 筆記(1)Unity筆記
- Unity 的 Surface Shader有關記錄Unity
- Unity3D 透明物體ShaderUnity3D
- 【Unity Shader入門精要】— 高階篇Unity
- css毛玻璃效果(外加background屬性)CSS
- Android 毛玻璃效果的實現Android
- Unity3D Shader官方教程翻譯(三)----Shader語法:屬性Unity3D
- Unity3D Shader官方教程翻譯(四)----Shader語法:SubShaderUnity3D
- Unity3D學習筆記3——Unity Shader的初步使用Unity3D筆記
- 毛玻璃效果在Android的實現Android
- 使用Glide快速實現毛玻璃效果IDE
- 使用iOS 8 SDK新增毛玻璃(Blur)效果iOS
- Unity&Shader常用函式的使用方法Unity函式
- Unity Shader-後處理:Bloom全屏泛光UnityOOM
- Unity3D Shader官方教程翻譯(一)Unity3D
- Unity3D Shader官方教程翻譯(二)Unity3D
- Unity2D橫板遊戲之背景視差與無限滾動效果Unity遊戲
- CSS 奇思妙想 | 全相容的毛玻璃效果CSS
- IOS8模糊毛玻璃的效果UIVisualEffectViewiOSUIView