1. Three.js 精灵材质与点云

在 Three.js 中,精灵(Sprite)材质点云(Points,原 PointCloud) 都是用于高效渲染大量相似对象的特性,但它们有不同的应用场景和工作方式。

1.1. 精灵材质 (SpriteMaterial)

精灵是一种始终面向相机的 2D 平面对象,非常适合用于制作:

1.1.1. 基本使用

javascript

// 创建精灵材质
const spriteMaterial = new THREE.SpriteMaterial({
  color: 0xff0000,
  map: new THREE.TextureLoader().load('texture.png'),
  transparent: true
});

// 创建精灵
const sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(2, 2, 1); // 设置大小
scene.add(sprite);

1.1.2. 精灵材质属性

javascript

const material = new THREE.SpriteMaterial({
  color: 0xffffff,     // 颜色
  map: texture,        // 纹理
  transparent: true,   // 透明度
  opacity: 0.8,        // 不透明度
  rotation: Math.PI/4  // 旋转(弧度)
});

1.2. 点云 (Points)

点云用于高效渲染大量点(粒子),每个点可以使用相同的材质。

1.2.1. 基本使用

javascript

// 创建几何体,添加顶点
const geometry = new THREE.BufferGeometry();
const count = 10000;
const positions = new Float32Array(count * 3);

for (let i = 0; i < count * 3; i++) {
  positions[i] = (Math.random() - 0.5) * 100;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));

// 创建点云材质
const material = new THREE.PointsMaterial({
  color: 0xffffff,
  size: 0.5,          // 点的大小
  transparent: true
});

// 创建点云
const points = new THREE.Points(geometry, material);
scene.add(points);

1.2.2. 点云材质属性

javascript

const pointsMaterial = new THREE.PointsMaterial({
  color: 0xffffff,
  size: 1.0,           // 点大小
  sizeAttenuation: true, // 透视衰减
  map: texture,        // 点的纹理
  transparent: true,
  opacity: 0.8,
  alphaTest: 0.5       // 透明度测试
});

1.3. 主要区别对比

主要区别对比.png

1.4. 进阶用法

1.4.1. 精灵贴图动画

javascript

// 使用精灵表(sprite sheet)
const texture = new THREE.TextureLoader().load('spritesheet.png');
texture.repeat.set(1/6, 1/6); // 6x6 精灵表
texture.needsUpdate = true;

const spriteMaterial = new THREE.SpriteMaterial({ map: texture });

1.4.2. 点云着色器自定义

javascript

 const vertexShader = `    
    varying vec3 vColor;
    uniform float time;
    uniform float size;

    void main() {
      vColor = color;

      // 添加脉动动画
      float pulse = sin(time * 2.0 + position.x * 0.1) * 0.5 + 0.5;

      // 计算最终位置
      vec3 pos = position;
      pos *= 1.0 + pulse * 0.2; // 脉动效果

      vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
      gl_PointSize = size * (8.0 / -mvPosition.z) * (1.0 + pulse * 0.3);
      gl_Position = projectionMatrix * mvPosition;
    }
  `;
  const fragmentShader = `
    varying vec3 vColor;

    void main() {
      // 创建圆形点
      vec2 coord = gl_PointCoord - vec2(0.5);
      float distance = length(coord);

      if (distance > 0.5) {
        discard;
      }
      // 渐变透明度
      float alpha = 1.0 - smoothstep(0.3, 0.5, distance);
      // 输出颜色
      gl_FragColor = vec4(vColor, alpha);
    }
  `;
  // 创建着色器材质
  const customMaterial = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    transparent: true,
    vertexColors: true,
    uniforms: {
      time: { value: 0.0 },
      size: { value: 5.0 }
    }
  });

1.5. 性能优化建议

  1. 精灵优化

  2. 点云优化

1.6. 实际应用示例

javascript

// 创建粒子系统(雨滴效果)
function createRainParticles() {
  const geometry = new THREE.BufferGeometry();
  const particleCount = 5000;

  // 位置
  const positions = new Float32Array(particleCount * 3);
  const velocities = new Float32Array(particleCount);

  for (let i = 0; i < particleCount; i++) {
    positions[i * 3] = Math.random() * 200 - 100;
    positions[i * 3 + 1] = Math.random() * 100 + 50;
    positions[i * 3 + 2] = Math.random() * 200 - 100;
    velocities[i] = Math.random() * 0.5 + 0.5;
  }

  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
  geometry.setAttribute('velocity', new THREE.BufferAttribute(velocities, 1));

  const material = new THREE.PointsMaterial({
    color: 0xaaaaaa,
    size: 0.5,
    transparent: true,
    opacity: 0.8
  });

  const rain = new THREE.Points(geometry, material);

  // 动画循环中更新位置
  function animateRain() {
    const positions = rain.geometry.attributes.position.array;

    for (let i = 0; i < particleCount; i++) {
      positions[i * 3 + 1] -= velocities[i];

      if (positions[i * 3 + 1] < -50) {
        positions[i * 3 + 1] = 100;
      }
    }

    rain.geometry.attributes.position.needsUpdate = true;
  }

  return { rain, animateRain };
}

根据具体需求选择: