three.js -- Particles 粒子

591 阅读1分钟

// 创建空的几何体
const particlesGeometry = new THREE.BufferGeometry(1, 32, 32)

// 创建粒子材质,点材质
const particlesMaterial = new THREE.PointsMaterial()
particlesMaterial.size = 0.1 /* 点大小 */
particlesMaterial.sizeAttenuation = true /* 点根据相机深度衰减 */

// 添加随机粒子
const count = 500
const positions = new Float32Array(count * 3) /* 随机位置 */
const colors = new Float32Array(count * 3) /* 随机颜色--color */

for(let i = 0; i <= count * 3; i++) {
  positions[i] = (Math.random() - 0.5) * 10
  colors[i] = Math.random
}
particlesGeometry.seAttribute(
  'position',
  new THREE.BufferAttribute(positions, 3)
)
// 添加随机颜色--color
particlesGeometry.seAttribute(
  'color',
  new THREE.BufferAttribute(colors, 3)
)

// 创建粒子贴图
const textureLoader = new THREE.TextureLoader()
const particleTexture = textureLoader.load('/textures/particles/2.png')
particlesMaterial.color = new THREE.Color(0xff0000)
// 使用透明贴图,有个问题是GPU根据绘制顺序会遮挡后面的粒子
particlesMaterial.transparent = true
particlesMaterial.alphaMap = particleTexture
// 解决粒子遮挡
// 1: 设置运行alphaTest时要使用的alpha值
particlesMaterial.alphaTest = 0.01
// 2: 这种,如果有其他几何体存在会出现渲染混乱
particlesMaterial.depthTest = false
// 3: 完美方案 渲染此材质是否对深度缓冲区有任何影响
particlesMaterial.depthWrite = false
// 4: 混合模式:在使用此材质显示对象时要使用何种混合。
particlesMaterial.blending = THREE.AdditiveBlending

// 设置颜色使用顶点着色--color
particlesMaterial.vertexColors = true

// 粒子波浪效果
const tick = () => {
  const elapsedTime = clock.getElapsedTime()
  // 动画操作,就是更新每个粒子的点位
  for(let i = 0; i < count; i++) {
    const i3 = i*3
    const x = particlesGeometry.attributes.position.array[i3]
    particlesGeometry.attributes.position.array[i3 + 1] = Math.sin(elapsedTime + x)
  }
  // 开启更新
  particlesGeometry.attributes.position.needsUpdate = true
  
  controls.update()
  renderer.render(scene, camera)
  window.requestAnimationFrame(tick)
}
tick()

粒子星球案例

/* Galaxy 星球 */
const parameters = {
  count: 1000,
  size: 0.02,
  raduis: 5, /* 星系半径 */
  branches: 3, /* 星系分支 */
  spin: 3, /* 星系分支的旋转 */
  randomness: 0.2, /* 星系分支的随机性 */
  randomnessPower: 3, /* 星系分支的随机幂 */
  insideColor: '#ff6030', /* 星系分支里面的颜色 */
  outsideColor: '#1b3984' /* 星系分支外面的颜色 */
}

const geometry = null
const material = null
const points = null


const generateGalaxy = () => {
  // 优化性能处理
  if(points !== null) {
    geometry.dispose()
    material.dispose() /* 处理材质。材质的纹理不会被处理 */
    scene.remove(points) /* 删除场景中的点 */
  }
  // geometry 几何体
  geometry = new THREE.BufferGeometry()
  // 定位和颜色值
  const positions = new Float32Array(parameters.count * 3)
  const colors = new Float32Array(parameters.count * 3)
  
  const colorInside = new THREE.Color(parameters.insideColor)
  const colorOutside = new THREE.Color(parameters.outsideColor)
  
  for(let i = 0; i < parameters.count; i++) {
    const i3 = i * 3
    
    const raduis = Math.random() * parameters.raduis /*半径随机值*/
    const spinAngle = raduis * parameters.spin /* 自旋 */
    const branchAngle = (i % parameters.branches) / parameters.branches * Math.PI * 2
    
    /* 星系的随机性 */
    const randomX = Math.pow(Math.random, parameters.randomnessPower) * (Math.random() < 0.5 ? 1 : -1)
    const randomY = Math.pow(Math.random, parameters.randomnessPower) * (Math.random() < 0.5 ? 1 : -1)
    const randomZ = Math.pow(Math.random, parameters.randomnessPower) * (Math.random() < 0.5 ? 1 : -1)
    positions[i3    ] = Math.cos(branchAngle + spinAngle) * raduis + randomX
    positions[i3 + 1] = randomY
    positions[i3 + 2] = Math.sin(branchAngle + spinAngle) * raduis + randomZ
    
    /* 星系的颜色将两种颜色混合 */
    const mixedColor = colorInside.clone() /* 需要先克隆一个值,不然会出现混乱 */
    mixedColor.lerp(colorOutside, raduis / parameters.raduis)
    colors[i3    ] = mixedColor.r
    colors[i3 + 1] = mixedColor.g
    colors[i3 + 2] = mixedColor.b
  }
  
  geometry.setAttribute(
    'position',
    new THREE.BufferAttribute(positions, 3)
  )
  /* 使用顶点着色需要给一组新的顶点数据 */
  geometry.setAttribute(
    'color',
    new THREE.BufferAttribute(colors, 3)
  )
  
  // material 材质
  material = new THREE.PointsMaterial({
    size: parameters.size,
    sizeAttenuation: true,
    depthWrite: false,
    blending: THREE.AdditiveBlending,
    vertexColors: true, /* 使用顶点着色 */
  })
  
  // Points 创建点
  points = new THREE.Points(geometry, material)  
  scene.add(points)
}
generateGalaxy()

gui.add(parameters, 'count').min(100).max(10000).step(100).onFinishChange(generateGalaxy) 
// 每当更改时触发函数
gui.addColor(this.parameters, 'insideColor').onFinishChange(generateGalaxy)