之前一直曉得可以用FlowMap實現一些靜態的水流效果,但是一直沒有機會去用。不過最近在定完人物和車輛的效果之後,又要著手研究下主城的表現,所以順帶把FlowMap的實現備忘一下。
FlowMap是RG通道(不一定非得RG)上存儲了水平方向上流動方向的圖,可以簡單理解為一個矢量場的可視化,我用的FlowMap是使用FlowMap Painter【1】製作的。
導入的時候注意一下設置,不使用標準RGB空間,也不使用壓縮(當然等下在移動端用的時候還是得改改,到時候看吧):
目前為止看到的大部分FlowMap的使用,可能都來自於這篇Valve關於傳送門2中水流的分享【2】,我知道很多人不會去看的,所以就簡單扼要的總結下。
其實SIGGRAPH10上的這篇文章簡單來說就是這幾點:使用法線表現波紋;美術烘出FlowMap;在片元著色器上使用FlowMap擾亂法線貼圖。
美術的內容暫時不談,關鍵在於這頁:
也就是使用兩個Layer交替呈現,畢竟對於FlowMap給出的FlowVector,你不能順著一個方向隨著時間遞增一直讀讀讀,那樣整個畫面就支離破碎了。所以這邊的方法就是截斷在某個時間區域內,然後用兩層Layer分別來展示。
不過Valve這篇分享還有個比較局限的地方就是他們只用FlowMap來表達整個地圖中水流大致的方向,因此解析度要求並不高(4texels/meter)。因此我覺得要想實現更好的效果,估計還是得引入Detailed FlowMap。這裡我沒寫,畢竟移動端多讀一次貼圖我都感覺罪大惡極……
float3 GetUVW(float2 uv, float2 flowVec, float time, float interval, bool b) { float3 res; if (b) { time += 0.5 * interval; uv.x += 0.5; } float progress = frac(time / interval); res.xy = uv - flowVec * progress; res.z = 1 - abs(1 - progress * 2); return res; }
void surf (Input IN, inout SurfaceOutputStandard o) { float2 flow = tex2D(_FlowMap, IN.uv_MainTex).rg * 2 - 1; float3 uvw1 = GetUVW(IN.uv_MainTex, flow, _Time.y, _Interval, false); float3 uvw2 = GetUVW(IN.uv_MainTex, flow, _Time.y, _Interval, true); // Albedo comes from a texture tinted by color fixed4 c1 = tex2D (_MainTex, uvw1.xy) * _Color; fixed4 c2 = tex2D (_MainTex, uvw2.xy) * _Color; o.Albedo = c1.rgb * uvw1.z + c2.rgb * uvw2.z; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c1.a * uvw1.z + c2.a * uvw2.z; o.Normal = UnpackNormal(tex2D(_NormalMap, uvw1.xy)) * uvw1.z + UnpackNormal(tex2D(_NormalMap, uvw2.xy)) * uvw2.z; }
當然,Valve的分享不可能這麼簡單。接下來的內容還提到了他們是如何引入Noise去消除水面上的重複感的,以及對污水上使用FlowMap的改進。有興趣的話可以前往原文拜讀,順便也可以看看【3】,寫得事無巨細,包您滿意。
【1】FlowMap Painter | teckArtist
【2】http://alex.vlachos.com/graphics/Vlachos-SIGGRAPH10-WaterFlow.pdf
【3】Texture Distortion
推薦閱讀: