图形 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的实现——流动效果实现

Valve关于WaterFlow的分享

FlowMap的使用

 

跳转回百人合集

posted @ 2022-01-14 21:58  anesu  阅读(1819)  评论(0编辑  收藏  举报