美文网首页
three.js - Particles

three.js - Particles

作者: 闪电西兰花 | 来源:发表于2024-02-20 17:35 被阅读0次
  • Particles can be used to create stars, smoke, rain, dust, fire, etc.
  • You can have thousands of them with a reasonable frame rate(帧率)
  • Each particles is composed of plane(two triangles) always facing the camera
  • Create particles is like to create Mesh:
  • Set up
  // 导入three.js
  import * as THREE from 'three'
  // 导入轨道控制器
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

  // scene
  const scene = new THREE.Scene()
  
  // camera
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    100
  )
  camera.position.z = 3

  // render
  const renderer = new THREE.WebGLRenderer()
  renderer.setSize(window.innerWidth, window.innerHeight)
  document.body.appendChild(renderer.domElement)

  window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()

    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  })

  // axesHelper (根据自己的需要选择是否添加坐标轴)
  const axesHelper = new THREE.AxesHelper(5)
  scene.add(axesHelper)

  // controls
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true

  // animate
  const clock = new THREE.Clock()
  function animate () {
    let elapsedTime = clock.getElapsedTime()

    controls.update()
    requestAnimationFrame(animate)
    renderer.render(scene, camera)
  }
  animate()
  • Simple particles(完成后如下图)
  /**
   * particles
   */
  // Geometry
  const particlesGeometry = new THREE.SphereGeometry(1, 32, 32)  // 球体 - 半径、水平和垂直分段数

  // Material
  const particlesMaterial = new THREE.PointsMaterial({
    size: 0.02, // 粒子大小
    sizeAttenuation: true, // 尺寸衰减,产生透视效果,粒子距离camera越远看起来越小
  })

  // Points
  const particles = new THREE.Points(particlesGeometry, particlesMaterial)
  scene.add(particles)
Simple particles.png
  • Custom geometry 自定义几何图形
  /**
   * particles
   */
  // Geometry
  const particlesGeometry = new THREE.BufferGeometry()
  const count = 500 // bufferGeometry个数
  const positions = new Float32Array(count * 3) // 参数表示数组长度,每个bufferGeometry有 x,y,z 3个坐标点
  // 填充positions数组
  for(let i = 0; i < count * 3; i++) {
    positions[i] = (Math.random() - 0.5) * 10  // 事粒子居中并扩大显示范围
  }
  // 3个值为一组(向缓冲区几何体添加属性)
  particlesGeometry.setAttribute(
    'position', 
    new THREE.BufferAttribute(positions, 3)
  )

  // Material
  const particlesMaterial = new THREE.PointsMaterial({
    size: 0.02, // 粒子大小
    sizeAttenuation: true, // 尺寸衰减,产生透视效果,粒子距离camera越远看起来越小
  })

  // Points
  const particles = new THREE.Points(particlesGeometry, particlesMaterial)
  scene.add(particles)
