最近刚好学到Shader Graph水体流动,看下其他实现方式记录下
1 什么是flow map
1 什么是Flow map?
flowmap的实质:一张记录了2D向量信息的纹理Flow map上的颜色(通常为RG通道) 记录该处向量场的方向,让模型上某一点表现出定量流动的特征。通过在shader中偏移uv再对纹理进行采样,来模拟流动效果
2 纹理映射,左边是UV坐标值,右边是采样结果
UV使用flow map偏移出现不同效果
为什么要使用flowmap?
类似UV动画,而非顶点动画。换言之,无需对模型顶点进行操作,易实现运算开销小。
不仅仅是水面,任何和流动相关的效果都可以采用flowmap.
flowmap的原理:通过所带有的向量场信息对uv进行了一个偏移,来干扰我们采样时候的这个过程。如图可以看到,经过flowmap发生偏移后,让原本正常的采样变成了一个扭曲的效果
我们需要更好的运动方向,正确的方法是从flowMap获取流动方向,但是,flow map不能直接使用,需要将flow map上的色值从[0,1]的范围映射到方向向量的范围[-1.1].
2 flow map Shader
借助Shader Graph理解Flow map
1.采样Flow map获取向量场信息
2.用向量场信息,使采样贴图时的UV随时间变化
3.对同一贴图以半个周期的相位差采集两次,并线性插值,使贴图流动连续
这里用到了一个函数模拟网站
https://www.desmos.com/calculator?lang=zh-CN
最简单的随时]间偏移?
time
为什么是相减?
先来看看 uv+time 的情况uv) + (time.0) :模型上某个点: 随着time增加,采样到的像素越远
视觉上可以形容为: 更远距离的像素偏移向该点,视觉效果和我们直观认识到的运算法则是相反的.
UV值作为向量 (u,v) ,自然也遵循向量的运算法则.但UV偏移时,改变的不是顶点的位置。
https://teckartist.com/?page_id=107 点击下载流形图绘制工具
随着时间进行,变形越来越夸张,需要把偏移控制在一定范围内实现无缝循环
依据Flowmap的原理,我们想要做出流动效果,就需要uv随时间进行周期性偏移
既然是周期性偏移,那么时间必须有个阀值 周期有阀值人们第一想到是frac()函数
但frac()函数有个问题,那就是周期的有断层
人们希望0.99循环 到1的断层去掉 这时候有个聪明人想到了加权平均.用两条有周期有规律变换的线 蓝紫两线的加权不断变换使断层效果消失 x=0.5时蓝线权重1 紫线权重0 x=1时蓝线权重0紫线权重1
那么 x在0~1周期 y在010线性波动的怎么做呢? 有人给公式了
有人会想到 sin和cos +一些变化代替上面的公式 这并不好用因为输入是弧度π是无理数. 你无法准确控制sin和cos的周期 用/3.1415926 这种形式运算也废GPU指令 时间拉长就会有越来越大误差.
3 Shader实现
frac函数的作用
Shader "MyShader/Water"
{Properties{// basecolor_MainTex ("Texture", 2D) = "white" {}//混色_Color("Tint", Color) = (1,1,1,0.5)//水体法线_MainTex ("Texture", 2D) = "white" {}_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"}Cull OffLighting OffZWrite OnLOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma shader_feature _REVERSE_FLOW_ON#include "UnityCG.cginc"struct a2v{float4 pos : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1)float4 pos : SV_POSITION;};fixed4 _Color;sampler2D _MainTex;sampler2D _FlowMap;float4 _MainTex_ST;float _FlowSpeed;float _TimeSpeed;v2f vert (a2v v){v2f o;//顶点坐标o.pos = UnityObjectToClipPos(v.pos);//顶点UVo.uv = v.uv;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_ONflowDir *= -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}}
}
想试试加上NormalMap视频里代码不全,用到了TBN向量计算世界空间法线,后续补。