如何在Three.js中用单个几何体实现多个精灵单向动画?
实现统一方向的精灵下落动画(无需多几何体)
不需要为每个精灵单独创建几何体,用单几何体就能高效实现,核心是利用顶点属性结合着色器动画,或者通过BufferGeometry的动态更新在CPU端处理,以下是两种可行方案:
方案1:GPU端着色器动画(性能最优)
原示例的旋转是针对整个Points对象,要实现粒子沿统一方向(比如Y轴向下)移动,在着色器中处理动画是最优选择,避免CPU频繁更新数据。
给几何体添加自定义属性
为BufferGeometry添加控制每个雪花下落速度的属性(方向统一,速度可随机):const particlesCount = 1000; const geometry = new THREE.BufferGeometry(); const positions = new Float32Array(particlesCount * 3); const speeds = new Float32Array(particlesCount); for (let i = 0; i < particlesCount; i++) { positions[i * 3] = Math.random() * 100 - 50; // X轴随机范围 positions[i * 3 + 1] = Math.random() * 50 + 20; // 初始Y轴高度 positions[i * 3 + 2] = Math.random() * 100 - 50; // Z轴随机范围 speeds[i] = Math.random() * 2 + 1; // 随机下落速度(1-3) } geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.setAttribute('speed', new THREE.BufferAttribute(speeds, 1));自定义顶点着色器
修改PointsMaterial的顶点着色器,让粒子沿Y轴向下移动,超出边界后重置位置实现循环:const material = new THREE.PointsMaterial({ map: snowTexture, transparent: true, onBeforeCompile: (shader) => { shader.uniforms.time = { value: 0 }; shader.vertexShader = ` uniform float time; attribute float speed; ${shader.vertexShader} `.replace( '#include <begin_vertex>', `#include <begin_vertex> transformed.y -= time * speed; if (transformed.y < -20) transformed.y = 50; ` ); material.userData.shader = shader; } });动画循环中更新时间参数
function animate() { requestAnimationFrame(animate); if (material.userData.shader) { material.userData.shader.uniforms.time.value += 0.01; } renderer.render(scene, camera); } animate();
方案2:CPU端动态更新顶点位置(适合简单场景)
如果不想编写着色器,也可以直接更新BufferGeometry的position属性,需开启动态标记优化性能:
初始化动态几何体
const particlesCount = 1000; const geometry = new THREE.BufferGeometry(); const positions = new Float32Array(particlesCount * 3); for (let i = 0; i < particlesCount; i++) { positions[i * 3] = Math.random() * 100 - 50; positions[i * 3 + 1] = Math.random() * 50 + 20; positions[i * 3 + 2] = Math.random() * 100 - 50; } geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.attributes.position.setDynamic(true); // 标记为动态属性,减少更新开销动画循环中更新位置
function animate() { requestAnimationFrame(animate); const positions = geometry.attributes.position.array; for (let i = 0; i < particlesCount; i++) { positions[i * 3 + 1] -= 0.1; // 统一向下移动,固定速度0.1 if (positions[i * 3 + 1] < -20) positions[i * 3 + 1] = 50; // 重置位置 } geometry.attributes.position.needsUpdate = true; // 通知Three.js更新数据 renderer.render(scene, camera); } animate();
为什么不推荐多几何体方案?
每个精灵单独创建几何体或Points对象会大幅增加Draw Call,当粒子数量较多时,会导致渲染性能急剧下降。单几何体的方案仅需1次Draw Call,能有效保证动画流畅度。
内容的提问来源于stack exchange,提问作者thdoan
相关产品推荐
相关产品推荐

