轉自:http://blog.sina.com.cn/s/blog_89d90b7c0102vaqy.html
熱空氣扭曲在大自然中形成是比較復雜的,這里只是通過取屏幕紋理和移動UV來模擬熱扭曲效果。
先看效果:
詳細的原理和實現在下面的代碼中。
Shader "Xffect/my_distortion" { Properties { _NoiseTex ("絮亂圖", 2D) = "white" {} // 絮亂圖 _AreaTex ("區域圖(Alpha):白色為顯示區域,透明為不顯示區域", 2D) = "white" {} // 區域圖 _MoveSpeed ("絮亂圖移動速度", range (0,1.5)) = 1 // 絮亂圖移動速度 _MoveForce ("絮亂圖疊加后移動強度", range (0,0.1)) = 0.1 // 絮亂圖疊加強度,多張運動紋理疊加后再相乘的系數 } Category { // 【渲染隊列】在透明物體前,類型為【透明】 Tags { "Queue"="Transparent+1" "RenderType"="Transparent" } // 最終透明混合 = 貼圖RGB*貼圖A + 背景RGB*(1-貼圖A) // 透明混合【源的A值】【1-SrcAlpha】 Blend SrcAlpha OneMinusSrcAlpha // 該寫法為最常用最真實的透明混合顯示,半透明圖的正常顯示 // GEuqal 點的alpha值大於等於0.01時渲染 AlphaTest Greater .01 // 在PS區域圖時,不顯示的地方透明度為0即可。 // 關閉剔除,關閉燈光,不記錄深度 Cull Off Lighting Off ZWrite Off SubShader { GrabPass { Name "BASE"//在后續的通道中可以使用給定的名字來引用這個紋理。當你在1個場景中有多個對象使用grab pass 時候,這樣做會提高效率。 Tags { "LightMode" = "Always" } } Pass { Name "BASE" Tags { "LightMode" = "Always" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" struct appdata_t { float4 vertex : POSITION; // 輸入的模型坐標頂點信息 float2 texcoord: TEXCOORD0; // 輸入的模型紋理坐標集 }; struct v2f { float4 vertex : POSITION; // 輸出的頂點信息 float4 uvgrab : TEXCOORD0; // 輸出的紋理做標集0 float2 uvmain : TEXCOORD1; // 輸出的紋理坐標集1 }; float _MoveSpeed; // 聲明絮亂圖移動速度 float _MoveForce; // 聲明運動強度 float4 _NoiseTex_ST; // 絮亂圖采樣 float4 _AreaTex_ST; // 區域圖采樣 sampler2D _NoiseTex; // 絮亂圖樣本對象 sampler2D _AreaTex; // 區域圖樣本對象 sampler2D _GrabTexture; // 全屏幕紋理的樣本對象,由GrabPass賦值 v2f vert (appdata_t v) { v2f o; // 從模型坐標-世界坐標-視坐標-(視覺平截體乘以投影矩陣並進行透視除法)-剪裁坐標 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); // 將裁剪坐標中的【頂點信息】進行換算給uvgrab賦值 #if UNITY_UV_STARTS_AT_TOP // Direct3D類似平台scale為-1;OpenGL類似平台為1。 float scale = -1.0; #else float scale = 1.0; #endif o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y * scale) + o.vertex.w) * 0.5; o.uvgrab.zw = o.vertex.zw; // 區域圖紋理:獲取輸入的紋理坐標集,並且使用_MainTex_ST采樣圖,支持在視檢器調節縮放和偏移值 o.uvmain = TRANSFORM_TEX(v.texcoord, _AreaTex); return o; } half4 frag( v2f i ) : COLOR { // 控制【UV的運動】,這樣在進行采樣時,offsetColor1拿到的顏色也是運動的。 half4 offsetColor1 = tex2D(_NoiseTex, i.uvmain + _Time.xz * _MoveSpeed);// 將xy與xz交叉位移 half4 offsetColor2 = tex2D(_NoiseTex, i.uvmain - _Time.yx * _MoveSpeed);// 將xy與yx交叉位移 // 將【正在移動的絮亂圖紋理信息】的rg用於給uvgrab累加,加2個col就會出現2個絮亂圖紋理 i.uvgrab.x += ((offsetColor1.r + offsetColor2.r) - 1) * _MoveForce; // 疊加強度 i.uvgrab.y += ((offsetColor1.g + offsetColor2.g) - 1) * _MoveForce; // 本來只會顯示物體背后的屏幕紋理(視覺上該物體透明了) // 但是上面給x,y疊加了運動的rg值,所以就形成透明絮亂圖運動的效果 half4 noiseCol = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uvgrab)); // 屏幕紋理不需要透明,所以設置為1。 noiseCol.a = 1f; // 對區域圖進行采樣。 half4 areaCol = tex2D(_AreaTex, i.uvmain); // 紋理相乘:區域紋理RBG都為1,區域紋理A為O的像素將不會顯示 // 即可達到絮亂圖在區域圖中才顯示的效果。 return noiseCol * areaCol; } ENDCG }//end pass }//end subshader // 用於老式顯卡 SubShader { Blend DstColor Zero Pass { Name "BASE" SetTexture [_MainTex] { combine texture } } } } } 配合粒子的發射,就可以在刀光上實現熱扭曲效果等。
注:
此shader在部分手機上會有問題,是因為對grabpass的支持不夠,
這里可以采用攝像機獲取屏幕紋理的方式代替_GrabTexture。
原理在http://blog.sina.com.cn/s/blog_89d90b7c0102va4m.html的最下面。