[Yugong Series] Three.js Topic in August 2023 - Particle Special Effects Case


foreword

Particle effect is a visual effect that can simulate the movement and changes of many particles in space to form various beautiful patterns and dynamic effects. Common particle effects include smoke, flame, water flow, starry sky, bubbles, etc., which can be widely used in movies, TV, games and other fields. Realizing particle special effects requires the use of computer graphics techniques, such as particle systems, computational fluid dynamics, etc.

1. Particle effect case

1. Particle Plane

A particle plane refers to a plane used to present particle effects in computer graphics. Particle effects are realized by simulating a large number of particle movements and changes. In order to present these particles visually, they need to be arranged on a plane with certain rules so that the audience can see the dynamic changes of these particles. Therefore, the particle plane is often used as the carrier of particle effects, and various particle effects can be realized by calculating and controlling the position, speed, color, etc. of the particles on the plane. In practical applications, particle planes can be used in game scenes, movie special effects, virtual reality and other fields to bring users a more vivid visual experience.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    *{
      
      
      margin: 0;
      padding: 0;
    }
  </style>
  <script src="../lib/three/three.js"></script>
</head>
<body>

</body>
</html>

<script>
  const clock = new THREE.Clock()
  // 创建一个场景
  const scene = new THREE.Scene();

  // 创建一个相机 视点
  const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
  // 设置相机的位置
  camera.position.set(0,0,200);
  camera.lookAt(new THREE.Vector3(0,0,0));

  // 创建一个渲染器
  const renderer = new THREE.WebGLRenderer();
  // 设置渲染器尺寸
  renderer.setSize(window.innerWidth, window.innerHeight);

  document.body.appendChild(renderer.domElement);

  renderer.setClearColor(0xffffff);
  // 添加灯光
  const spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(2000,8000,4000);
  scene.add(spotLight);

  createSystemSprite();

  // 创建一个基础的粒子
  function createNormalSprite() {
      
      
    for (let i = -5; i < 5; i++) {
      
      
      for (let j = -5; j < 5; j++) {
      
      
        const material = new THREE.SpriteMaterial({
      
      
          color: Math.random() * 0xffffff
        })
        const sprite = new THREE.Sprite(material);
        sprite.position.set(i * 10, j * 10, 0);
        sprite.scale.set(2,2,2);
        scene.add(sprite);
      }
    }
  }

  // 粒子系统来创建粒子
  function createSystemSprite() {
      
      
    const geometry = new THREE.Geometry();
    const material = new THREE.PointCloudMaterial({
      
      
      size: 4,
      vertexColors: true,
    })

    for (let i = -5; i < 5; i++) {
      
      
      for (let j = -5; j < 5; j++) {
      
      
        geometry.vertices.push(new THREE.Vector3(i * 10, j * 10, 0))
        geometry.colors.push(new THREE.Color(Math.random() * 0xffffff))
      }
    }

    scene.add(new THREE.PointCloud(geometry, material))
  }

  const animation = () => {
      
      
    // 渲染
    renderer.render(scene, camera);

    requestAnimationFrame(animation);
  }
  animation()
</script>

insert image description here

2. Particle Cube

A Particle Cube is a three-dimensional grid of cubes, each containing one particle. Particle cubes are widely used in fields such as fluid dynamics and material science to simulate the motion of fluids and the behavior of materials. In fluid dynamics, particle cubes are used to model the particles of a fluid. These particles can interact within the cube, simulating the dynamic behavior of fluids. In materials science, particle cubes can be used to simulate the behavior of materials, such as plastic deformation, fracture, etc. It can also be used to simulate the mechanical properties of materials, such as elastic modulus and Poisson's ratio.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    *{
      
      
      margin: 0;
      padding: 0;
    }
  </style>
  <script src="../lib/three/three.js"></script>
  <script src="../lib/three/tween.min.js"></script>
  <script src="../lib/three/dat.gui.js"></script>
</head>
<body>

</body>
</html>

