什么是Flowmap
flowmap的实质是张记录了2D向量信息的纹理,Flow map上的颜色( 通常为RG通道)记录该处向量场的方向,让模型上某一点表现出定量流动的特征。通过在shader中偏移uv再对纹理进行采样,来模拟流动效果。Flowmap的特征是周期性变化。
UV映射:UV坐标用于查找纹理的颜色值。UV贴图(用于理解) :使用(R, G)颜色通道表示坐标:黑色(0,0) ,绿色处(0,1),红色处(1,0),黄色处(1,1)。UV贴图上颜色相同处: 意味着采样纹理时使用了同一位置。
为什么使用Flowmap
类似UV动画,而非顶点动画。换言之,无需对模型顶点进行操作,易实现,运算开销小。不仅仅是水面,任何和流动相关的效果都可以采用flowmap。
Flowmap Shader
1.采样Flow map获取向量场信息
2.用向量场信息,使采样贴图时的UV随时间变化
3.对同一贴图以半个周期的相位差采集两次,并线性插值,使贴图流动连续
最简单的随时间偏移 UV - time
先来看看uv+time的情况:(u,v) + (time,0) ,模型上某个点:随着time增加,采样到的像素越远,视觉上可以形容为:更远距离的像素偏移向该点,视觉效果和我们直观认识到的运算法则是相反的。
UV值作为向量(u,v) ,自然也遵循向的运算法则。但UV偏移时,改变的不是顶点的位置。
flow map不能直接使用,要将flow map.上的色值从[0,1 ]的范围映射到方向向量的范围[-1,1]。
//从flowmap获取流向
float3 flowDir = tex2D(_FlowMap, i.uv) * 2.0 - 1.0;
随着时间的进行,UV偏移会越来越夸张,为了把偏移控制在一定范围内,需要构造两个周期相同,相位相差半个周期的波形函数。
//构造周期相同,相位相差半个周期的波形函数
float phase0 = frac( Time * 0.1 * TimeSpeed ) ;
float phasel = frac( Time * 0. 1 * TimeSpeed + 0. 5) ;
为了得到无缝循环的流动,利用相位差半个周期的两层采样进行加权混合,使纹理流动一个周期重新开始时的不自然情况被另一层采样覆盖。
//平铺贴图用的uv
float2 tiling_ _uv = i. uV * MainTex_ ST. xy +_ MainTex ST. zw;
//用波形函数周期化向量场方向,用偏移后的uv对材质进行偏移采样
half3 tex0 = tex2D( MainTex, tiling_uv - flowDir. xy * phase0) ;
half3 tex1 = tex2D( MainTex, tiling_uv - flowDir. xy * phase1) ;
//构造函数计算随波形函数变化的权值,使得MainTex采样值在接近最大偏移时有权值为0,并因此消隐,构造较平滑的循环
float flowLerp = abs((0.5 - phase0) / 0.5);
half3 fina1Color = lerp(tex0, tex1, flowLerp) ;
Shader "FX/FlowMap"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("Tint", Color) = (1,1,1,1)
_FlowMap("FlowMap", 2D) = "white" {}
_FlowSpeed("intensity", float) = 0.1
_TimeSpeed("speed", float)= 1
[Toggle]_reverse_flow("reverse", Int) = 0
}
SubShader
{
Tags { "RenderType"="Opaque"
"IgnoreProjector" = "True"
"RenderType" = "Opaque"
}
Cull Off
Lighting Off
ZWrite On
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma shader_feature _REVERSE_FLOW_ON
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
fixed4 _Color;
sampler2D _MainTex;
sampler2D _FlowMap;
float4 _MainTex_ST;
float _FlowSpeed;
float _TimeSpeed;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
//UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//0~1 *2-1 得到 -1~1 方向
fixed4 flowDir = tex2D(_FlowMap, i.uv) * 2.0 - 1.0;
//强度修正
flowDir *= _FlowSpeed;
//正负修正
#ifdef _REVERSE_FLOW_ON
flowDir *= -1;
#endif
//两个0~1循环 计时
float phase0 = frac(_Time * 0.1 * _TimeSpeed);
float phase1 = frac(_Time * 0.1 * _TimeSpeed + 0.5);
float2 tiling_uv = i.uv * _MainTex_ST.xy + _MainTex_ST.zw;
half3 tex0 = tex2D(_MainTex, tiling_uv + flowDir.xy * phase0);
half3 tex1 = tex2D(_MainTex, tiling_uv + flowDir.xy * phase1);
float flowLerp = abs((0.5 - phase0) / 0.5);
half3 finalColor = lerp(tex0, tex1, flowLerp);
return float4(finalColor, 1.0) * _Color;
}
ENDCG
}
}
}
Flowmap的制作
1. Flowmap Painter
unity制作的绘制Flowmap的工具,用该工具得到的flowmap为线性空间下的颜色,不需要gamma矫正,Unity中请取消勾选 “SRGB”。
flowmap贴图设置:
1.无压缩或高质量
2.确认色彩空间
导出时注意gamma校正选项、UV匹配:用Labs UV transfer节点来匹配高模和低模的UV
2. Houdini Labs
Houdini Labs是内置在houdini中的一组游戏开发相关的节点,可以在github中搜索sidefx Labs或着直接在houdini中安装得到。在较早版本的houdini中无法在shelf内找到该工具,你只能通过github下载。在这些未被内置到houdini的版本中, 这组工具的名称为gamedev。