vue+threejs控制相机:向前推进、向右平移

978 阅读3分钟

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


写在前面

本文用vue+threejs控制相机向前推进、向右平移。

下面是演示gif:

20221011_105652.gif

代码说明

  1. html和css

id="THREE45"用来插入threejs渲染器节点,buttons放置水平移动按钮和向前推进按钮

<template>
  <div class="item">
    <div id="THREE45">
      <div class="buttons">
        <el-button type="primary" @click="clickBtn1">水平移动</el-button>
        <el-button type="primary" @click="clickBtn2">向前推进</el-button>
      </div>
    </div>
  </div>
</template>
<style lang="less" scoped>
.buttons {
  position: absolute;
  top: 20px;
  right: 20px;
}
</style>
  1. 引入threejs和需要的模块
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
  1. data中定义的变量

camera相机,scene场景,dracoLoader文件加载器,renderer渲染器,controls轨道控制器

  data() {
    return {
      camera: null,
      scene: null,
      dracoLoader: null,
      renderer: null,
      controls: null,
    };
  },
  1. mounted()

调用的方法都在代码中有解释说明

  mounted() {
    this.dracoLoader = new DRACOLoader();
    this.dracoLoader.setDecoderPath("js/libs/draco/");
    this.dracoLoader.setDecoderConfig({ type: "js" });
    this.initScene(); // 创建场景
    this.initCamera(); // 创建相机
    this.initLight(); // 创建灯光
    this.initRenderer(); // 创建渲染器
    this.initControls(); //创建轨道控制器
    this.initGround(); // 创建地面
    this.initModel(); // 加载模型
  },
  1. initScene()

创建场景,同时设置场景背景颜色,不设置默认为黑色

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

创建相机

initCamera() {
  this.camera = new THREE.PerspectiveCamera(
    35,
    (window.innerWidth - 201) / window.innerHeight,
    1,
    500
  ); // 透视相机
  this.camera.position.x = 0.5;
  this.camera.position.y = 0.5; // 设置相机的位置
  this.camera.position.z = 1.8;
  this.scene.add(this.camera); // 将相机添加到场景中
},
  1. initLight()

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

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. initRenderer()

创建渲染器,并将渲染器添加到document.getElementById("THREE45")中

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("THREE45").appendChild(this.renderer.domElement);
},

9.initControls()

创建轨道控制器(可以控制相机围绕物体进行旋转缩放平移操作),并开启惯性,设置控制器的焦点为(0, 0.1, 0)

initControls() {
  this.controls = new OrbitControls(this.camera, this.renderer.domElement);
  this.controls.addEventListener("change", this.render);
  this.controls.enableDamping = true; // 开启惯性
  this.controls.target.set(0, 0.1, 0);
  // this.controls.update();
},
  1. initGround()

创建一个1x1的地面

initGround() {
  const ground = new THREE.Mesh(
    new THREE.BoxGeometry(1, 0.0015, 1),
    new THREE.MeshPhongMaterial({
      color: 0x999999,
      depthWrite: false,
      transparent: true,
      opacity: 1,
    })
  );
  ground.receiveShadow = true;
  this.scene.add(ground);
},
  1. initModel()

使用dracoLoader加载一个兔子模型,加载完后调用this.animate();渲染场景,并且在焦点的位置放置了一个红色立方体,所有的旋转平移缩放都是按着焦点来的,可以看的更直观

initModel() {
  this.dracoLoader.load("/models/models/draco/bunny.drc", (geometry) => {
    geometry.computeVertexNormals();

    const material = new THREE.MeshStandardMaterial({
      color: 0xffffff,
    });
    let mesh = new THREE.Mesh(geometry, material);
    mesh.castShadow = true;
    mesh.receiveShadow = true;
    mesh.position.y = -0.035;
    this.scene.add(mesh);

    this.dracoLoader.dispose();

    this.animate();
  });

  // 创建一个红色立方体,放置在焦点的位置,所有的旋转平移缩放都是按这焦点来的
  let redMesh = new THREE.Mesh(
    new THREE.BoxGeometry(0.02, 0.02, 0.02),
    new THREE.MeshBasicMaterial({ color: 0xff0000 })
  ); // 网格模型
  redMesh.position.set(
    this.controls.target.x,
    this.controls.target.y,
    this.controls.target.z
  );
  this.scene.add(redMesh); // 将这个立方体添加到场景中
},
  1. animate()和render()

animate()方法中调用了this.controls.update();,因为启用了enableDamping(惯性)

render()方法为渲染器渲染场景

animate() {
  requestAnimationFrame(this.animate);
  this.controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true
  this.render();
},
render() {
  this.renderer.render(this.scene, this.camera);
},
  1. clickBtn1()和clickBtn2()

clickBtn1()向右平移事件,将相机向右平移0.05,同时将视线的焦点也向右平移0.05

clickBtn2()向前推进事件,使用.subVectors()方法计算两向量相减,将相减后的向量转成方向方向,即为要移动的方向,然后使用.translateOnAxis()方法向要移动的方向移动0.05

clickBtn1() {
  this.camera.position.x += 0.05;
  this.controls.target.x += 0.05;
},
clickBtn2() {
  let subVector = new THREE.Vector3();
  subVector.subVectors(this.controls.target, this.camera.position); // 两向量相减
  let unitVector = subVector.normalize(); // 转成方向向量

  this.camera.translateOnAxis(unitVector, 0.05);
},

写在最后

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