vue+threejs控制相机:飞到某位置

795 阅读3分钟

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


写在前面

本文用vue+threejs控制相机飞到预先设定好的位置。

演示gif如下:

20221019_095651.gif

代码说明

  1. html+css

创建一个id容器,用来添加three.js的渲染器节点,增加一个按钮【飞到位置】,点击后相机飞到制定的位置

<template>
  <div class="item">
    <div id="THREE49"></div>
    <div class="btn_box">
      <el-button @click="flyToPosi">飞到位置</el-button>
    </div>
  </div>
</template>
<style lang="less" scoped>
.btn_box {
  position: absolute;
  top: 10px;
  left: 210px;
}
</style>
  1. 引入three.js和需要的模块,引入tween.js

OrbitControls:轨道控制器,GLTFLoader:模型加载器

TWEEN的使用可以看我的文章:vue中使用tween.js的全过程

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";

const TWEEN = require("@tweenjs/tween.js");
  1. data()中定义变量

camera相机,scene场景,gltfLoader模型加载器,renderer渲染器,manager模型加载进度管理

  data() {
    return {
      camera: null,
      scene: null,
      gltfLoader: null,
      renderer: null,
      controls: null,

      manager: null,

      tween: null,
    };
  },
  1. mounted()方法中调用的方法

各方法的作用在代码中都有注释

  mounted() {
    this.manager = new THREE.LoadingManager();
    this.gltfLoader = new GLTFLoader(this.manager);
    this.initScene(); // 创建场景
    this.initCamera(); // 创建相机
    this.initLight(); // 创建灯光
    this.initRenderer(); // 创建渲染器
    this.initControls(); //创建轨道控制器
    this.initModel(); // 加载模型
    this.initTween(); // 创建tween

    this.manager.onLoad = () => {
      this.animate();
      console.log("Loading complete!");
    };
  },
  1. 创建场景

创建场景并设置场景背景颜色

    initScene() {
      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color(0x000000); // 设置场景背景颜色
    },
  1. 创建相机

创建一个透视相机并设置相机的位置,然后将相机添加到场景中

    initCamera() {
      this.camera = new THREE.PerspectiveCamera(
        35,
        (window.innerWidth - 201) / window.innerHeight,
        1,
        600
      ); // 透视相机
      this.camera.position.x = 10.5;
      this.camera.position.y = 32.3; // 设置相机的位置
      this.camera.position.z = 104.3;
      this.scene.add(this.camera); // 将相机添加到场景中
    },
  1. 创建灯光

创建一个平行光和一个环境光

    initLight() {
      const light = new THREE.DirectionalLight(0xffffff); // 平行光
      light.position.set(0.5, 1.0, 0.5).normalize(); // 设置平行光的方向,从(0.5, 1.0, 0.5)->target一般(0, 0, 0)
      this.scene.add(light); // 将灯光添加到场景中

      const ambLight = new THREE.AmbientLight(0xf0f0f0, 0.1); // 环境光
      this.scene.add(ambLight);
    },
  1. 创建渲染器

创建场景渲染器,并将渲染器元素添加到html中

    initRenderer() {
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.renderer.outputEncoding = THREE.sRGBEncoding;
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.setSize(window.innerWidth - 201, window.innerHeight);
      document.getElementById("THREE49").appendChild(this.renderer.domElement);
    },
  1. 创建控制器

创建轨道控制器,设置要控制的相机和渲染器dom元素,并设置控制器的焦点,设置后更新控制器

    initControls() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.addEventListener("change", this.render);

      this.controls.target.set(22, 26, -7); // 控制器的焦点
      this.controls.update();
    },
  1. 加载模型

使用gltfLoader加载模型,设置模型的缩放程度和位置,并将模型添加到场景中

    initModel() {
      this.gltfLoader.load(
        "/models/models/gltf/urban_tree/scene.gltf",
        (gltf) => {
          gltf.scene.scale.set(10, 10, 10);
          gltf.scene.position.set(40, 0, 0);
          this.scene.add(gltf.scene);
        }
      );
      this.gltfLoader.load("/models/models/gltf/shiba/scene.gltf", (gltf) => {
        gltf.scene.scale.set(3, 3, 3);
        gltf.scene.position.set(-5, 3, 1);
        this.scene.add(gltf.scene);
      });
      this.gltfLoader.load("/models/models/gltf/matilda/scene.gltf", (gltf) => {
        gltf.scene.scale.set(0.1, 0.1, 0.1);
        this.scene.add(gltf.scene);
      });
    },
  1. 创建Tween

创建一个tween动画,他将控制相机和轨道控制器的焦点从原来的位置移动到设置好的位置,(x1,y1,z1):控制器的焦点,(x2,y2,z2):相机位置,动画持续时间是2000毫秒,动画的速度曲线是Quadratic.Out

    initTween() {
      let target = this.controls.target;
      let position = this.camera.position;
      this.tween = new TWEEN.Tween({
        x1: target.x,
        y1: target.y,
        z1: target.z,
        x2: position.x,
        y2: position.y,
        z2: position.z,
      })
        .to({ x1: 22, y1: 26, z1: -7, x2: -85.5, y2: 7.6, z2: 18.6 }, 2000)
        .easing(TWEEN.Easing.Quadratic.Out)
        .onUpdate((object) => {
          let { x1, y1, z1, x2, y2, z2 } = object;
          this.controls.target.set(x1, y1, z1); // 控制器的焦点
          this.camera.position.set(x2, y2, z2); // 相机位置

          this.controls.update();
        });
    },
  1. 渲染场景

通过打印this.camera.position的值可以知道我们想飞到的相机位置,通过打印this.controls.target的值可以知道我们想要飞到的位置的轨道控制器的焦点

    animate(time) {
      requestAnimationFrame(this.animate);
      TWEEN.update(time);

      this.render();
    },
    render() {
      // console.log("this.camera.position", this.camera.position); // 获取相机位置
      // console.log("target", this.controls.target); // 获取控制器的焦点
      this.renderer.render(this.scene, this.camera);
    },
  1. 飞到位置

调用.start()方法开始执行动画

    flyToPosi() {
      this.tween.start();
    },

写在最后

以上就是所有的代码和说明。