<script>
  const clock = new THREE.Clock()
  // 创建一个场景
  const scene = new THREE.Scene();

  // 创建一个相机 视点
  const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
  // 设置相机的位置
  camera.position.set(0,30,100);
  camera.lookAt(new THREE.Vector3(0,0,0));

  // 创建一个渲染器
  const renderer = new THREE.WebGLRenderer();
  // 设置渲染器尺寸
  renderer.setSize(window.innerWidth, window.innerHeight);

  document.body.appendChild(renderer.domElement);

  // 添加灯光
  const spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(2000,8000,4000);
  scene.add(spotLight);

  // 创建一个立方体
  const geometry = new THREE.BoxGeometry(10, 10, 10, 10, 10, 10);

  function getSprite() {
      
      
    const canvas = document.createElement('canvas')
    const size = 8
    canvas.width = size * 2;
    canvas.height = size * 2;

    const c = canvas.getContext('2d')

    const gradient = c.createRadialGradient(size, size, 0, size, size, size);
    gradient.addColorStop(0.1, 'rgba(0,255,255,1)')

    c.fillStyle = gradient;
    c.arc(size, size, size / 2, 0, Math.PI * 2);
    c.fill();

    const texture = new THREE.Texture(canvas)
    texture.needsUpdate = true;
    return texture;
  }

  // 存储原始坐标
  const indexList = [];

  // 设定当前随机的范围
  const range = 100;

  const controls = {
      
      
    polymeric: false, // 是否要组合成立方体
    completeMesh: false, // 组合之后是否要显示立方体
    showMesh: false, // 是否要现在显示立方体
  };

  let cloud;

  function createMesh() {
      
      
    cloud = new THREE.Mesh(new THREE.BoxGeometry(10,10,10,10,10,10), new THREE.MeshNormalMaterial())
    scene.add(cloud);
  }

  function createRandomPosition(i) {
      
      
    geometry.vertices[i].x = Math.random() * range - range / 2;
    geometry.vertices[i].y = Math.random() * range - range / 2;
    geometry.vertices[i].z = Math.random() * range - range / 2;
  }

  function createPointCloud() {
      
      

    let listen = false;
    for (let i = 0; i < geometry.vertices.length; i++) {
      
      
      indexList.push({
      
      
        x: geometry.vertices[i].x,
        y: geometry.vertices[i].y,
        z: geometry.vertices[i].z
      })
      createRandomPosition(i);

      if (controls.polymeric) {
      
      
        const tween = new TWEEN.Tween(geometry.vertices[i]).to(indexList[i], 2000).start();
        if (!listen) {
      
      
          listen = true;

          if (controls.completeMesh) {
      
      
            tween.onComplete(() => {
      
      
              scene.remove(cloud);
              createMesh();
            })
          }
        }
      }
    }


    const material = new THREE.PointCloudMaterial({
      
      
      size: 2,
      transparent: true,
      map: getSprite(),
    })

    cloud = new THREE.PointCloud(geometry, material);
    cloud.sortParticles = true;
    scene.add(cloud);
  }

  createPointCloud()

  const gui = new dat.GUI();
  const onChange = () => {
      
      
    scene.remove(cloud);
    controls.showMesh ? createMesh() : createPointCloud();
  }
  for (const key in controls) {
      
      
    gui.add(controls, key).onChange(onChange);
  }

  const animation = () => {
      
      
    scene.rotation.y += 0.01;
    // 渲染
    renderer.render(scene, camera);
    TWEEN.update();

    requestAnimationFrame(animation);
  }
  animation()
</script>

insert image description here

3. Water ripple

Water ripples refer to the fluctuations on the water surface due to external factors, usually appearing as circular ripples that spread from the center to the surrounding. Water ripples can be caused by factors such as wind blowing, shore beating, and water body vibration. Under the refraction and reflection of light, the water ripples will also present beautiful light and shadow effects. Water ripples are a natural beauty and one of the subjects that many people like to photograph.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    *{
      
      
      margin: 0;
      padding: 0;
    }
  </style>
  <script src="../lib/three/three.js"></script>
</head>
<body>

</body>
</html>

