【愚公系列】2023年08月 Three.js专题-粒子特效案例


前言

粒子特效是一种视觉效果,可以模拟出许多粒子在空间中的运动和变化,形成各种美丽的图案和动态效果。常见的粒子特效包括烟雾、火焰、水流、星空、气泡等,可以在电影、电视、游戏等领域中得到广泛应用。实现粒子特效,需要使用计算机图形学技术,如粒子系统、计算流体力学等。

一、粒子特效案例

1.粒子平面

粒子平面是指在计算机图形学中,用于呈现粒子特效的平面。粒子特效是通过模拟大量的粒子运动和变化来实现的,而为了在视觉上呈现这些粒子,需要将它们以一定的规律排列在一个平面上,使得观众可以看到这些粒子的动态变化。因此,粒子平面常常被用作粒子特效的载体,通过对平面上的粒子进行位置、速度、颜色等的计算和控制,来实现各种不同的粒子效果。在实际应用中,粒子平面可以应用于游戏场景、电影特效、虚拟现实等领域中,为用户带来更加生动的视觉体验。

<!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>

在这里插入图片描述

2.粒子立方体

粒子立方体是一个三维的立方体网格,每个网格中包含一个粒子。粒子立方体广泛应用于流体动力学和材料科学等领域,用于模拟流体的运动和材料的行为。 在流体动力学中,粒子立方体被用来模拟流体的粒子。 这些粒子可以在立方体内相互作用,从而模拟流体的动态行为。 在材料科学中,粒子立方体可用于模拟材料的行为,如塑性变形,断裂等。 它也可以用于模拟材料的力学特性,如弹性模量和泊松比等。

<!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>

在这里插入图片描述

3.水波纹

水波纹是指水面上因为外界因素而产生的波动现象,通常表现为由中心向周围扩散的圆形波纹。水波纹可以由风吹、岸边拍打、水体震动等因素引起。在光线的折射与反射下,水波纹还会呈现出美丽的光影效果。水波纹是一种自然美景,也是很多人喜欢拍摄的主题之一。

<!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>

在这里插入图片描述

4.图片拆分重组

图片拆分重组是一种图像处理技术,它将一张图片拆分成多个小图片,再重新组合成新的图片。这种技术可以用于图片压缩、图片加密、图片转换等多种应用场景。

在图片拆分重组过程中,一般会将原始图片分成多个小块,每个小块可以是固定大小或者根据某种算法自适应大小。然后,可以通过不同的算法对这些小块进行重组,形成新的图片。比如可以按照某种规律将小块重新排列,或者通过加密算法对小块进行重新组合,从而生成一张新的图片。

图片拆分重组技术可以用于保护个人隐私、图像传输等领域,同时也可以用于美化图片、卡通化处理等应用。

<!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>

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/aa2528877987/article/details/132263364
今日推荐