https://space.bilibili.com/7398208?spm_id_from=333.788.b_765f7570696e666f.1
什么是FlowMap
FlowMap
Flow map是Valve在2010年的GDC中,介绍的他们在《求生之路2》和《传送门2》中用到的实现水面流动效果的艺术。这种方式因为原理简单,容易实现,而且运算量比较少,所以到现在都还在使用。
FlowMap的实质
一张记录了2D向量信息的纹理
假设有一个2D平面,平面上每个点都对应一个向量,这个向量指向这个点接下来要运动的方向。
我们通过颜色的色值(RG两个通道)来记录这些向量的信息,就得到了一张flowmap
在shader中干扰uv(偏移uv),对纹理进行采样,这样就得到了一个模拟流动的效果
UV映射(纹理映射)
Flowmap的原理
UV经过Flow Map发生偏移之后,它让一个原本采样正常的图,变成了一个扭曲的效果。
需要注意的是UE4和Unity的UV坐标有所不同,UE4它翻转了绿通道
为什么要使用flowmap?
真实感水体渲染技术总结:https://zhuanlan.zhihu.com/p/95917609
下图为战神中flowmap在天空球中的应用,通过交替出现的云朵可以看出flowmap的典型特征周期性变化
FlowMap shader
实现思路
- 采样Flow map获取向量场信息
- 用向量场信息,使采样贴图时的UV随时间变化
- 对同一贴图以半个周期的相位差采集两次,并线性插值,使贴图流动连续
如何实现最简单的随时间偏移?
使用UV - time
为什么是相减?
先来看看 uv+time 的情况(u,v) + (time,0) :模型上某个点: 随着time增加,采样到的像素越远
视觉上可以形容为:更远距离的像素偏移向该点,视觉效果和我们直观认识到的运算法则是相反的,所以需要相减
uv偏移并没有改变顶点位置,只是采样到了更远的像素
我们需要更好的运动方向,正确的方法是从flowMap获取流动方向
颜色值范围是[0,1]的,而方向向量的范围都是[-1,1],因此我们就需要使用映射手段(即乘以2,减去1),这样才能从FlowMap获取我们的流向(之前推导mvp矩阵时也用过这个方法)
把变形控制在一定范围之内
这个时候时间到达最大值之后会跳变至0,为了解决这个问题需要构造两层相差半个周期的采样,再对它们进行一个插值混合。
在这个插值混合中,我们需要用到一个变化的权重。
1.构造了相差半个周期的的采样
2.通过frac函数我们限制在了[0,1]范围,
3.接下来使用一个函数来代表我们采样贴图的过程,它会呈现出周期性的脉动的趋势。
其中红色代表的是我们构造的权重
我们可以看到当它采样到的纹理偏移到最大值的时候,这个时候他对应的这个权值对应的是0
也就是说,它会在到达最大值的时候会消隐,并且被另外一层所取代
当蓝色的权重取到1的时候,它正好是蓝色处于偏移到半个周期位置之时。
而此时红色正好达到了它的一个最大的偏移,这就意味着当某一层偏移到最大值的时候,它会完全消隐。
这时候我们看到另一层我们所采用的贴图,它会从偏移到半个周期的时候,然后在这一层也到达一个最大偏移的时候又替换为另外一层。
这样就是规避了我们偏移到最大的时候,产生的跳变问题,如果我们使用一个函数来进行线性插值来混合这两个用来模拟采样的函数的话,你会发现它会从一层的中间位置,缓慢地过渡到另一层的中间位置,这样就构成了一个连续的函数。
代码
用flowmap修改法线贴图
应用在光照模型之前
绘制FlowMap的工具
需要注意的是,用FlowMapPainter得到的flowmap为线性空间下的颜色,也就是Gamma1.0,不需要Gamma校正,在Unity导入后请取消勾选sRGB,使用无压缩或者高质量。如果使用有损压缩会看到奇怪的小方块。