图形 2.8 flowmap的实现——流动效果实现
flowmap的实现——流动效果实现
FlowMap概述
什么是FlowMap?FlowMap是Valve在2010年的GDC(游戏开发者大会, Game Developers Conference)中,介绍的他们在求生之路2和传送门2中用来实现水面流动效果的技术,这种方式因为它原理简单,容易实现,而且运算量比较少,所以到现在都还在被使使用,这个技术使用了一张被称为flowmap的贴图,用它来模拟场景中水面的流向。
Flowmap的实质就是一张记录了2D向量信息的纹理 Flow map上的颜色(通常为RG通道)记录该处向量场的方向,让模型上某一点表现出定量流动的特征。通过在shader中偏移uv再对纹理进行采样,来模拟流动效果。
右图就是表示颜色色值和它对应的方向的关系:
我们知道纹理映射的原理,使用(R,G)颜色通道表示坐标:黑色(0,0),绿色处(0,1),红色处(1,0),黄色处(1,1),这些位置分别对应贴图的采样位置,因此uv贴图上颜色相同的地方就意味着采样到了同一处纹理。
而我们的Flowmap则是通过它上面所带有的向量场信息对uv进行了一个偏移来干扰采样纹理的这个过程。(注意UE4中的uv坐标,它反转了绿通道)
那我们为什么要去使用这样一个FlowMap的技术呢?因为它仅仅只是修改我们在采样纹理时用到的uv,而没有对顶点进行操作,即它是一种非常廉价和高效的达到流动效果的一种方法,易实现,运算开销小。实际上不仅仅是水面,任何和流动相关的效果都可以采用flowmap。
Flowmap Shader
我们可以借助Shader来理解Flowmap,一个Flowmap shader首先要从Flowmap贴图中采样去得到向量场的信息,然后再用向量场的信息对uv进行偏移,而且要使采样贴图的uv完成一种随时间周期性变化无缝循环的效果,即对同一贴图以半个周期的相位差采集两次并线性插值,使贴图流动连续。
首先确认流动的方向,由于贴图的色值是[0,1]的,所以我们要映射到[-1,1]。
//从flowmap获取流向 float3 flowDir = tex2D(_FlowMap, i.uv) * 2.0 - 1.0;
如果我们直接让它根据时间变换,会因为随着时间进行从而使变形越来越夸张,因此我们为了把偏移控制在一定范围内,这里我们使用frac()来取Time的小数部分。
//常用函数frac,返回浮点数的小数部分 float phase = frac(_Time);
但是我们用frac取了小数部分后发现了另一个问题,即这样会产生一个从1到0非常突兀的跳变。
为了解决这个问题,我们需要构造两层相差半个周期的采样,再对他们进行一个差值的混合。
//构造周期相同且相位相差半个周期的波形函数 TimeSpeed是可控制的流速参数 float phase0 = frac(_Time * 0.1 * _TimeSpeed ); float phase1 = frac(_Time * 0.1 * _TimeSpeed + 0.5);
因为我们希望无缝循环,因此对其进行插值混合。我们使用一个这样的插值函数,它的周期为1,且越接近0时,第一层采样的权重越高;越接近1时,第二层采样的权重越高。
flowLerp插值函数:
//待偏移的uv float2 tilling_uv = i.uv * _MainTex_ST.xy + _Main_ST.zw; //用上面构造的两个波形函数对向量场计算后的贴图进行偏移采样 half3 tex0 = tex2D(_MainTex, tilling_uv - flowDir.xy * phase0); half3 tex1 = tex2D(_MainTex, tilling_uv - flowDir.xy * phase1); //再使用刚刚构造了插值函数,对两个采样的结果进行插值计算 float flowLerp = abs((0.5 - phase0) / 0.5); half3 finalColor = lerp(tex0, tex1, flowLerp);
整个流程我们可以划分为:采样flowmap → 得到向量场 → 构造两个相位 → 偏移uv → 用偏移的uv采样法线(两层,并差值混合) → 将法线用于光照计算。
FlowMap的制作
主要介绍两种绘制FlowMap的方法,一个是比较方便小巧的Flowmap painter,第二种则是基于Houdini来制作的。
Flowmap painter
Flowmap painter是一个非常简单的工具,它只能用于绘制flowmap贴图,非常好上手,非常好学,是个可能打开来就能知道怎么使用的软件。
Flowmap painter基本功能
- 拖动鼠标绘制向量
- 勾选Erase切换成擦除
- Radius和Strength:控制笔刷的范围和力度
- Clear:复原
- FlowLine:显示向量方向
- VertColor:显示顶点色
- Toggle Wrap:开启平铺
- FlowLineScale:向量显示的长度
- Flip等:反转红绿通道
- Load等:加载对应路径的flowmap、Texture或遮罩
注意用该工具得到的flowmap为线性空间下的颜色,不需要gamma校正,Unity中请需要勾选“sRGB”。同时不要进行压缩,不然也会影响流动效果。
这个sRGB选项可以看看图形部分2.6伽马校正的线性工作流部分。
Houdini Labs制作flowmap
Houdini Labs是内置在houdini中的一组游戏开发相关的节点,可以到github中搜多sidefx Labs或者直接在houdini中安装得到。Flowmap是属于Houdini Labs中的一组节点,在新版本的Houdini的工具中能找到SideFX Labs直接使用。
节点:
Labs Flowmap
初始化向量场信息V,可选择有
- Normal(模型法线生成初始v向量)
- Slope(计算梯度生成v向量)
- Direction(将所有v向量设置为固定方向)
可以勾选Visualize Flow Vector来可视化方向 其中每个顶点携带顶点坐标和向量方向。
Labs Flowmap Brush
在视图中可以编辑向量方向,功能类似一个笔刷工具。还有很多参数可调制。回车开始开始笔刷编辑,ESC退出编辑状态。Comb Lift 用于调整笔刷模式,0 为正常绘制,1 为擦除 (使向量变为该点的法线值),-1 使向量指向该点法线的反方向。
Labs Flowmap to Color
通过读取向量场v信息改显示顶点的颜色信息。其中每个顶点携带顶点坐标,向量信息,顶点色。并且给一个顶点的UV坐标。(如果前面有UV坐标,就沿用之前的UV坐标,如果没有UV坐标,就会创建一套新的。)
Labs Flowmap Visualize
实现预览Flowmap效果,在参数栏中可以设置贴图平铺,速度,扰动次数。左侧视图中可以看到流动的效果。
Labs Guide Flowmap
输入两个参数,分别是向量场(Labs Flowmap),曲线(Drawcurve),曲线用来设置流向。
这个节点将用曲线的切线方向和原有的向量场之间混合。可以在Drawcurve节点中通过回车进入编辑模式。 通过曲线来修改向量信息。 可以通过这个节点修改参数,如:控制混合强度、影响范围、全局影响、曲线的分段数之类的。
Labs Flowmap Obstacle
需要两个参数,分别是向量场,模型。
这个节点将模型转化为体素(VDB),并在和向量场接触的位置改写向量场,使受影响的向量指向远离碰撞体的方向,模拟出一个反冲的效果。
Strength决定反冲强度。
Division Size和Dilate Volume用于控制VDB,分别用于控制体素的细分程度和整体体积。细分程度不应该过小。
Blur strength用于平滑,避免局部的不自然。
Labs maps_baker
输出制作好的flowmap,设置:选择路径,最重要的是选择输出顶点色,并且设置gamma 值。
作业
接下来是究极烂活部分 这个接近10mb的动图包含了烂活精神。
有时候也不知道为什么自己会做出这种烂活。
代码部分倒是大同小异。
fixed4 frag (v2f i) : SV_Target { float3 flowDir = tex2D(_FlowMapTex, i.uv) * 2.0f - 1.0f; flowDir *= _Speed; float phase0 = abs(fmod(_Time.y*0.25, 2) - 1); float phase1 = frac(_Time.y*0.25 + 0.5f); half3 tex0 = tex2D(_MainTex, i.uv + flowDir.xy * phase0); half3 tex1 = tex2D(_MainTex, i.uv + flowDir.xy * phase1); float flowLerp = abs((phase0 - 0.5f) / 0.5f); half3 finalColor = lerp(tex0, tex1, flowLerp); return fixed4(finalColor, 1); }
参考
【技术美术百人计划】图形 2.8 flowmap的实现——流动效果实现