You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Three.js中用单个几何体实现多个精灵单向动画?

实现统一方向的精灵下落动画(无需多几何体)

不需要为每个精灵单独创建几何体,用单几何体就能高效实现,核心是利用顶点属性结合着色器动画,或者通过BufferGeometry的动态更新在CPU端处理,以下是两种可行方案:

方案1:GPU端着色器动画(性能最优)

原示例的旋转是针对整个Points对象,要实现粒子沿统一方向(比如Y轴向下)移动,在着色器中处理动画是最优选择,避免CPU频繁更新数据。

  1. 给几何体添加自定义属性
    为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));
    
  2. 自定义顶点着色器
    修改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;
      }
    });
    
  3. 动画循环中更新时间参数

    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属性,需开启动态标记优化性能:

  1. 初始化动态几何体

    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); // 标记为动态属性,减少更新开销
    
  2. 动画循环中更新位置

    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 20:50:36