<script>
  const clock = new THREE.Clock()
  // 创建一个场景
  const scene = new THREE.Scene();

  // 创建一个相机 视点
  const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
  // 设置相机的位置
  camera.position.set(0,50,200);
  camera.lookAt(new THREE.Vector3(0,0,0));

  // 创建一个渲染器
  const renderer = new THREE.WebGLRenderer();
  // 设置渲染器尺寸
  renderer.setSize(window.innerWidth, window.innerHeight);

  document.body.appendChild(renderer.domElement);

  // 添加灯光
  const spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(2000,8000,4000);
  scene.add(spotLight);

  const total = 20;
  const spriteList = [];

  createNormalSprite();

  // 使用canvas贴图来实现圆形
  function getSprite() {
      
      
    const canvas = document.createElement('canvas')
    const size = 8
    canvas.width = size * 2;
    canvas.height = size * 2;

    const c = canvas.getContext('2d')

    c.fillStyle = '#00ff00';
    c.arc(size, size, size / 1.5, 0, Math.PI * 2);
    c.fill();

    const texture = new THREE.Texture(canvas)
    texture.needsUpdate = true;
    return texture;
  }

  // 创建一个基础的粒子
  function createNormalSprite() {
      
      
    const material = new THREE.SpriteMaterial({
      
      
      color: 0x008800,
      map: getSprite(),
    })
    for (let i = -total; i < total; i++) {
      
      
      for (let j = -total; j < total; j++) {
      
      
        const sprite = new THREE.Sprite(material);
        sprite.position.set(i * 10, 0, j * 10);
        spriteList.push(sprite);
        scene.add(sprite);
      }
    }
  }

  // 变化的速度
  const speed = 0.1;
  // 波浪的高度
  const height = 5;
  // 波浪的幅度
  const step = 0.3;

  let status = 0;

  const animation = () => {
      
      
    // 渲染
    renderer.render(scene, camera);

    requestAnimationFrame(animation);

    let index = -1;
    for (let x = 0; x < total * 2; x++) {
      
      
      for (let y = 0; y < total * 2; y++) {
      
      
        index++;

        spriteList[index].position.y = (Math.sin(x + status) * step) * height + (Math.sin(y + status) * step) * height

        // 缩放系数
        const scaleValue = (Math.sin(x + status) * step) + 1
        spriteList[index].scale.set(scaleValue,scaleValue,scaleValue)
      }
    }

    status += speed;
  }
  animation()
</script>

insert image description here

4. Image splitting and reorganization

Image splitting and reorganization is an image processing technology that splits a picture into multiple small pictures and then reassembles them into new pictures. This technology can be used in various application scenarios such as image compression, image encryption, and image conversion.

In the process of image splitting and reorganization, the original image is generally divided into multiple small blocks, and each small block can be of a fixed size or an adaptive size according to a certain algorithm. These small blocks can then be reorganized by different algorithms to form new pictures. For example, the small blocks can be rearranged according to a certain rule, or the small blocks can be recombined through an encryption algorithm to generate a new picture.

Image splitting and reorganization technology can be used in fields such as protection of personal privacy, image transmission, etc., and can also be used in applications such as beautifying images and cartoon processing.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    *{
      
      
      margin: 0;
      padding: 0;
    }
  </style>
</head>
<body>

</body>
</html>

