vue+threejs写物体效果:缩放

344 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情


写在前面

本文用vue和threejs写物体的缩放效果。

下面是gif展示:

20220928_152702.gif

完整代码说明

  1. html写一个id容器,用于展示threejs,并且引入需要的threejs 模块
  <div class="item">
    <div id="THREE31"></div>
  </div>
  
import * as THREE from "three";

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; // 轨道控制器
import { ThreeMFLoader } from "three/examples/jsm/loaders/3MFLoader.js"; // .3mf模型加载器

import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js"; // GUI面板
  1. 创建场景

创建场景并设置背景。

initScene() {
  this.scene = new THREE.Scene(); // 创建场景
  this.scene.background = new THREE.Color(0x8cc7de); // 设置背景颜色
  this.scene.fog = new THREE.Fog(0xa0a0a0, 10, 500); // 雾化效果
},
  1. 创建相机

创建一个透视相机(相机垂直角度,长宽比,近端面,远端面),并设置相机的位置。

initCamera() {
  this.camera = new THREE.PerspectiveCamera(
    35,
    (window.innerWidth - 201) / window.innerHeight,
    1,
    500
  ); // 创建透视相机
  this.camera.position.set(-100, 80, 100); // 设置相机位置
  this.scene.add(this.camera); // 将相机添加到场景中
},
  1. 创建灯光

创建一个半球光HemisphereLight,天空颜色为白色0xffffff,地面颜色为0x444444

创建一个平行光DirectionalLight,允许该平行光产生阴影。

initLights() {
  const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444); // 创建半球光
  hemiLight.position.set(0, 100, 0); // 设置半球光位置
  this.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);
  this.scene.add(dirLight); // 将平行光添加到场景中
},
  1. 创建渲染器

创建渲染器WebGLRenderer,并设置设备像素比,并调整画布大小,并且允许渲染阴影。

initRenderer() {
  this.renderer = new THREE.WebGLRenderer({ antialias: true });
  this.renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比
  this.renderer.setSize(window.innerWidth - 201, window.innerHeight); // 考虑设备像素比调整画布大小
  this.renderer.outputEncoding = THREE.sRGBEncoding;
  this.renderer.shadowMap.enabled = true; // 允许渲染阴影
  this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  document.getElementById("THREE31").appendChild(this.renderer.domElement);
},
  1. 加载模型

加载.3mf模型,并且设置模型允许产生阴影child.castShadow = true;

在加载完模型后,生成GUI面板,并且开始渲染。

initTruckModel() {
  const loader = new ThreeMFLoader();
  // 加载模型
  loader.load("models/models/3mf/truck.3mf", (object) => {
    object.quaternion.setFromEuler(new THREE.Euler(-Math.PI / 2, 0, 0)); // z-up conversion
    object.traverse((child) => {
      child.castShadow = true;
    });

    this.truckModel = object;

    this.scene.add(object); // 将模型添加到场景中

    this.createPanel(); // 生成GUI面板
    this.animate(); // 渲染
  });
},
  1. 创建地面

创建一个平面PlaneGeometry,给它加上颜色材质MeshPhongMaterial,形成一个平面网格模型Mesh,将平面旋转90度即变成一个地面,并且设置允许地面接收阴影ground.receiveShadow = true;

initGround() {
  const ground = new THREE.Mesh(
    new THREE.PlaneGeometry(1000, 1000),
    new THREE.MeshPhongMaterial({ color: 0x999999, depthWrite: false })
  ); // 创建一个平面
  ground.rotation.x = -Math.PI / 2; // 将平面旋转90度变成地面
  ground.position.y = 11;
  ground.receiveShadow = true; // 允许地面接收阴影
  this.scene.add(ground); // 将地面添加到场景中
},
  1. 创建轨道控制器

使用引入的OrbitControls创建轨道控制器,能使用鼠标进行画面的操作,但对画面的切换范围做了一些限制controls.minDistance controls.maxDistance controls.minPolarAngle controls.maxPolarAngle controls.enablePan,在代码中都进行了解释。

initControls() {
  // 创建轨道控制器
  const controls = new OrbitControls(this.camera, this.renderer.domElement);
  controls.addEventListener("change", this.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();
},
  1. 生成GUI面板

创建一个GUI面板,添加一个折叠面板,折叠面板的标题为物体效果 ,该折叠面板下有一个子项,为缩放,初始值为1,能控制的范围为(0.5, 2.0),缩放值变化的步长为0.1,绑定了一个change事件,触发后对模型进行缩放操作this.truckModel.scale.set(val, val, val);,默认该折叠面板是打开的folder1.open();

createPanel() {
  const panel = new GUI({ width: 310 }); // 创建一个长310的面板
  const folder1 = panel.addFolder("物体效果");
  let settings = {
    缩放: 1,
  };
  folder1.add(settings, "缩放", 0.5, 2.0, 0.1).onChange((val) => {
    this.truckModel.scale.set(val, val, val);
  });
  folder1.open();
},
  1. 渲染

一个帧动画requestAnimationFrame(this.animate)+渲染器渲染this.renderer.render(this.scene, this.camera)

animate() {
  requestAnimationFrame(this.animate);
  this.render();
},
render() {
  this.renderer.render(this.scene, this.camera);
},
  1. mounted方法

在mounted方法中一次调用。

mounted() {
    this.initScene(); // 创建场景
    this.initCamera(); // 创建相机
    this.initLights(); // 创建灯光
    this.initRenderer(); // 创建渲染器
    this.initTruckModel(); // 加载模型
    this.initGround(); // 创建地面
    this.initControls(); // 创建轨道控制器
},

写在最后

以上就是全部代码和说明。