百人计划学习地址:图形 2.8 Flowmap的实现
一、基础概念首先复习一下UV映射
UV坐标用于查找纹理贴图上的颜色值
什么是FlowMap?
Flowmap的实质就是一张记录了2D向量信息的纹理
Flow map上的颜色(通常为RG通道)记录该处向量场的方向,让模型上某一点表现出定量流动的特征。通过在shader中偏移uv再对纹理进行采样,来模拟流动效果。
我们使用(R,G)向量来表示色值,如下图三所示,表现了向量的方向与色值的对应
Flow Map实现原理
通过所带有的向量场的信息对UV进行偏移,并干扰采样的过程,使原本的采样效果变得扭曲
为什么要使用FlowMap?
用于表现各种流动的效果,好处如下
- 效果类似于UV动画,而非顶点动画。
- 无需对模型顶点进行操作
- 易实现,运算开销小
shader的实现流程
- 采样Flow map获取向量场信息
- 用向量场信息,使采样贴图时的UV随时间变化
- 对同一贴图以半个周期的相位差采集两次,并线性插值,使贴图流动连续
如何实现最简单的随时间偏移?
使用UV - time
为什么是相减?
先来看看 uv+time 的情况(u,v) + (time,0) :模型上某个点: 随着time增加,采样到的像素越远
视觉上可以形容为:更远距离的像素偏移向该点,视觉效果和我们直观认识到的运算法则是相反的,所以需要相减
我们需要更好的运动方向,正确的方法是从flowMap获取流动方向
但是,flow map不能直接使用,需要将flow map上的色值从[0,1]的范围映射到方向向量的范围[-1.1]
下一步需要把变形控制在一定范围之内
使用周期函数来控制
用相位差半个周期的两层采样进行加权混合,使流动变得更加无缝
完整的shader过程
使用Unity3d制作的绘制Flowmap工具
基本功能
- 拖动鼠标绘制向量
- 勾选Erase切换成擦除
- Radius和Strength:控制笔刷的范围和力度
- Clear:复原
- FlowLine:显示向量方向
- VertColor:显示顶点色
- Toggle Wrap:开启平铺
- FlowLineScale:向量显示的长度
- Flip等:反转红绿通道
- Load等:加载对应路径的flowmap、Texture或遮罩
注意
通过该工具得到的flowMap为线性空间下的颜色,不需要Gamma校正,Unity中需取消勾选SRGB
另一个需要注意的是,需要在Unity3d中把贴图设置成无压缩或者高质量
使用Houdini Labs制作Flow map
需要安装SideFx Labs插件(低版本需要再Github中安装,高版本直接在houdini的工具栏即可安装)
对于Houdini具体的使用方法视频里讲的非常详细,这里不多做介绍,只标注一些Flowmap制作中的注意事项
开始先创立一个Grid(网格),并细分它的顶点,以确保Flowmap的效果更好
然后创建一个Labs Flowmap节点,为模型初始化一个向量场 V
向量场有三种初始化的方式:Nomral,Slope,Direction
接着创建Labs Flowmap Brush节点,功能类似于笔刷,用于绘制向量方向
Comb Lift 用于调整笔刷模式:
- 0 为正常绘制
- 1 为擦除 (使向量变为该点的法线值)
- -1 使向量指向该点法线的反方向
其他的Brush和Symmetry 标签下为笔刷控制选项。如笔刷不透明度Opacity
接着我们创建FlowMap To Color节点,用于显示顶点色(vectex Color)
注意:Flip Green Channel(Unity)
该选项并非反转了绿通道,而是反转了红绿两个通道,这将使流向反向。
实际上可以不需要勾选这个选项。
此外,该节点还将检测模型是否有UV,如果没有,将赋予模型UV。
再接着创建Labs Flowmap Visualize节点,用于观察flowmap的流动表现
左侧视图中可以看到流动的效果
再添加一个Labs Maps Baker 节点就可以渲染出我们要的FlowMap贴图了
最后点击Render,就可以生成我们需要的FlowMap了
Labs Flowmap Guide节点
Labs flowmap obstacle节点
尝试用FlowMap做一个效果
用FlowMapPainter自己画了一个flowmap
shader代码也基本是用了教程中的算法
fixed4 frag (v2f i) : SV_Target
{
float3 flowDir = tex2D(_FlowTex, i.uv) * 2 - 1;
flowDir *= _FlowSpeed;
//控制时间周期
float phase0 = frac(_Time * 0.1 * _TimeSpeed);
float phase1 = frac(_Time * 0.1 * _TimeSpeed + 0.5);
half4 tex0 = tex2D(_MainTex, i.uv.xy - flowDir.xy * phase0);
half4 tex1 = tex2D(_MainTex, i.uv.xy - flowDir.xy * phase1);
float flowlerp = abs((0.5 - phase0) / 0.5);
half4 MainColor = lerp(tex0, tex1, flowlerp) * _Color;
return MainColor;
}
尝试做了一个乌云内部灰尘扰动的效果(好菜