<script type="module">
  import * as THREE from 'https://cdn.skypack.dev/[email protected]';
  const clock = new THREE.Clock()
  // 创建一个场景
  const scene = new THREE.Scene();

  // 创建一个相机 视点
  const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
  // 设置相机的位置
  camera.position.set(0,0,100);
  camera.lookAt(new THREE.Vector3(0,0,0));


  // 创建一个渲染器
  const renderer = new THREE.WebGLRenderer();
  // 设置渲染器尺寸
  renderer.setSize(window.innerWidth, window.innerHeight);

  document.body.appendChild(renderer.domElement);

  // 添加灯光
  const spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(2000,8000,4000);
  scene.add(spotLight);

  const geometry = new THREE.PlaneGeometry(50,50,50,50)
  const material = new THREE.MeshBasicMaterial({
      
      
    map: new THREE.TextureLoader().load('../assets/pikachu.png'),
  })

  const indexList = geometry.index.array;

  const {
      
       position, normal, uv } = geometry.attributes;

  const p = position.array;
  const n = normal.array;
  const u = uv.array;
  const positionList = []
  const uvList = []
  const normalList = []

  Array.from(indexList).forEach(i => {
      
      
    positionList.push(p[i * 3], p[i * 3 + 1], p[i * 3 + 2])

    normalList.push(n[i * 3], n[i * 3 + 1], n[i * 3 + 2])

    uvList.push(u[i * 2], u[i * 2 + 1])
  })

  // 缓冲几何体
  const bufferGeometry = new THREE.BufferGeometry()
  bufferGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positionList), 3))
  bufferGeometry.setAttribute('originPosition', new THREE.BufferAttribute(new Float32Array(positionList), 3))
  bufferGeometry.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normalList), 3))
  bufferGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvList), 2))

  const plane = new THREE.Mesh(bufferGeometry, material)
  scene.add(plane)

  // 生成一些配置信息,
  const animate = []
  for (let i = 0; i < positionList.length; i += 9) {
      
      
    animate.push({
      
      
      // 开始动画的时间
      // 动画的完成程度是多少
      // 每一个动画的起点、终点、和控制点信息
      startTime: null,
      process: 0,
      start: {
      
      x: 0, y: 0, z: 0},
      end: {
      
      x: 0, y: 0, z: 0},
      c1: {
      
       x: Math.random() * 10, y: Math.random() * 40 - 20, z: 0 },
      c2: {
      
       x: Math.random() * 10, y: Math.random() * 40 - 20, z: 0 },
    })
  }

  // 贝塞尔曲线
  const bezier = (start, c1, c2, end, p) => {
      
      
    const result = {
      
      }
    const key = ['x', 'y', 'z']

    const p1 = p * p * p
    const p2 = p * p * (1 - p)
    const p3 = p * (1 - p) * (1 - p)
    const p4 = (1 - p) * (1 - p) * (1 - p)

    for (const k of key) {
      
      
      result[k] =
        start[k] * p4 +
        c1[k] * p3 * 3 +
        c2[k] * p2 * 3 +
        end[k] * p1;
    }

    return result;
  }

  // 是否开始,开始的时间。总的动画时间
  let startTotal = Date.now();
  // 总的进度
  let progress = 0;
  const circle = 2000;
  const animation = () => {
      
      
    // 粒子运动
    // 进度
    progress = (Date.now() - startTotal) / circle
    if (progress > 1) {
      
      
      progress = 1;
    }

    const x = -50 / 2 + 50 * progress;
    animate.forEach((item,index) => {
      
      
      if (!item.startTime && positionList[index * 2] < x) {
      
      
        item.startTime = Date.now();
      }

      // 已经开始动画
      if (item.startTime && item.process < 1) {
      
      
        item.process = (Date.now() - item.startTime) / circle;
        if (item.process > 1) {
      
      
          item.process = 1;
        }

        for (let i = 0; i < 3; i ++) {
      
      
          const cIndex = index * 3 + i;

          const originPosition = {
      
      
            x: positionList[cIndex * 3],
            y: positionList[cIndex * 3 + 1],
            z: positionList[cIndex * 3 + 2]
          }

          const {
      
      start, end, c1, c2} = item;
          const bezierPosition = bezier(start, c1, c2, end, item.process);
          const newPosition = {
      
      
            x: originPosition.x + bezierPosition.x,
            y: originPosition.y + bezierPosition.y,
            z: originPosition.z + bezierPosition.z,
          }

          bufferGeometry.attributes.position.setXYZ(cIndex, newPosition.x, newPosition.y, newPosition.z);
        }
      }
    })
    plane.geometry = bufferGeometry.clone();

    // 渲染
    renderer.render(scene, camera);

    requestAnimationFrame(animation);
  }
  animation()
</script>

insert image description here

Guess you like

Origin blog.csdn.net/aa2528877987/article/details/132263364