着色器动画是Three.js中实现高性能视觉效果的核心技术,通过GPU并行计算处理大量顶点或像素,能创造出流畅的动态效果。
着色器动画的本质是通过时间变量驱动图形的顶点位置、颜色等属性随时间变化。在Three.js中,我们通过uniform变量将CPU的时间数据传递给GPU(着色器),让着色器根据时间计算动态效果。
关键概念:
uniform:CPU向GPU传递的全局变量(所有顶点/像素共享)uTime:存储时间的uniform变量,随动画循环递增(示例)着色器分为顶点着色器(处理顶点位置)和片段着色器(处理像素颜色),两者通过varying变量传递数据。
const vertexShader = `
// 声明变量
varying vec2 vUv; // 传递UV坐标给片段着色器
varying float vElevation; // 传递波浪高度给片段着色器
uniform float uTime; // 时间变量(从CPU传入)
uniform float amplitude; // 波浪振幅(可调节)
void main() {
vUv = uv; // 传递UV坐标
// 核心:根据时间计算波浪高度
// sin函数创建周期性波动,position.x控制水平方向波动,uTime控制动画速度
float elevation = sin(position.x * 3.0 + uTime) * amplitude;
vElevation = elevation; // 保存高度用于颜色计算
// 计算新的顶点位置(y轴叠加波浪高度)
vec3 newPosition = vec3(position.x, position.y + elevation, position.z);
// 转换为最终屏幕坐标
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`;
const fragmentShader = `
precision mediump float; // 精度设置
varying vec2 vUv; // 接收顶点着色器的UV坐标
varying float vElevation; // 接收波浪高度
uniform float opacity; // uniform传递的透明度参数
void main() {
// 基础颜色(基于UV坐标)
vec3 color = vec3(vUv.x, vUv.y, opacity);
// 根据波浪高度调整亮度(波峰更亮,波谷稍暗)
// (vElevation是从顶点着色器传递过来的波浪高度值,范围大约在 -0.3 到 0.3 之间(因为 amplitude = 0.3))
// 将波浪高度值向上平移 1.0 个单位:
// 最小值:-0.3 + 1.0 = 0.7
// 最大值:0.3 + 1.0 = 1.3
// 这样就将原来的 [-0.3, 0.3] 范围映射到了 [0.7, 1.3],确保结果总是正数
float brightness = vElevation + 1.0;
vec3 finalColor = color * brightness;
// 输出最终颜色
gl_FragColor = vec4(finalColor, 1.0);
}
`;
// 创建着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShader, // 顶点着色器
fragmentShader: fragmentShader, // 片段着色器
uniforms: {
uTime: { value: 0 }, // 时间变量(初始值0)
opacity: { value: 0.5 }, // 透明度
amplitude: { value: 0.3 } // 波浪振幅
}
});
// 创建平面几何体
const geometry = new THREE.PlaneGeometry(10, 10, 100, 100);
const plane = new THREE.Mesh(geometry, shaderMaterial);
plane.rotation.x = -Math.PI / 2; // 平面旋转90度
scene.add(plane);
通过requestAnimationFrame创建动画循环,不断更新uTime的值,驱动着色器动画:
function animate() {
// 核心:每帧更新uTime(时间递增,控制动画速度)
shaderMaterial.uniforms.uTime.value += 0.01;
// 持续渲染
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
它是连接CPU和GPU的时间桥梁。CPU通过动画循环不断更新uTime的值(如每帧+0.01),GPU(着色器)根据这个值计算实时动画状态,实现周期性变化。
sin(position.x * 3.0 + uTime)中:
position.x * 3.0:控制水平方向的波浪密度(值越大,波浪越密集)+ uTime:让波浪随时间向右移动(类似x + t的平移效果)amplitude:控制波浪高度着色器动画直接在GPU上运行,即使平面有100x100=10000个顶点,也能流畅运行(传统CPU修改顶点位置会很卡顿)。
这种方法可以扩展到更复杂的效果,如水流、火焰、粒子动画等。
通过时间变量驱动的着色器动画,是实现复杂动态效果的关键技术。掌握uTime的使用,可以让你的3D场景变得更加生动和富有表现力。