vue+threejs写物体效果:模型闪烁

1,520 阅读3分钟

本文已参加「新人创作礼」活动,一起开启掘金创作之旅。


写在前面

看了一下thingjs的例子,它的模型闪烁是根据Clock启动时间的变化进行透明度的改变实现功能。

image.png

根据它的写法,尝试使用vue+threejs进行模型闪烁功能的实现。

效果演示

20220916_153625.gif

使用的模型是threejs官网例子里的一个小车模型

完整代码说明

  1. 创建一个容器
<template>
  <div class="item">
    <div id="THREE31"></div>
  </div>
</template>
  1. 引入用到的threejs模块和GUI

OrbitControls是threejs提供的轨道控制器,用了它,整个场景就能随鼠标的操作进行旋转缩放。

ThreeMFLoader是thrrejs提供的一个文件加载器,可以用来加载.3mf文件,因为我选的模型是.3mf的,所以引入该模块,threejs还支持其他很多文件类型的模型的加载器,感兴趣的可以自己去thrrejs的官网看看。

import * as THREE from "three";

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { ThreeMFLoader } from "three/examples/jsm/loaders/3MFLoader.js";

import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
  1. vue的mounted()方法,用来初始化threejs
mounted() {
  this.initThreejs();
},
  1. initThreejs()的代码
initThreejs() {
  let camera, scene, renderer;

  let model, settings;

  let clock;

  init();

  function init() {
    // threejs的代码
  }

  function createPanel() {
    // gui的代码
  }

  function animate() {
    // ...
  }

  function render() {
    renderer.render(scene, camera);
  }
},
  1. init()的代码,包括场景、相机、灯光(半球光和平行光)、渲染器、地面模型、轨道控制器的创建
// 场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x8cc7de); // 场景的背景颜色
scene.fog = new THREE.Fog(0xa0a0a0, 10, 500);

// 相机,这里创建的是一个透视相机
camera = new THREE.PerspectiveCamera(
  35,
  (window.innerWidth - 201) / window.innerHeight,
  1,
  500
);
camera.position.set(-100, 80, 100); // 相机的位置
scene.add(camera);

// 半球光
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444); // 天空发出的光的颜色是0xffffff,地面发出的光的颜色是0x444444
hemiLight.position.set(0, 100, 0); // 灯光的位置
scene.add(hemiLight);

// 平行光
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(-0, 40, 50); // 平行光的位置
dirLight.castShadow = true; // 是否显示阴影
dirLight.shadow.camera.top = 50;
dirLight.shadow.camera.bottom = -25;
dirLight.shadow.camera.left = -25;
dirLight.shadow.camera.right = 25;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 200;
dirLight.shadow.mapSize.set(1024, 1024);
scene.add(dirLight);

// 渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth - 201, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.shadowMap.enabled = true; // 是否允许阴影贴图
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.getElementById("THREE31").appendChild(renderer.domElement);

// 地面
const ground = new THREE.Mesh(
  new THREE.PlaneGeometry(1000, 1000), // 一个长1000,宽1000的正方形
  new THREE.MeshPhongMaterial({ color: 0x999999, depthWrite: false })
);
ground.rotation.x = -Math.PI / 2; // 绕x轴旋转90度
ground.position.y = 11;
ground.receiveShadow = true; // 地面接收阴影
scene.add(ground);

// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener("change", render);
controls.minDistance = 50; // 控制距离下限
controls.maxDistance = 200; // 控制距离上限
controls.minPolarAngle = 0; // 控制角度下限
controls.maxPolarAngle = Math.PI / 2; // 控制角度上限
controls.enablePan = false;
controls.target.set(0, 20, 0);
controls.update();

// 加载.3mf模型
const loader = new ThreeMFLoader();
loader.load("models/models/3mf/truck.3mf", function (object) {
  object.quaternion.setFromEuler(new THREE.Euler(-Math.PI / 2, 0, 0));
  object.traverse(function (child) {
    child.castShadow = true;
    if (child.material) {
      child.material.transparent = true; // 每个材质都开启透明度设置,这个不开启改了opacity的值也不会生效
      child.material.opacity = 1; // 默认设置透明度为1
    }
  });

  model = object;

  scene.add(object);

  // 生成gui
  createPanel();

  // 渲染
  animate();
});
  1. createPanel()的代码,使用了gui,用来控制是否开启闪烁效果
  function createPanel() {
    const panel = new GUI({ width: 310 });

    const folder1 = panel.addFolder("物体效果");

    settings = {
      闪烁: false,
    };

    folder1.add(settings, "闪烁");

    folder1.open();
  }
  1. animate()的代码,requestAnimationFrame(animate);这个要加上,没有这个改了透明度页面没有变化,本来是按照thingjs那样用的sin函数,但是透明度一开始是1,而一开始的elapsed是0,会使透明度突然从1变成0.5,就会有卡顿的感觉,所以改成了cos函数。
  function animate() {
    requestAnimationFrame(animate);
    if (settings && settings["闪烁"]) {
      const elapsed = clock.getElapsedTime();

      model.traverse(function (child) {
        if (child.material) {
          // getElapsedTime 获取自时钟启动后的秒数
          // 让透明度根据秒数的增加呈函数式变化
          let opacity = 0.5 + 0.5 * Math.cos(2 * elapsed);
          child.material.opacity = opacity;
        }
      });
    }
    render();
  }

写在最后

以上就是所有的代码以及说明~