US3S9L4——顶点波动效果——流动的河流
US3S9L4——顶点波动效果——流动的河流
顶点波动可以达到的目标
假设我们的目标是让一个矩形网格面片,通过顶点动画,实现出河流的效果。
所谓的河流效果,就是呈现出波浪感,而想要呈现出波浪感,我们必须了解波长、波动频率、波动幅度这些关键因素
波浪感的关键因素
-
波长:指两个相邻波峰或波谷之间的距离。波长越大,波动越缓慢,波形周期越长。
- 波长的倒数:,倒数越大,表示波动越频繁,波形周期越短
-
波动频率:指波动在单位时间内发生的次数(相当于波浪变化的频率)
-
波动幅度:指波峰或波谷相对于中线(静止位置)的最大偏移位置
我们需要在我们的 Shader 代码中,声明这三个关键因素变量,用于控制顶点的偏移,从而实现流动的 2D 河流效果
波浪感基本原理
基本原理:让我们的顶点在对应的轴向产生偏移。
主要运用的就是 Shader 中的内置函数 sin()
,以及内置时间变量 _Time.y
-
sin()
是正弦函数,正弦函数是一个周期性函数,常用与表示波动和震荡效果,它的返回值是 -1 ~ 1 -
_Time.y
是切换到当前场景后所经过的时间,参与到计算中,可以让我们的波浪周期性变化
再结合波长的倒数、波动评率、波动幅度等可变参数,参与到计算中,便可以实现效果
具体步骤:
-
关键步骤一:让顶点上下动起来
我们可以利用
sin
函数让顶点在希望移动的轴向上产生偏移,
并且为了能够周期性变化,可以让时间参与到计算中:sin(_Time.y)
,该函数随着时间的变化,会不停地返回 -1 ~ 1 之间的数,
为了控制波动评率,我们可以声明波动评率变量参与计算:sin(_Time.y * 波动频率)
用得到的返回值,作为顶点在某一轴向的偏移值,便可以让顶点动起来问题:所有顶点偏移的会一样,会呈现出整体上下移动的效果
-
关键步骤二:让顶点有差异性的动起来
为了让顶点之间偏移位置有差异,我们可以在计算
sin()
时利用每个顶点的不同点制造差异性
对于顶点来说,不同点主要来自坐标,我们可以利用他们变化**某个轴的坐标制造差异性:**sin(_Time.y * 波动频率 + 顶点某轴坐标)
用得到的返回值,作为顶点在某一轴向的偏移值,便可以让顶点有差异性的动起来问题:无法改变波长和波动幅度
-
关键步骤三:体现波长和波动幅度
sin(_Time.y * 波动频率 + 顶点某轴坐标)
已经可以帮助我们实现波动变化了,
想要体现出波长和波动幅度,我们只需要将这两个变量参与计算即可- 波长的体现:
sin(_Time.y * 波动频率 + 顶点某轴坐标 * 波长的倒数)
,倒数越大,波形周期越短 - 波动幅度体现:
sin(_Time.y * 波动频率 + 顶点某轴坐标 * 波长的倒数) * 波动幅度
其中,某轴指的是与波本身平行的哪个轴,即横轴,以下图为例,X轴就是与波平行的那个轴
相当于将 -1~1 范围扩大了
- 波长的体现:
流动的2D河流基本原理,就是利用下面这个公式,对顶点位置进行偏移计算:
纵轴位置的偏移量 = sin( _Time.y * 波动频率 + 顶点在横轴上的坐标 * 波长的倒数) * 波动幅度
其中:具体轴向根据模型空间决定,波动频率、波长倒数、波动幅度为自定义变量,可以外部调节
关闭批处理
渲染标签:“DisableBatching” = "True"
主要作用:是否对 SubShader
关闭批处理
我们在制作顶点动画时,有时需要关闭该Shader的批处理,因为我们在制作顶点动画时,有时需要使用模型空间下的数据
而批处理会合并所有相关的模型,这些模型各自的模型空间会丢失,导致我们无法正确使用模型空间下相关数据
在实现流程的2D河流效果时,我们就需要让顶点在模型空间下进行偏移
因此我们需要使用该标签,为该 Shader 关闭批处理
流动的2D河流效果具体实现
以下图的面片为例,Z 轴是横轴,X 轴是纵轴,因此需要在 X 轴上偏移顶点,实现波动效果
(实际上一个模型要在哪个轴上波动,取决于此模型的本地坐标系是怎么样的)
-
新建 Shader,删除无用代码
-
声明属性、映射属性
- 主纹理(
_MainTex
) - 叠加的颜色(
_Color
) - 波动幅度(
_WaveAmplitude
) - 波动频率(
_WaveFrequency
) - 波长的倒数(
_InvWaveLength
)
1
2
3
4
5
6
7
8Properties
{
_MainTex("Texture", 2D) = "white" {} // 主纹理
_Color("Color", color) = (1, 1, 1, 1) // 叠加颜色
_WaveAmplitude("WaveAmplitude", Float) = 1 // 波动幅度
_WaveFrequency("WaveFrequency", Float) = 1 // 波动频率
_InvWaveLength("InvWaveLength", Float) = 1 // 波长倒数
} - 主纹理(
-
透明 Shader 设置相关,并禁用批处理
该模型纹理可能会有透明区域,因此需要使用透明混合相关的设置,具体详见:US3S3L5——透明度混合
此外,由于该 Shader 会使用模型空间下的顶点位置,因此需要禁用批处理,防止其抛弃模型空间下顶点修改渲染标签的渲染类型为透明,队列为透明,并忽略投影机,同时禁用批处理
关闭深度写入,开启混合,需要使用SrcAlpha OneMinusSrcAlpha
混合1
2
3
4
5
6
7
8Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" "DisableBatching"="True"}
Pass
{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
// ...
} -
结构体相关
顶点和 uv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _WaveAmplitude;
float _WaveFrequency;
float _InvWaveLength; -
顶点着色器
利用理论中讲解的公式,计算对应轴向偏移位置,注意,需要在模型空间中偏移
上图的例子中,Z 轴是横轴,X 轴是纵轴,因此我们要沿着模型的 Z 轴去偏移顶点 Y 轴上的位置
1
2
3
4
5
6
7
8
9
10
11
12v2f vert (appdata_base v)
{
v2f o;
// 模型空间下的偏移计算,假设此模型的横轴是Z轴,纵轴是Y轴,因此需要沿着模型的Z轴去偏移顶点Y轴上的位置
float4 offset;
offset.x = sin(_Time.y * _WaveFrequency + v.vertex.z * _InvWaveLength) * _WaveAmplitude;
offset.yzw = float3(0, 0, 0);
o.vertex = UnityObjectToClipPos(v.vertex + offset);
// 计算uv坐标
o.uv = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
return o;
} -
片元着色器
直接进行颜色采样,颜色叠加
1
2
3
4
5
6
7fixed4 frag (v2f i) : SV_Target
{
// 从纹理采样并叠加颜色
fixed4 color = tex2D(_MainTex, i.uv);
color.rgb *= _Color.rgb;
return color;
}
显示效果(波动幅度0.2,波动频率0.3,波动倒数3):
现在,这条面片的顶点就在其纵轴上偏移了,而且会随着时间修改偏移量,实现了波动的效果
如果还要结合纹理滚动效果,可以在顶点着色器修改UV坐标来实现,具体可见:US3S9L3——滚动的背景
首先在属性处声明纹理滚动速度属性:
1 | Properties |
然后在片元着色器上修改UV坐标,让其随着时间变化
1 | v2f vert (appdata_base v) |
显示效果(注意,如果纹理没有滚动效果,可能是因为滚动方向和颜色变化方向不一致导致的,这时可以修改滚动方向或者更换贴图来解决):
完整 Shader 代码如下:
1 | Shader "TeachShader/Lesson94" |