使用Unity製作起霧的窗戶效果著色器
- 高斯模糊效果
- 讀寫紋理
- 根據紋理修改模糊效果
我們在每一部分的最後會提供可以使用的著色器,你可以從中學習方法,以便在製作其它著色器時重用或改寫。
獲取實現水霧窗戶效果的著色器程式碼:
https://github.com/lindenreid/Unity-Shader-Tutorials/blob/master/Assets/Materials/Shaders/window.shader
高斯模糊
窗戶的起霧效果通過高斯模糊和往上面新增輕微的著色來實現。
我們會通過使用GrabPass標籤,獲取已經在攝像機渲染的窗戶後面的畫素,然後對這些畫素應用高斯模糊演算法。
很多文章講解過高斯模糊的實現原理,本文使用了《GLSL程式碼的高斯模糊教程》來編寫自定義的著色器:
https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson5
使用GrabPass
我們需要獲取窗戶後面的畫素,以便對其進行模糊處理,我們可以使用Unity的GrabPass。
GrabPass將在物件後渲染的畫素繪製到著色器可以訪問的紋理上。使用該渲染批次時,我們需要使用SubShader程式碼塊中的GrabPass標籤。
- SubShader
- {
- //繪製透明窗戶也很重要
- Tags
- {
- "Queue" = "Transparent"
- }
- //將物件後的螢幕內容抓取到_BGTex中
- GrabPass
- {
- "_BGTex"
- }
- // … 其它著色器程式碼…
接下來,在CGPROGRAM標籤中,確保已經包含Unity.cginc檔案,從而使用讀取GrabPass的特別函式。
- #include "UnityCG.cginc"
為了能夠讀取GrabPass紋理,我們需要合適的紋理座標。
Unity通過ComputeScreenGrabPos函式非常簡單的獲得座標,只要將剪輯空間頂點位置作為輸入提供,該函式就能夠給出合適的紋理座標來讀取GrabPass紋理。我們可以在頂點著色器中進行這項計算。
- vertexOutput output;
- output.pos = UnityObjectToClipPos(input.vertex);
- output.grabPos = ComputeGrabScreenPos(output.pos);
應用模糊效果
現在,我們可以在片元著色器Fragment Shader中讀取紋理,然後應用模糊效果。
我們通過以下引數編寫模糊演算法。
- float4 gaussianBlur(
- float2 dir,
- float4 grabPos,
- float res,
- sampler2D tex,
- float radius
- )
- {
- //模糊演算法在此編寫
- }
該演算法會獲取GrabPass紋理,即“tex”,應用模糊效果,並返回float4型別的畫素顏色。
下面介紹每個引數的含義:
float2 dir:模糊效果將應用於兩個通道,所以我們需要“dir”即方向引數。效果會在X方向和Y方向各應用一次,因為我們使用(1,0)表示X方向,(0,1)表示Y方向,所以獲得的是Float2型別。
float4 grabPos:grabPos變數表示模糊畫素的紋理座標。
float res:res變數表示X軸和Y軸上的紋理解析度。
sampler2D tex:該變數表示要模糊的紋理。我們需要整個紋理,因為模糊演算法會對原始畫素附近的畫素進行取樣。
float radius:該變數表示從原始畫素到模糊位置的距離。數值越大,模糊效果越強。
接下來,讓我們定義控制模糊效果所需的引數。
我們需要一個浮點數定義模糊強度,我們將其定義為_BlurRadius,並在著色器程式碼的開始將該變數公開給屬性塊的材質。
我們還需要GrabPass紋理,該紋理的名稱要和GrabPass標籤中的名稱相同,本示例中為_BGTex。我們可以通過建立_YourTextureName_TexelSize屬性來獲取需要紋理的大小資訊。
我們給模糊效果加入了深藍色著色,使效果更明顯。如果想使用該顏色,請新增顏色到屬性中,我們將其命名為_FogColor。
- //屬性
- //在材質設定
- uniform float4 _FogColor;
- uniform float _BlurRadius;
- //獲取通道
- uniform sampler2D _BGTex;
- uniform float4 _BGTex_TexelSize;
現在,我們得到了將模糊效果應用到背景紋理的所需資訊。
我們打算將模糊效果應用到兩個渲染批次:一個在X方向,另一個在Y方向。通常,我們會讓第二個模糊渲染批次處理第一個模糊的結果,而不是處理原始背景影象。但這樣需要更多著色器,過程也會更復雜。
所以,我對模糊的處理比較簡單,在兩個方向模糊了原始影象並新增效果。該方法的缺點是:1、模糊的質量較低。2、模糊部分比原始影象更亮,因為新增了效果。
我們將模糊部分乘以著色顏色。請注意,_TexelSize在.zw屬性中包含紋理的xy大小。
- float4 blurX = gaussianBlur(float2(1,0),
- input.grabPos,
- _BGTex_TexelSize.z,
- _BGTex,
- _BlurRadius);
- float4 blurY = gaussianBlur(float2(0,1),
- input.grabPos,
- _BGTex_TexelSize.w,
- _BGTex,
- _BlurRadius);
- return (blurX + blurY) * _FogColor;
效果
我們將該著色器應用到材質上,並將其附加到場景的一個平坦表面上,現在著色器實現了基本的模糊效果。
下圖是起霧窗戶效果的預覽。
讀寫紋理
為了按照滑鼠互動改變著色器效果,我們需要將滑鼠移動寫入紋理,並在著色器讀取該紋理。
從著色器讀取紋理
首先,我們在著色器中建立名為_MouseMap的sampler2D屬性。
- uniform sampler2D _MouseMap;
在片元著色器中,繪製該紋理以便除錯。
- float4 mouseSample=tex2D(_MouseMap,input.texCoord.xy);
以上就是片元著色器的功能,用於實現紋理的讀寫過程。對_MouseMap屬性進行編寫前,我們將得到不透明灰色平面,如下所示。
使用C#程式碼寫入紋理
為了寫入紋理,我們需要建立C#指令碼,並將指令碼附加到平面。
我們可以通過C#程式碼的Material.Set函式,設定著色器屬性。只需要讓屬性的字串名稱對應在著色器的對應名稱即可。
- void OnMouseDrag ()
- {
- //從滑鼠位置向螢幕建立光線
- //然後測試對紋理的碰撞效果
- Ray ray = cam.ScreenPointToRay(Input.mousePosition);
- RaycastHit hit;
- if(Physics.Raycast(ray, out hit, 100))
- {
- Color color = new Color(1, 0, 0, 1);
- //把紋理座標轉換為畫素座標
- int x = (int)(hit.textureCoord.x * texture.width);
- int y = (int)(hit.textureCoord.y * texture.height);
- //寫入被碰到的畫素
- texture.SetPixel(x, y, color);
- //寫入Radius範圍內的相鄰畫素
- for (int i = 0; i < texture.height; i++)
- {
- for (int j = 0; j < texture.width; j++)
- {
- float dist = Vector2.Distance(new Vector2(i,j),
- new Vector2(x,y)
- );
- if(dist <= Radius)
- texture.SetPixel(i, j, color);
- }
- }
- //應用改動並告知著色器
- texture.Apply();
- destinationRenderer.material.SetTexture("_MouseMap", texture);
- }
- }
我們為BlurColor選取了黑色,所以執行時場景效果如下圖所示。
寫入滑鼠位置
現在,我們新增一個OnMouseDrag()函式,當玩家點選划動平面時,在滑鼠位置周圍繪製圓圈。請將MeshCollider元件附加到平面物件,使它接收OnMouseDrag()事件。
現在執行遊戲,我們應該能在紋理上使用滑鼠進行繪圖了。
根據紋理修改模糊效果
現在,我們可以根據剛建立的滑鼠拖動紋理來改變模糊效果。
根據滑鼠拖動紋理應用模糊效果
我們返回到著色器部分,根據從紋理讀取的數值應用模糊部分。由於我們在滑鼠點選的位置繪製了紅色,而且紋理預設是黑色,因此我們可以根據紅色通道修改模糊和著色量。
我們要進行以下乘法。
- _BlurRadius * (1 - red channel)
由於紅色通道的數值在0~1之間,因此紅色數值越大,模糊的半徑越小。這種情況下,紅色通道會是0或1,所以它會在紅色繪製的位置移除模糊效果。
著色顏色同理,只不過需要在未應用起霧效果的部分定義_ClearColor。
- // r = 1表示滑鼠點選
- // r = 0表示沒有滑鼠操作
- float blurRadius = _BlurRadius * (1-mouseSample.r);
- float4 color = mouseSample.r*_ClearColor + (1.0-mouseSample.r)*_FogColor;
- float4 blurX = gaussianBlur(float2(1,0),
- input.grabPos,
- _BGTex_TexelSize.z,
- _BGTex,
- blurRadius);
- float4 blurY = gaussianBlur(float2(0,1),
- input.grabPos,
- _BGTex_TexelSize.w,
- _BGTex,
- blurRadius);
- return (blurX + blurY) * color;
現在,我們可以在視窗進行繪製,點選的位置將消退模糊和著色效果。
我們已經得到了不錯的窗戶起霧著色器。但是為什麼不做的更復雜一些呢?
時間演算法
在處理著色器前,請思考一下演算法的原理。基本上,我們需要根據點選指定畫素的時間,來修改模糊量。畫素時間值越小,表示它被點選的時間越近,因此起霧效果較小。
我們還需要最大持續時間來定義畫素恢復起霧效果的速度。該值將用於把時間轉換為標準化數值,即0~1,用於調整最小值和最大值之間的模糊量。
演算法如下所示。
- age = current time - time drawn
- percent max age = age / max age
然後,我們將標準化的“percent max age”值應用到模糊半徑和著色。畫素時間值越小,百分比最大持續時間越小,從而使模糊強度越小。
類似地,我們會根據percent max age值,使用較小的著色顏色量和較大的清晰顏色。
- blur radius = max radius * percent max age
- tint = (1 - percent max age)*(clear color) + (percent max age)*(fog color)
應用時間
為了將其應用於著色器,首先我們將畫素繪製時間寫入滑鼠貼圖紋理的r通道,而不是隻寫入1.0。
- Color color = new Color(Time.timeSinceLevelLoad, 0, 0, 1);
接下來,在著色器應用之前的演算法,獲取percent max age值。
- //從滑鼠點選紋理獲取畫素繪製的時間
- float timeDrawn = tex2D(_MouseMap, input.texCoord.xy).r;
- //時間 = 當前時間 - 繪製時間
- float age = clamp(_Time.y - timeDrawn, 0.0001, _Time.y);
- //百分比最大時間 = 時間/最大時間
- float percentMaxAge = saturate(age / _MaxAge);
最後,我們將percent max age值應用到模糊半徑和著色顏色。
- // 時間越長表示百分比最大時間越大,從而有更大的模糊效果
- float blurRadius = _BlurRadius * percentMaxAge;
- float4 color = (1-percentMaxAge)*_ClearColor + percentMaxAge*_FogColor;
現在,模糊效果會根據定義的最大持續時間進行恢復。如下圖所示,我們將_MaxAge設為1秒,使模糊效果快速淡化。
結語
本教程介紹瞭如何將顏色之外的資訊編碼到紋理中,以及如何利用該方法實現不錯的效果。
獲取水霧窗戶效果的著色器程式碼:
https://github.com/lindenreid/Unity-Shader-Tutorials/blob/master/Assets/Materials/Shaders/window.shader
作者:Linden Reid
來源:Unity官方平臺
原地址:https://mp.weixin.qq.com/s/cLyCGg6h9WGkIk5Xj4KU1w
相關文章
- unity製作刮刮樂效果Unity
- 使用Unity著色器實現精靈(Sprite)塗鴉效果Unity
- 揭祕《Sherman》:使用Unity製作影視級光照效果Unity
- 使用Unity製作遊戲AIUnity遊戲AI
- JavaScript+DIV製作彈出警告視窗效果JavaScript
- Unity Shader之磨砂玻璃與水霧玻璃效果Unity
- 使用jquery製作彈出框效果jQuery
- Cardboard開發教程:使用Unity製作Cardboard全景圖片瀏覽器Unity瀏覽器
- Unity製作遊戲中的場景Unity遊戲
- 一種巧妙的使用 CSS 製作波浪效果的思路CSS
- 在WPF中使用著色器
- Unity製作一個小星球Unity
- Unity製作特寫鏡頭Unity
- Unity 2D遊戲製作Unity遊戲
- 如何用Unity GUI製作HUDUnityGUI
- 在Qt裡使用QSplashScreen類製作Splash啟動視窗QT
- 電話機器人的話術怎麼製作效果好機器人
- ppt製作的翻書效果及教程
- PopupWindow使用詳解(二)Popwindow製作常見花哨效果
- CSS製作水滴波浪效果案例CSS
- css製作下拉框效果CSS
- 使用ZBrush製作女性機器人的操作流程ZBrush機器人
- visualstudio著色器設計器shadergraph使用
- 使用CSS3製作導航條和毛玻璃效果CSSS3
- WebGL:使用著色器進行幾何造型Web
- Unity3D模型製作規範[轉]Unity3D模型
- Canvas + JavaScript 製作圖片粒子效果CanvasJavaScript
- 離屏Canvas——製作放大鏡效果Canvas
- OpenGL著色器教程
- three.js 著色器材質之初識著色器JS
- 解除DLSupCBT的NAG窗和KEY檔案製作(一)
- Premiere影片重影效果怎麼做?Premiere製作影片重影效果的方法REM
- 純CSS製作液晶屏的顯示效果CSS
- CSS3滾動視差效果的製作技巧CSSS3
- 有了智慧防霧鏡,洗澡時再也不怕鏡子起霧
- Unity遊戲積分/計分系統製作方法Unity遊戲
- 使用jquery製作日曆(生日)選擇器jQuery
- 用unity製作簡單的太空遊戲(2)-簡單炮臺Unity遊戲