ThreeJs + vue 写星空背景

475 阅读2分钟

最近成都找工作太难了,找工作的途中看到了很多需要熟练ThreeJS的公司,正好想起了在公司时老板提的一个要求,我需要一个星空背景;具体效果如下展示:

动画.gif

直接进入正题

1、创建vue项目(vue官方网站上面去看,这一步略过)

2、引入ThreeJs

npm instll three

接下来就是代码环节了

首先我们要创建一个vue的模板,在模板中需要创建一div的容器,然后给上Id为 containerStar

<div id="containerStar"></div>

引入threejs

import * as THREE from "three";
// 注意要使用控制器,需要导入OribitControls而且是在js文件当中导入
// 创建控制器的时候也是直接  new Orbitcontrols
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

创建需要使用的初始数据

data() {
    return {
      positions: [],
      colors: [],
      triangles: 10000,
    };
  },

初始化

初始化的时候需要创建场景对象、创建粒子系统、相机设置、创建渲染器

init() {
      //  创建场景对象Scene
      this.scene = new THREE.Scene();

      this.creatGeometry() // 创建粒子系统
      //点光源
      let point = new THREE.PointLight(0xffffff);
      point.position.set(400, 200, 300); //点光源位置
      this.scene.add(point); //点光源添加到场景中
      //环境光
      let ambient = new THREE.AmbientLight(0xffffff);
      this.scene.add(ambient);
      
      /**
       * 相机设置
       */
      let container = document.getElementById("containerStar");
      this.camera = new THREE.PerspectiveCamera(70,container.clientWidth / container.clientHeight,1,100000);
      // this.camera.position.set(0, 0, 0); //设置相机位置
      this.camera.lookAt(this.scene.position);
      // this.camera.position.z = 1;

      /**
       * 创建渲染器对象
       */
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.renderer.setSize(container.clientWidth, container.clientHeight);
      this.renderer.setClearColor(0x000000, 1); //设置背景颜色
      container.appendChild(this.renderer.domElement);

      //创建控件对象
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    },

创建粒子系统

creatGeometry() {
      this.triangles = 10000;
      this.geometry = new THREE.BufferGeometry();
      this.positions = [];
      this.normals = [];
      this.colors = [];

      const color = new THREE.Color();

      const n = 800,n2 = n / 2; // triangles spread in the cube
      const d = 1,d2 = d / 2; // individual triangle size

      const pA = new THREE.Vector3();
      const pB = new THREE.Vector3();
      const pC = new THREE.Vector3();

      const cb = new THREE.Vector3();
      const ab = new THREE.Vector3();

      for (let i = 0; i < this.triangles; i++) {
        // positions

        const x = Math.random() * n - n2;
        const y = Math.random() * n - n2;
        const z = Math.random() * n - n2;

        const ax = x + Math.random() * d - d2;
        const ay = y + Math.random() * d - d2;
        const az = z + Math.random() * d - d2;

        const bx = x + Math.random() * d - d2;
        const by = y + Math.random() * d - d2;
        const bz = z + Math.random() * d - d2;

        const cx = x + Math.random() * d - d2;
        const cy = y + Math.random() * d - d2;
        const cz = z + Math.random() * d - d2;

        this.positions.push(ax, ay, az);
        // this.positions.push(bx, by, bz);
        // this.positions.push(cx, cy, cz);

        // flat face normals

        pA.set(ax, ay, az);
        pB.set(bx, by, bz);
        pC.set(cx, cy, cz);

        cb.subVectors(pC, pB);
        ab.subVectors(pA, pB);
        cb.cross(ab);

        cb.normalize();

        const nx = cb.x;
        const ny = cb.y;
        const nz = cb.z;

        this.normals.push(nx, ny, nz);
        // this.normals.push(nx, ny, nz);
        // this.normals.push(nx, ny, nz);
        // colors

        const vx = x / n + 0.5;
        const vy = y / n + 0.5;
        const vz = z / n + 0.5;

        color.setRGB(vx, vy, vz);

        // const alpha = Math.random();

        this.colors.push(color.r, color.g, color.b);
        // this.colors.push(color.r, color.g, color.b);
        // this.colors.push(color.r, color.g, color.b);
      }

      this.geometry.setAttribute("position",new THREE.Float32BufferAttribute(this.positions, 3));
      this.geometry.setAttribute("color",new THREE.Float32BufferAttribute(this.colors, 3));
      this.geometry.setAttribute("normal",new THREE.Float32BufferAttribute(this.normals, 3) );
      
      this.geometry.computeBoundingSphere();
      const material = new THREE.PointsMaterial({
        color: 0xffffff,
        size: 0.5,
        alphaMap: 0x000000,
        sizeAttenuation: true,
        // morphTargets: false,
        map: this.generateSprite(), //给粒子转变形状
      });

      this.mesh = new THREE.Points(this.geometry, material);
      // this.mesh.matrix = true
      // console.log(this.mesh,this.mesh.geometry.attributes.position );

      this.group = new THREE.Group();
      this.group.add(this.mesh);
      this.scene.add(this.group);
    },

粒子虽然已经创建了,但是还是圆形的,与我们想要的星星是不一样的,星星在运动的时候是有光晕或者尾气的。所以我们需要给粒子变形

generateSprite() {
      this.canvas = document.createElement("canvas");
      this.canvas.width = 20;
      this.canvas.height = 20;

      var context = this.canvas.getContext("2d");
      var gradient = context.createRadialGradient(
        this.canvas.width / 2,
        this.canvas.height / 2,
        0,
        this.canvas.width / 2,
        this.canvas.height / 2,
        this.canvas.width / 2
      );
      gradient.addColorStop(0, "rgba(255,255,255,1)");
      // gradient.addColorStop(0.2, 'rgba(0,255,255,1)');
      // gradient.addColorStop(0.4, 'rgba(0,0,64,1)');
      gradient.addColorStop(1, "rgba(0,0,0,1)");

      context.fillStyle = gradient;
      context.fillRect(0, 0, this.canvas.width, this.canvas.height);

      var texture = new THREE.Texture(this.canvas);
      texture.needsUpdate = true;
      return texture;
    },

最后一步就是让所有的粒子都动起来了

animate() {

      requestAnimationFrame(this.animate);

      this.renderer.clear();
      
      let data = this.mesh.geometry.attributes.position.array;
      // // let dataView = new DataView( buffer );
      let normalData = [];
      for (let i = 0; i < data.length / 3; i++) {
        normalData[3 * i] = data[3 * i];
        normalData[3 * i + 1] = data[3 * i + 1];
        if (data[3 * i + 2] > 400) {
          normalData[3 * i + 2] = -400;
        } else {
          normalData[3 * i + 2] = data[3 * i + 2] + 1;
        }
      }
      this.geometry.setAttribute(
        "position",
        new THREE.Float32BufferAttribute(normalData, 3)
      );
      this.mesh.geometry.attributes.position.needsUpdate = true;
      this.renderer.render(this.scene, this.camera);
    },

结语:第一次写文章不知道怎么弄就直接贴代码了,希望能满足你们的一个需求,谢谢