Unity Shader 顶点动画:在顶点着色器中实现风吹草动、河流波动、布料模拟

深入探讨如何利用 GPU 顶点着色器的并行算力,以零 CPU 开销实现三种经典自然动画效果,附完整 GLSL 代码与原理解析。

什么是顶点动画?原理

Vertex Animation --- GPU-side Deformation

传统 CPU 动画需要每帧将新的顶点位置上传到 GPU,带宽压力大。顶点动画(Vertex Animation) 则把位移逻辑全部写进顶点着色器(Vertex Shader),让 GPU 数千个核心并行计算每个顶点的位置偏移,CPU 只需传递时间 uniform float uTime 一个参数即可驱动整个动画。

核心优势

零 CPU 骨骼运算、零逐帧网格上传、天然适合草地/海洋等大量重复单元的场景,结合 GPU Instancing 效果尤佳。

顶点着色器在 GPU 渲染管线的最前端执行,它的核心输入是模型空间顶点坐标 attribute vec3 position,输出是裁剪空间坐标 gl_Position。我们只需在这两者之间插入数学函数,就能让顶点"动起来"。

渲染管线中的位置

对于草地、水面、布料这三类效果,它们的共同特征是:形变规律可以用数学公式描述------正弦波、Gerstner 波方程、弹簧约束近似------因此非常适合放进顶点着色器以数学方式驱动。

风吹草动草地

Grass Wind Sway

草地动画的关键在于:越靠近草尖,摆动幅度越大;草根固定不动。这本质上是以 Y 轴坐标为权重的正弦位移。

摆动原理

核心公式

offsetX = sin(posX · freq + uTime · speed) × amplitude × heightFactor

heightFactor = posY / grassHeight (归一化高度权重)

其中 posX 使每根草的相位不同,让草地呈现自然的错位摆动 而非整齐划一。uTime 随帧推进驱动动画,heightFactor 确保草根不动、草尖摆动最大。

cs 复制代码
// ╔════════════════════════════════════════════╗// ║  grass.vert --- 风吹草动顶点着色器           ║// ╚════════════════════════════════════════════╝
precision mediump float;
// ── Uniforms(CPU 端每帧更新)──────────────uniform mat4 modelViewProjection;uniform float uTime;       // 当前时间(秒)uniform float uWindStrength;  // 风力强度 [0, 1]uniform float uWindFreq;     // 风吹频率(Hz)uniform vec2 uWindDir;      // 风的水平方向
// ── Attributes(来自顶点缓冲区)────────────attribute vec3 position;     // 模型空间顶点坐标attribute float aGrassHeight; // 该草叶总高度(烘焙进去)
// ── Varyings(传给片段着色器)──────────────varying float vBlend;     // 用于片段着色(草尖更亮)
void main() {
    // 1. 计算归一化高度权重(草根=0,草尖=1)    float heightFactor = position.y / aGrassHeight;    //    clamp 防止负值(草根可能在 y=0 以下)    heightFactor = clamp(heightFactor, 0.0, 1.0);
    // 2. 主风摆动:沿风向的正弦偏移    float phase = dot(position.xz, uWindDir) * 2.5 + uTime * uWindFreq;    float sway = sin(phase) * uWindStrength;
    // 3. 次级扰动:更高频,幅度更小,增加随机感    float sway2 = sin(phase * 2.3 + 1.2) * uWindStrength * 0.3;
    // 4. 用高度权重使草尖摆动、草根固定    vec3 animPos = position;    animPos.x += (sway + sway2) * uWindDir.x * heightFactor;    animPos.z += (sway + sway2) * uWindDir.y * heightFactor;
    // 5. 重力下垂补偿(风强时草尖略向下弯)    animPos.y -= heightFactor * heightFactor * abs(sway) * 0.15;
    // 6. 输出 varying 用于着色    vBlend = heightFactor;
    // 7. 最终裁剪空间坐标    gl_Position = modelViewProjection * vec4(animPos, 1.0);}

优化技巧

实际项目中可叠加两个不同频率/方向的正弦,模拟主风 + 次级扰动,使草地动作更自然丰富。另外可利用一张**风力贴图(Wind Map)**采样局部风速,实现区域性强风效果。

河流波动水面

Water Wave Simulation with Gerstner Waves

水面波动比草地复杂------真实的水波不只是 Y 方向的上下起伏,还存在水平方向的轨道运动 (Orbital Motion)。Gerstner 波是游戏工业中最常用的物理近似模型,它同时影响顶点的 X、Y、Z 三个分量。

Gerstner 波方程

Gerstner 波公式

X' = X + Σ (Q·A) · D.x · cos(k·(D·P) - ω·t)

Z' = Z + Σ (Q·A) · D.y · cos(k·(D·P) - ω·t)

Y' = Σ A · sin(k·(D·P) - ω·t)

k = 2π/L(波数) | ω = √(g·k)(角频率) | Q = 陡度参数

其中 D 是波的传播方向(二维归一化向量),A 是振幅,L 是波长,Q 控制波峰陡度(Q=0 为正弦波,Q=1 为尖锐波峰)。叠加多个不同方向、波长的 Gerstner 波即可得到真实感的海洋/河流效果。

完整顶点着色器代码

cs 复制代码
// ╔════════════════════════════════════════════╗// ║  water.vert --- Gerstner 波水面着色器        ║// ╚════════════════════════════════════════════╝
precision highp float;
uniform mat4 modelViewProjection;uniform mat3 normalMatrix;uniform float uTime;
// ── Gerstner 波参数(支持最多 4 个叠加)───// waveDir[i]   : vec2 传播方向(归一化)// waveAmp[i]   : float 振幅 A// waveLen[i]   : float 波长 L// waveSteep[i] : float 陡度 Q ∈ [0, 1]uniform vec2  waveDir[4];uniform float waveAmp[4];uniform float waveLen[4];uniform float waveSteep[4];
attribute vec3 position;attribute vec3 normal;
varying vec3 vWorldNormal;varying vec3 vWorldPos;
// ── 单个 Gerstner 波贡献函数 ───────────────vec3 GerstnerWave(vec2 dir, float A, float L, float Q,
               vec2 xz, float t,              inout vec3 tangent, inout vec3 binormal) {
    float k = 6.28318 / L;        // 波数 k = 2π/L    float c = sqrt(9.8 / k);    // 相速度 c = √(g/k)    float f = k * (dot(dir, xz) - c * t);    float qa = Q * A;
    // 更新切线与副切线(用于法线推导)    tangent += vec3(-dir.x * dir.x * k * qa * sin(f), dir.x * k * A * cos(f), -dir.x * dir.y * k * qa * sin(f));    binormal += vec3(-dir.x * dir.y * k * qa * sin(f), dir.y * k * A * cos(f), -dir.y * dir.y * k * qa * sin(f));
    return vec3(qa * dir.x * cos(f), A * sin(f), qa * dir.y * cos(f));}
