之前一直曉得可以用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】alex.vlachos.com/graphi

【3】Texture Distortion

推薦閱讀:

相关文章