之前一直晓得可以用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

推荐阅读:

相关文章