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