Three.js做一个粒子发射器

import * as THREE from "three";
import TWEEN from "@tweenjs/tween.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import gltfUrl from "@/assets/models/11-5-1-processed.glb?url";
import pngUrl from "@/assets/textures/raindrop.png?url";

window.THREE = THREE;
export default function () {
  let camera: THREE.PerspectiveCamera,
    scene: THREE.Scene,
    renderer: THREE.WebGLRenderer,
    controls: OrbitControls;
  const textureLoader = new THREE.TextureLoader();
  const container = document.createElement("div");
  let clock = new THREE.Clock();
  let group: THREE.Group;
  let itemHeight: number;

  init();

  return container;

  function init() {
    createScene();
    createObj();
    createRenderer();
    createCamera();
    createControls();
    animate();
  }

  function createObj() {
    // 创建的单个粒子的尺寸为(radius * 2, height + radius * 2, radius * 2)
    const radius = 4;
    const height = 192;
    itemHeight = radius * 2 + height;
    // 存放样条曲线的点集
    const points = [];
    //上半部分四分之一圆弧
    for (let i = Math.PI / 2; i > 0; i -= 0.3) {
      points.push(
        new THREE.Vector2(
          Math.cos(i) * radius,
          Math.sin(i) * radius + height / 2
        )
      );
    }
    //中间直线
    for (let i = height / 2; i > -height / 2; i -= height) {
      points.push(new THREE.Vector2(radius, i));
    }
    //下半部分四分之一圆弧
    for (let i = 0; i <= Math.PI / 2; i += 0.3) {
      points.push(
        new THREE.Vector2(
          Math.cos(i) * radius,
          -Math.sin(i) * radius - height / 2
        )
      );
    }

    // 补充一个点,去掉底部的小洞
    points.push(new THREE.Vector2(0, -radius - height / 2));
    console.log(points);
    const geometry = new THREE.LatheGeometry(points);
    const pngec = textureLoader.load(pngUrl);
    // 围绕中心点旋转180度
    // pngec.center = new THREE.Vector2(0.5, 0.5);
    // pngec.rotation = Math.PI;
    // pngec.mapping = THREE.UVMapping;
    const material = new THREE.MeshBasicMaterial({
      transparent: true,
      opacity: 0.6,
      vertexColors: false,
      map: pngec,
      blending: THREE.AdditiveBlending,
      color: new THREE.Color(0xffffff),
    });
    const mesh = new THREE.Mesh(geometry, material);

    group = new THREE.Group();
    group.name = "粒子发射器";

    for (let i = 0; i < 10; i++) {
      const copy_mesh = mesh.clone();
      const position = new THREE.Vector3(
        THREE.MathUtils.randFloat(0, 1000),
        0,
        THREE.MathUtils.randFloat(0, 1000)
      );
      copy_mesh.position.set(position.x, position.y, position.z);
      copy_mesh.userData = {
        startTime: THREE.MathUtils.randFloat(0, 6),
        speed: THREE.MathUtils.randFloat(1, 10),
        // speed: 1,
      };
      copy_mesh.scale.set(1, 0, 1);
      group.add(copy_mesh);
    }
    scene.add(group);
  }

  function createCamera() {
    camera = new THREE.PerspectiveCamera(
      45,
      window.innerWidth / window.innerHeight,
      1,
      15000
    );
    camera.position.set(200, 500, 200);
  }

  function createScene() {
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0x333333);
    scene.add(new THREE.AxesHelper(1000));
  }

  function createControls() {
    controls = new OrbitControls(camera, renderer.domElement);
  }

  function createRenderer() {
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);
  }

  //
  function animate() {
    requestAnimationFrame(animate);
    // 获取动画已经开始的时间
    const elapsedTime = clock.getElapsedTime();
    const topHeight = 1000;
    group.traverse((obj) => {
      // 粒子在y轴,由0到1000之间移动
      if (obj.type === "Mesh") {
        const startTime = obj.userData.startTime;
        const speed = obj.userData.speed;
        if (elapsedTime >= startTime || true) {
          const position_y = obj.position.y;
          // 用于获取物体的物理尺寸
          const sizeVect = new THREE.Vector3();
          const box = new THREE.Box3().setFromObject(obj);
          box.getSize(sizeVect);
          // 以下用于处理缩放
          let scaleY = obj.scale.y;
          if (position_y <= itemHeight / 2) {
            // 处于增长阶段
            scaleY = scaleY + speed / itemHeight;
          } else if (position_y >= topHeight - itemHeight / 2) {
            // 处于缩小阶段
            scaleY = scaleY - speed / itemHeight;
          }
          // 处理缩放的极限值
          if (scaleY >= 1) scaleY = 1;
          if (scaleY <= 0) scaleY = 0;

          obj.scale.setY(scaleY);
          // position记录的是中心点
          // 以下用于处理位移
          let add_posi_y = position_y + (scaleY === 1 ? speed : speed / 2);
          if (add_posi_y >= topHeight) add_posi_y = 0;
          obj.position.setY(add_posi_y);
          // console.log(box.max);
        }
      }
    });

    controls.update();
    renderer.render(scene, camera);
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容