vue+threejs控制相机:限制水平、俯仰、缩放范围

2,874 阅读2分钟

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


写在前面

本文用vue+threejs限制相机水平、俯仰、缩放范围。

如果不对相机的水平、俯仰、缩放范围作限制,就会看到模型的所有角度,有时候,我们可能只需要给用户看到模型的某一面。

代码说明

场景、灯光、渲染器等的创建,模型的导入就不多说了,前面写了很多篇文章都涉及到这些部分。

这次主要讲限制水平、俯仰、缩放范围的逻辑。

首先,我们要清楚我们是通过什么来限制这些范围的:threejs提供了OrbitControls(轨道控制器),可以使得相机围绕目标进行轨道运动,而他也提供了一些属性方法帮我们来限制相机的运动。

属性

  • maxAzimuthAngle 相机能水平旋转的角度上限,有效值范围为[-2 * Math.PI,2 * Math.PI]
  • minAzimuthAngle 相机能水平旋转的角度下限,有效值范围为[-2 * Math.PI,2 * Math.PI]
  • minDistance 相机能向内移动的距离,默认值为0
  • maxDistance 相机能向外移动的距离,默认值为无穷大
  • minPolarAngle 相机能垂直旋转的角度下限,范围是0到Math.PI,其默认值为0
  • maxPolarAngle 相机能垂直旋转的角度上限,范围是0到Math.PI,其默认值为Math.PI

方法

  • .getAzimuthalAngle() 获得当前的水平旋转弧度
  • .getPolarAngle() 获得当前的垂直旋转弧度

通过改变属性我们可以实现对相机的限制,通过调用方法我们可以对我们要怎么限制、限制多少来进行调整。

水平旋转

在代码中,我对水平旋转的角度作了如下限制

this.controls.maxAzimuthAngle = 0;
this.controls.minAzimuthAngle = (-85 * Math.PI) / 100;

为什么会选择0和(-85 * Math.PI) / 100作为限制的值呢?

因为在render()方法中打印了当前的水平旋转弧度,我就知道我想要展示的面是水平旋转到什么角度,我设置了maxAzimuthAngle=0,minAzimuthAngle = (-85 * Math.PI) / 100,我能水平旋转的范围就是(-85 * Math.PI) / 100逆时针旋转到0的弧度范围

render() {
  console.log(
    "水平旋转弧度",
    (this.controls.getAzimuthalAngle() / Math.PI).toFixed(2)
  );
  console.log(
    "垂直旋转弧度",
    (this.controls.getPolarAngle() / Math.PI).toFixed(2)
  );
  this.renderer.render(this.scene, this.camera);
},

这是水平旋转到(-85 * Math.PI) / 100时的模型 image.png

这是水平旋转到0的模型

image.png

垂直旋转可同理设置:

this.controls.minPolarAngle = 0;
this.controls.maxPolarAngle = (50 * Math.PI) / 100;

这是垂直旋转到(50 * Math.PI) / 100的模型

image.png

这是垂直旋转到0的模型

image.png

缩放范围

我设置的缩放范围为:

this.controls.minDistance = 1.5;
this.controls.maxDistance = 5;

缩放范围的设置可以通过肉眼判断。

这是缩放范围为1.5时模型的大小

image.png

这是缩放范围为5时模型的大小

image.png

完整代码

<template>
  <div class="item">
    <div id="THREE47"></div>
  </div>
</template>

<script>
import * as THREE from "three";

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";

export default {
  data() {
    return {
      camera: null,
      scene: null,
      gltfLoader: null,
      renderer: null,
      controls: null,
    };
  },
  mounted() {
    this.gltfLoader = new GLTFLoader();
    this.initScene(); // 创建场景
    this.initCamera(); // 创建相机
    this.initLight(); // 创建灯光
    this.initRenderer(); // 创建渲染器
    this.initControls(); //创建轨道控制器
    this.initModel(); // 加载模型
  },
  methods: {
    initScene() {
      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color(0x000000); // 设置场景背景颜色
    },
    initCamera() {
      this.camera = new THREE.PerspectiveCamera(
        35,
        (window.innerWidth - 201) / window.innerHeight,
        1,
        500
      ); // 透视相机
      this.camera.position.x = -2;
      this.camera.position.y = 0.3; // 设置相机的位置
      this.camera.position.z = 0;
      this.scene.add(this.camera); // 将相机添加到场景中
    },
    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);
    },
    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("THREE47").appendChild(this.renderer.domElement);
    },
    initControls() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.addEventListener("change", this.render);
      this.controls.enableDamping = true; // 开启惯性

      this.controls.maxAzimuthAngle = 0; // 相机能水平旋转的角度上限,有效值范围为[-2 * Math.PI,2 * Math.PI]
      this.controls.minAzimuthAngle = (-85 * Math.PI) / 100; // 相机能水平旋转的角度下限,有效值范围为[-2 * Math.PI,2 * Math.PI]
      this.controls.minDistance = 1.5; // 相机能向内移动的距离,默认值为0
      this.controls.maxDistance = 5; // 相机能向外移动的距离,默认值为无穷大
      this.controls.minPolarAngle = 0; // 相机能垂直旋转的角度下限,范围是0到Math.PI,其默认值为0
      this.controls.maxPolarAngle = (50 * Math.PI) / 100; // 相机能垂直旋转的角度上限,范围是0到Math.PI,其默认值为Math.PI

      this.controls.target.set(-0.08, 0, -0.05); // 控制器的焦点
    },
    initModel() {
      this.gltfLoader.load(
        "/models/models/gltf/forest_house/scene.gltf",
        (gltf) => {
          gltf.scene.scale.set(3, 3, 3);

          this.scene.add(gltf.scene);

          this.animate();
        }
      );
    },
    animate() {
      // requestAnimationFrame(this.animate);

      this.controls.update();

      this.render();
    },
    render() {
      console.log("this.camera.position", this.camera.position); // 获取相机位置
      // this.controls.getAzimuthalAngle(); // 获得当前的水平旋转弧度
      // this.controls.getPolarAngle(); // 获得当前的垂直旋转弧度
      console.log(
        "水平旋转弧度",
        (this.controls.getAzimuthalAngle() / Math.PI).toFixed(2)
      );
      console.log(
        "垂直旋转弧度",
        (this.controls.getPolarAngle() / Math.PI).toFixed(2)
      );
      this.renderer.render(this.scene, this.camera);
    },
  },
};
</script>

<style lang="less" scoped>
</style>

写在最后

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