时间与动画 — 让着色器动起来
静态的着色器只能看到一成不变的颜色,而一旦引入时间变量,你的着色器就有了”生命”。本课将学习 Unity 的内置时间变量,以及如何利用它们驱动各种动态效果。
本课目标
- 理解 _Time、_SinTime、_CosTime 四种通道的含义
- 掌握时间驱动动画的基本模式
- 结合内置函数实现闪烁、脉动、流动、波动四种动画
1. _Time 变量详解
Unity 提供了三个内置的时间变量,它们都是 float4 类型,每个分量代表不同的速度:
1.1 _Time
| 通道 | 计算方式 | 速度 | 用途建议 |
|---|---|---|---|
_Time.x | t / 20 | 慢速(1/20) | 缓慢变化的动画 |
_Time.y | t | 正常速度(×1) | 最常用,等价于 Time.timeSinceLevelLoad |
_Time.z | t × 2 | 双倍速度(×2) | 快速动画 |
_Time.w | t × 3 | 三倍速度(×3) | 更快动画 |
1.2 _SinTime / _CosTime
这两个变量是 _Time 的三角函数简化版本:
| 通道 | _SinTime | _CosTime |
|---|---|---|
.x | sin(t / 8) | cos(t / 8) |
.y | sin(t / 4) | cos(t / 4) |
.z | sin(t / 3) | cos(t / 3) |
.w | sin(t) — 等于 sin(_Time.y) | cos(t) — 等于 cos(_Time.y) |
最常用的组合 绝大多数情况下你只需要
_Time.y作为时间输入,然后用sin(_Time.y)或cos(_Time.y)生成循环动画。_SinTime.w和_CosTime.w只是这两个运算的预计算版本。
2. UV 偏移 — 最简单的动画
最直接的动画方式就是在 UV 坐标上加上时间量,让纹理”滚动”起来。
这段代码让纹理沿着水平方向持续滚动。这就是游戏中的滚动背景、传送带等效果的基础。
你也可以同时让 UV 在两个方向上运动,产生更复杂的效果:
当 Tiling 值大于 1 时,这个效果看起来就像 UV 在”旋转”。
3. 四种常用动画模式
结合第 7 课的内置函数和 _Time 变量,我们可以归纳出四种最常用的动画模式。
闪烁 (Blink)
abs(sin(_Time.y * _Speed))
颜色在 0-1 之间来回切换,适合”警报灯”效果
脉动 (Pulse)
sin(_Time.y * _Speed) * 0.5 + 0.5
将 sin 的 [-1,1] 映射到 [0,1],平滑呼吸效果
流动 (Flow)
UV.x += _Time.y * _Speed
UV 持续偏移,适合水流、传送带
波动 (Wave)
sin(UV.x * _Freq + _Time.y * _Speed)
基于位置 + 时间的波动,适合旗帜、水面
3.1 闪烁 (Blink) — 完整示例
为什么用 abs(sin)?
sin(_Time.y) 的返回值在 [-1, 1] 之间循环。取绝对值后,变为 [0, 1] 之间循环,且每秒有两个周期(从 0 到 1 再到 0 算一个周期)。如果你想控制闪烁频率,调整 _Speed 即可。
3.2 脉动 (Pulse) — 完整示例
Blink vs. Pulse 的区别
| 模式 | 公式 | 效果 |
|---|---|---|
| Blink | abs(sin(t)) | 快速开/关,有”尖角”突变 |
| Pulse | sin(t) * 0.5 + 0.5 | 平滑呼吸,正弦波形状 |
3.3 流动 (Flow) — 完整示例
纹理 Wrap Mode 的选择 做流动动画时,纹理的 Wrap Mode 应设置为Repeat (默认),这样 UV 超出 [0,1] 范围时纹理会自动平铺。如果设置为 Clamp,边缘会出现拉伸。
3.4 波动 (Wave) — 完整示例
波动公式拆解
i.uv.x * _Frequency— 频率:控制波的数量(值越大波纹越密)_Time.y * _Speed— 速度:控制波动的快慢sin(...)— 波形:在 [-1, 1] 之间振荡* _Amplitude— 振幅:控制波动幅度(建议 0.01 ~ 0.1)
4. 组合动画
真正的游戏效果往往是多种模式的组合。下面是一个UV 旋转 + 脉动 + 色彩变化的组合示例:
设计模式:分层叠加 好的着色器动画通常遵循"分层设计"原则:
① 先从 UV 变换开始(滚动、旋转) ② 再处理颜色变化(脉动、闪烁、色调偏移) ③ 最后添加细节效果(边缘发光、噪声等) 每层单独调试,组合时效果更可控。
5. 顶点动画
_Time 不仅可以用在片元着色器中,更可以驱动顶点位置的变化,实现波浪、旗帜飘动等真正的 3D 动画。
Vertex Shader 动画 vs. Fragment Shader 动画 类型修改位置效果性能消耗顶点动画Vertex Shader几何形状变化(水面、旗帜、草地)少量顶点时很快,高模时较慢UV 动画Fragment Shader纹理滚动、颜色变化像素数量相关,通常很快
6. 顶点旋转动画
回顾第 7 课中的 Sin/Cos 旋转示例,现在结合 _Time 让它动起来:
这段代码让物体绕 Y 轴持续旋转。注意旋转发生在对象空间中,在 UnityObjectToClipPos 之前执行。
性能提示:在 Vertex Shader 中做 vs. CPU 端旋转
- CPU 端旋转(修改 Transform):每帧更新一次,适合逻辑需要知道物体朝向的情况
- Vertex Shader 旋转:纯 GPU 运算,适合纯视觉效果(如装饰性旋转部件)
7. 本课总结
| 概念 | 要点 |
|---|---|
| _Time | float4,4 种速度:慢/正常/2x/3x。最常用 _Time.y |
| _SinTime / _CosTime | sin(_Time) 和 cos(_Time) 的预计算版本 |
| 闪烁 (Blink) | abs(sin(_Time.y * speed)),快速开/关 |
| 脉动 (Pulse) | sin(_Time.y * speed) * 0.5 + 0.5,平滑呼吸 |
| 流动 (Flow) | UV.x += _Time.y * speed,纹理滚动 |
| 波动 (Wave) | sin(UV.x * freq + _Time.y * speed),旗帜/水面 |
| 顶点动画 | 在 vert 函数中修改顶点位置,产生几何形变 |
8. 练习与回顾
思考题
- _Time 各通道:_Time.x 到 _Time.w 的速度分别是多少?为什么要有多个速度?
- 闪烁 vs 脉动:
abs(sin(t))和sin(t) * 0.5 + 0.5在波形上有什么不同?各适合什么场景? - 波动公式:
sin(uv.x * freq + _Time.y * speed)中,freq 和 speed 各自控制什么? - 顶点 vs 片元动画:什么情况下应该在 Vertex Shader 中做动画,什么情况下应该在 Fragment Shader 中做?
- 动手实验:创建一个带波动效果的旗帜着色器。在 Vertex Shader 中修改顶点 Y 位置,在 Fragment Shader 中添加脉动颜色变化。
9. 教材对照
教材对照
本课对应《The Unity Shaders Bible》第 I 章 (p.173-175):
- 4.2.0 定时与动画 (p.173-175)
- _Time / _SinTime / _CosTime 的定义和使用
- UV 偏移 + 时间驱动的动画示例 并结合第 7 课的内置函数(Sin/Cos/Abs/Lerp 等)综合运用。