第 7 课:顶点着色器与形变
为什么要移动顶点
片元 Shader 能改变颜色,却不能改变物体轮廓。旗帜摆动、地形起伏、呼吸球体、柱体扭转等效果需要修改网格的顶点位置。Three.js 把几何体的 position、normal、uv 作为 attribute 送到顶点着色器;顶点 Shader 计算 transformed,再用 projectionMatrix * modelViewMatrix 输出裁剪空间位置。光栅化阶段连接移动后的顶点,并在三角形内部插值 vUv、高度等 varying;片元 Shader 最后给表面着色。
顶点 Shader 每个顶点执行一次。若几何体只有四个顶点,再复杂的波浪公式也只会移动四角,内部仍是平面。第 3、4、6、9、10 题使用细分平面;分段越多,形状越细,但顶点运算和内存也越多。先按视觉目标选足够的细分密度,再测量性能,而不是盲目提高到数百段。
坐标空间与最小形变
最简单的移动是 transformed.y += 0.30。这里 position 是模型局部空间,0.30 是模型单位,不是屏幕像素。modelViewMatrix 会把修改后的局部位置变到相机空间,projectionMatrix 再完成投影。由于相机透视,同样的局部位移在不同深度上对应的屏幕像素不同。
时间动画把常量换成 0.30*sin(uTime*2.0):整张平面的所有顶点读同一个 uTime,位移相同,因此整个网格上下移动,形状不变。波浪面把位移与每个顶点的 x/y 关联:
transformed.z += 0.16 * sin(position.x * 7.0 + uTime * 2.0);
transformed.z += 0.12 * sin(position.y * 6.0 - uTime * 1.4);
7.0、6.0 控制空间频率,0.16、0.12 控制波高,时间项决定相位移动。两个方向叠加后,波峰形成交错起伏。第 3 题把位移高度写入 vHeight,片元 Shader 再把高度映射为颜色,帮助观察几何变化。
权重、约束与形状
旗帜的一端固定:把风位移乘以 uv.x。左侧 UV x 为 0,位移为 0;右侧为 1,接受完整风力。uv.x*uv.x 会让位移更集中在右侧。布料题把两上角到顶点的 UV 距离变成固定权重,其他地方同时承受向下的“重力”位移和周期性风位移。这是解析动画:没有相邻顶点间的弹簧、碰撞或能量守恒,适合教学、旗面装饰和远景布料感;需要真实互动布料时应使用模拟。
球体呼吸把每个 position 乘相同的缩放系数,轮廓仍是球形。法线膨胀则用 position + normal*amount,使表面沿自身朝向外扩;它是常见的描边外壳和膨胀效果基础。若模型法线不连续,面与面会向不同方向分离;这既可制造爆炸感,也可能暴露接缝。
柱体扭转让每个顶点的旋转角取决于高度 position.y:底部与顶部旋转不同角度。只旋转 position.xz 而继续使用旧 normal 时,表面的高光方向会不完全正确;本练习保留这个简化并用条纹展示扭转。真实光照材质需要同步旋转法线,复杂位移还可能需要重新求导或重算法线。
噪声地形与指针水波
地形题在顶点 Shader 内按局部 x/y 计算平滑噪声,将结果加到局部 z。平面在 JS 中旋转到水平,因此局部 z 对应世界空间的向上方向。片元 Shader 用插值后的高度着色;它展示地形轮廓,但不是按新法线计算的真实坡面光照。
交互水波题把指针在画布中的位置转成 0–1 UV,传入 uPointer。顶点用 length(uv-uPointer) 求距波源的距离,sin(distance*frequency-uTime*speed) 形成向外移动的波纹,再乘指数项使远处逐渐消失。当前平面覆盖正方形预览区域,因此画布归一化位置可直接作为平面 UV;若平面只是场景中的一个物体,应使用 Raycaster 得到交点 UV,而不是直接用画布坐标。
常规实践与排查
JS 的渲染循环只更新时间 uniform,几何体和材质在启动时创建。公共运行时负责窗口尺寸、像素比与常见资源释放。形变后若要准确光照,必须考虑变形后的法线;若要正确碰撞和拾取,CPU 侧的原始 BufferGeometry 并不会自动变成 GPU 变形后的形状,Raycaster 仍可能命中旧表面。需要物理交互时,可在 CPU 重复位移公式,或采用 GPU 拾取等方案。
画面不动时先确认几何体分段足够、uTime 更新且顶点 Shader 使用的是 transformed 输出;图案撕裂时检查相邻顶点的位移是否连续;波源错位时检查 CSS 像素、画布位置和 Y 轴翻转。窄屏下平面仍以模型单位投影,可能留出更多背景,这是透视相机和画布宽高比共同决定的正常结果。
练习
| 难度 | 题目 | 可见验收 |
|---|---|---|
| ⭐ | 顶点坐标让平面上移 | 网格整体上移 |
| ⭐ | 平面随时间上下波动 | 平滑上下移动 |
| ⭐⭐ | Sin X + Sin Y 波浪面 | 交错起伏 |
| ⭐⭐ | 一端固定的风吹旗帜 | 左端固定、右端飘动 |
| ⭐⭐ | 球体沿半径呼吸 | 球体周期缩放 |
| ⭐⭐⭐ | 噪声驱动地形高度 | 有机地表起伏 |
| ⭐⭐⭐ | 沿法线膨胀爆炸 | 多面体沿法线膨胀 |
| ⭐⭐⭐ | 按高度扭转柱体 | 条纹随高度扭转 |
| ⭐⭐⭐⭐ | 指针位置产生水波 | 波源跟随指针 |
| ⭐⭐⭐⭐ | 固定点、重力与风的布料感 | 双顶角固定、下部飘动 |