void main() {    vec3 p = position;    vec3 tangent = vec3(1,0,0);    vec3 binormal = vec3(0,0,1);
    // 叠加 4 个 Gerstner 波    for (int i = 0; i < 4; i++) {        p += GerstnerWave(waveDir[i], waveAmp[i], waveLen[i], waveSteep[i],            position.xz, uTime, tangent, binormal);    }
    // 用叉积重建法线    vWorldNormal = normalMatrix * normalize(cross(binormal, tangent));    vWorldPos = p;    gl_Position = modelViewProjection * vec4(p, 1.0);}

法线重建

顶点位移后原始法线已失效,需要在着色器中同步推导新法线(Gerstner 波的法线有解析解),否则光照会出现严重错误。具体推导公式见代码注释。

布料模拟布料

Cloth Simulation via Vertex Shader

布料模拟是三种效果中最复杂的。真正的物理布料模拟(弹簧-质点系统)需要迭代约束求解 ,状态依赖上一帧------这天然不适合无状态的顶点着色器。 然而工业界有一个广泛使用的技巧:预烘焙 + 顶点动画纹理(VAT,Vertex Animation Texture),将离线模拟的顶点路径烘焙成浮点纹理,在着色器中按时间采样即可获得逼真的布料效果,且运行时开销极低。

两种实现路径对比

方案 适用场景 运行时开销 真实度
正弦叠加近似 旗帜、窗帘、远景 极低 中等
VAT 纹理采样 角色布料、特写道具
Compute Shader 约束 实时交互布料 中高 极高

本节同时演示正弦叠加法 (适合 WebGL/顶点着色器)和VAT 采样法的核心实现。正弦法用两个方向相近但不同的正弦波叠加,并施加重力下垂,使旗帜呈现自然悬垂感。

性能对比与最佳实践

效果 顶点数参考 额外 Uniform GPU 耗时估算 推荐叠加技术
草地 10 万~100 万 uTime, uWind < 0.5ms GPU Instancing
水面 1 万~50 万 uTime, 4×波参数 < 1ms LOD 网格分级
布料 1000~1 万 uTime / uVATTex 1~3ms VAT + 纹理压缩

黄金法则

顶点着色器里避免复杂 if/else 分支 ------GPU 的 SIMD 架构对分支非常不友好。优先用数学混合(mixclampsmoothstep)代替条件判断,以保持 warp 执行一致性。

总结

顶点着色器动画是现代游戏和实时渲染中性价比最高的技术之一。三种效果的核心思路可以归纳为:

  • 风吹草动:以高度为权重的正弦位移 + 相位错位
  • 河流波动:多层 Gerstner 波叠加 + 同步推导法线
  • 布料模拟:正弦近似(实时轻量)或 VAT 采样(高真实度)

三者都遵循同一个设计原则:将随时间变化的数学公式写入顶点着色器,CPU 只传递时间标量。这种"函数驱动"思想让动画完全运行在 GPU 端,与 CPU 物理、AI 等系统完全解耦,是大规模自然场景渲染的首选方案。

相关推荐
DowneyJoy3 小时前
【Unity3D补充知识点】常用数据结构分析-集合(List<T>)
数据结构·unity·c#·list
DowneyJoy4 小时前
【Unity3D补充知识点】常用数据结构分析-数组(Array)
数据结构·unity·c#
w-白兰地4 小时前
配置Unity中的ADB环境变量
unity·adb·游戏引擎
UTwelve4 小时前
【UE】如何正确旋转法线贴图
性能优化·ue5·材质·贴图·着色器
mxwin5 小时前
Unity Shader 几何着色器:动态生成图元与顶点拓扑修改
unity·游戏引擎·着色器
呆呆敲代码的小Y6 小时前
【Unity-AI开发篇】| 游戏中接入DeepSeek实现AI对话,完整详细步骤
人工智能·游戏·unity·ai·游戏引擎·u3d·deepseek
相信神话20211 天前
第四章:Godot 4.6 核心概念与开发环境搭建
游戏引擎·godot·2d游戏编程·godot4·2d游戏开发
代数狂人1 天前
在Godot中应用面向对象原则:C#脚本实践
c#·游戏引擎·godot
Sator11 天前
Unity关于射击游戏人物动画的设计经验
游戏·unity·游戏引擎