时间与动画 — 让着色器动起来

静态的着色器只能看到一成不变的颜色,而一旦引入时间变量,你的着色器就有了”生命”。本课将学习 Unity 的内置时间变量,以及如何利用它们驱动各种动态效果。

本课目标

  • 理解 _Time、_SinTime、_CosTime 四种通道的含义
  • 掌握时间驱动动画的基本模式
  • 结合内置函数实现闪烁、脉动、流动、波动四种动画

1. _Time 变量详解

Unity 提供了三个内置的时间变量,它们都是 float4 类型,每个分量代表不同的速度:

1.1 _Time

通道计算方式速度用途建议
_Time.xt / 20慢速(1/20)缓慢变化的动画
_Time.yt正常速度(×1)最常用,等价于 Time.timeSinceLevelLoad
_Time.zt × 2双倍速度(×2)快速动画
_Time.wt × 3三倍速度(×3)更快动画

1.2 _SinTime / _CosTime

这两个变量是 _Time 的三角函数简化版本:

通道_SinTime_CosTime
.xsin(t / 8)cos(t / 8)
.ysin(t / 4)cos(t / 4)
.zsin(t / 3)cos(t / 3)
.wsin(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 变量,我们可以归纳出四种最常用的动画模式。

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)

基于位置 + 时间的波动,适合旗帜、水面

为什么用 abs(sin)?

sin(_Time.y) 的返回值在 [-1, 1] 之间循环。取绝对值后,变为 [0, 1] 之间循环,且每秒有两个周期(从 0 到 1 再到 0 算一个周期)。如果你想控制闪烁频率,调整 _Speed 即可。

3.2 脉动 (Pulse) — 完整示例

Blink vs. Pulse 的区别

模式公式效果
Blinkabs(sin(t))快速开/关,有”尖角”突变
Pulsesin(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. 本课总结

概念要点
_Timefloat4,4 种速度:慢/正常/2x/3x。最常用 _Time.y
_SinTime / _CosTimesin(_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. 练习与回顾

思考题

  1. _Time 各通道:_Time.x 到 _Time.w 的速度分别是多少?为什么要有多个速度?
  2. 闪烁 vs 脉动abs(sin(t))sin(t) * 0.5 + 0.5 在波形上有什么不同?各适合什么场景?
  3. 波动公式sin(uv.x * freq + _Time.y * speed) 中,freq 和 speed 各自控制什么?
  4. 顶点 vs 片元动画:什么情况下应该在 Vertex Shader 中做动画,什么情况下应该在 Fragment Shader 中做?
  5. 动手实验:创建一个带波动效果的旗帜着色器。在 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 等)综合运用。