Custom geometry.png
  • Color, Map and Alpha Map
    • 添加简单的颜色、texture设置,仔细观察下图会发现前面的parcles遮挡了后面的,并且有明显的边缘
    /**
     * Textures
    */
    const textureLoader = new THREE.TextureLoader()
    const particleTexture = textureLoader.load('../public/imgs/particles/2.png')
    ...
    ...
    // Material
    const particlesMaterial = new THREE.PointsMaterial({
      color: '#ff88cc',
      size: 0.1, // 粒子大小
      sizeAttenuation: true, // 尺寸衰减,产生透视效果,粒子距离camera越远看起来越小
      map: particleTexture,
    })
    
    添加简单的颜色、texture设置.png
    • 理想中的效果应当是除圆环以外的部分都是透明不可见的,不会出现前后遮挡的问题,解决这个问题首先:activate the transparency and use the texture on the alphaMap instead of the map,但是修改后还是无法完美解决,遮挡的问题看似解决了,但是particle的边缘有时还依然存在
    // Material
    const particlesMaterial = new THREE.PointsMaterial({
      color: '#ff88cc',
      size: 0.1, // 粒子大小
      sizeAttenuation: true, // 尺寸衰减,产生透视效果,粒子距离camera越远看起来越小
      transparent: true,
      alphaMap: particleTexture,  // webgl对一个粒子的渲染和创建是同时的,同样黑色部分透明度都为0,就无法分辨前后
    })
    
    • we can still see the edges of the particles, this is because the particles are drawn in the same order as they are created, and WebGl doesn't really know which on is on front of the other

      edges of the particles.png
    • there are multiple ways to fix it, first we can use the alpha test, the alphaTest is a value between 0 and 1 that enables the WebGl to know when not to render the pixel according to the pixel's transparency, 有一定的效果,但是particle的边缘依然不能很好的重叠,前后遮挡效果一般

    // Material
    const particlesMaterial = new THREE.PointsMaterial({
      color: '#ff88cc',
      size: 0.1, // 粒子大小
      sizeAttenuation: true, // 尺寸衰减,产生透视效果,粒子距离camera越远看起来越小
      transparent: true,
      alphaMap: particleTexture,  // webgl对一个粒子的渲染和创建是同时的,同样黑色部分透明度都为0,就无法分辨前后
      alphaTest: 0.01, // 依然会有边缘出现
    })
    
    alphaTest的边缘问题.png
    • the second we can use depth test, the WebGl tests if what's being drawn is closer than what's already drawn, if it's behind , the particles won't be drawn; but it can create bugs if you have other objects in your scene or particles with different colors
    // Material
    const particlesMaterial = new THREE.PointsMaterial({
      color: '#ff88cc',
      size: 0.1, // 粒子大小
      sizeAttenuation: true, // 尺寸衰减,产生透视效果,粒子距离camera越远看起来越小
      transparent: true,
      alphaMap: particleTexture,  // webgl对一个粒子的渲染和创建是同时的,同样黑色部分透明度都为0,就无法分辨前后
      // alphaTest: 0.01, // 依然会有边缘出现
      depthTest: false, // 不适用页面有多个不同颜色物体,会出现穿透效果,测试当前正在渲染的粒子是否在其他粒子前面,behind就不画
    })
    ...
    ...
    // Cube
      const cube = new THREE.Mesh(
      new THREE.BoxGeometry(),
      new THREE.MeshBasicMaterial()
    )
    scene.add(cube)
    
    depthTest出现的穿透效果.png
    • the third we can use depth write, the depth of what's being drawn is stored in what we call depth buffer(深度缓冲区), instead of not testing if the particle is closer than what's in this depth buffer, we can tell the WebGl not to write particles in that depth buffer, 我们可以理解为有一个正在绘制的灰度处理区域,在这个区域里同时绘制当前正在绘制的内容
    // Material
    const particlesMaterial = new THREE.PointsMaterial({
      color: '#ff88cc',
      size: 0.1, // 粒子大小
      sizeAttenuation: true, // 尺寸衰减,产生透视效果,粒子距离camera越远看起来越小
      transparent: true,
      alphaMap: particleTexture,  // webgl对一个粒子的渲染和创建是同时的,同样黑色部分透明度都为0,就无法分辨前后
      // alphaTest: 0.01, // 依然会有边缘出现
      // depthTest: false, // 不适用页面有多个不同颜色物体,会出现穿透效果,测试当前正在渲染的粒子是否在其他粒子前面,behind就不画
      depthWrite: false, // depth buffer存储已渲染的部分
    })
    
    depthWrite解决了穿透的问题.png
    • the last solution is Blending, with the blending property, we can tell the WebGl to add the color of the pixel to the color of the pixel already drawn, 在绘制过程中颜色是层层叠加的,所以我们会看到叠加的部分很亮,接近白色;使用 blending可能会影响性能,因此建议不要同时渲染太多或太大的particles
    // Material
    const particlesMaterial = new THREE.PointsMaterial({
      color: '#ff88cc',
      size: 0.1, // 粒子大小
      sizeAttenuation: true, // 尺寸衰减,产生透视效果,粒子距离camera越远看起来越小
      transparent: true,
      alphaMap: particleTexture,  // webgl对一个粒子的渲染和创建是同时的,同样黑色部分透明度都为0,就无法分辨前后
      // alphaTest: 0.01, // 依然会有边缘出现
      // depthTest: false, // 不适用页面有多个不同颜色物体,会出现穿透效果,测试当前正在渲染的粒子是否在其他粒子前面,behind就不画
      depthWrite: false, // depth buffer存储已渲染的部分
      blending: THREE.AdditiveBlending, // 颜色叠加
    })
    
    blending.png
  • Different Color of each particle,使用顶点着色
  /**
   * particles
   */
  // Geometry
  ...
  const colors = new Float32Array(count * 3)  // 颜色是由 r,g,b 组成的
  // 填充positions数组
  for(let i = 0; i < count * 3; i++) {
    ...
    colors[i] = Math.random()
  }
  // 3个值为一组(向缓冲区几何体添加属性)
  ...
  particlesGeometry.setAttribute(
    'color',
    new THREE.BufferAttribute(colors, 3)
  )

  // Material
  const particlesMaterial = new THREE.PointsMaterial({
    // color: '#ff88cc',
    ...
    depthWrite: false, // depth buffer存储已渲染的部分
    blending: THREE.AdditiveBlending, // 颜色叠加
    vertexColors: true, // 顶点颜色
  })
Different Color of each particle.png
  • Animate
    • make the particles move up and down, like waves
    • use the points as an object, 设置每个particle的动画
    • we can update each vertex separately in particlesGeometry.attributes.position.array, 这是一个一维数组
  const clock = new THREE.Clock()
  function animate () {
    let elapsedTime = clock.getElapsedTime()

    // update particles
    for(let i = 0; i < count; i++) {  // count表示particles的数量,但是每个particle都有x,y,z3个坐标信息
      const i3 = i * 3  // 对应每个particle的x坐标值
      const x = particlesGeometry.attributes.position.array[i3]
      particlesGeometry.attributes.position.array[i3 + 1] = Math.sin(elapsedTime + x)
    }
    particlesGeometry.attributes.position.needsUpdate = true  // geometry的属性改变时需要重置

    controls.update()
    requestAnimationFrame(animate)
    renderer.render(scene, camera)
  }
  animate()
particles动画的状态.png

相关文章

网友评论

      本文标题:three.js - Particles

      本文链接:https://www.haomeiwen.com/subject/upjzgdtx.html