使用vue学习three.js之创建动画和移动相机-使用轨迹球控件TrackballControls控制相机

1.demo效果

在这里插入图片描述

如上图,该demo通过轨迹球控件TrackballControls控制相机。实现通过按住鼠标左键拖动控制场景旋转、翻滚相机,通过鼠标滚轮控制场景放大缩小,通过按住鼠标右键拖动控制场景平移,通过右上角的"resetCamera"按钮重置相机位置

2.TrackballControls介绍

通过轨迹球控件TrackballControls 我们可以实现场景的旋转、缩放、平移等功能

2.1 TrackballControls操控说明

以下是动作和操控比对说明

动作 操控
在场景中旋转、翻滚相机 按住鼠标左键拖动
放大和缩小 转动鼠标滚轮或按住鼠标中键拖动
在场景中平移 按住鼠标右键拖动

2.2 TrackballControls属性介绍

我们创建TrackballControls实例后。它有一些属性来告诉控件如何工作,现在来认识一下它们

属性 描述
enabled 是否开启控制器,默认true,如果设置为false,对相机的操作将失效
rotateSpeed 相机的旋转速度,默认 3.0
zoomSpeed 相机的缩放速度,默认 1.2
panSpeed 相机的平移速度,默认 0.3
noRotate 是否关闭相机旋转,默认 false
noZoom 是否关闭相机缩放,默认 false
noPan 是否关闭相机移动,默认 false
staticMoving 是否关闭拖拽惯性移动,默认 false
dynamicDampingFactor 拖拽惯性移动阻力,默认 0.2
minDistance 相机距离焦点的最近距离,默认 0
maxDistance 相机距离焦点的最远距离,默认 Infinity(无限远)

2.3 TrackballControls方法介绍

2.3.1 update()函数

TrackballControls控件控制相机更新的方法,需要在循环动画函数中调用,通常在render函数中调用

render() {
    
    
  this.trackballControls.update() // 相机更新
  this.renderer.render(this.scene, this.camera)
  requestAnimationFrame(this.render)
}

2.3.2 reset()函数

重置相机的方法,该函数可以使相机回到初始位置

2.3.3 dispose()函数

销毁实例化的TrackballControls对象

2.3.4 change()回调函数

有了这个函数我们可以监听相机变化事件,如果控制器调整了相机的位置,可以被监听到,就可以在回调函数中做相应的处理,添加监听的方式如下:

this.trackballControls.addEventListener('change', function(event) {
    
    
  console.log(event)
  //这里可以做一些基于相机位置变化的事情
})

3.实现要点

3.1 vue中引入TrackballControls控制器

在项目工程中引入TrackballControls 控制器的方式如下:

import {
    
     TrackballControls } from 'three/examples/jsm/controls/TrackballControls.js'

3.2 创建TrackballControls实例

// 创建轨迹球控件
createTrackballControls() {
    
    
  this.clock = new THREE.Clock() //创建THREE.Clock对象,用于计算上次调用经过的时间
  this.trackballControls = new TrackballControls(
    this.camera,
    this.renderer.domElement
  )

  this.trackballControls.rotateSpeed = 1.0 //相机的旋转速度
  this.trackballControls.zoomSpeed = 1.0 //相机的缩放速度
  this.trackballControls.panSpeed = 1.0 //相机的平移速度
  this.trackballControls.staticMoving = true //关闭拖拽惯性移动
  //添加相机移动监听事件
  this.trackballControls.addEventListener('change', function(event) {
    
    
    console.log(event)
    //这里可以做一些基于相机位置变化的事情
  })
}

3.3 相机重置处理

resetCamera() {
    
    
  this.trackballControls.reset()
}

3.4 render中处理相机更新

render() {
    
    
  const delta = this.clock.getDelta() // 获取自上次调用的时间差
  this.trackballControls.update(delta) // 相机更新
  this.renderer.render(this.scene, this.camera)
  requestAnimationFrame(this.render)
}

3.5 加载OBJ和MTL组合模型

首先要引入OBJ和MTL加载工具

import {
    
     OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
import {
    
     MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'

加载模型实现

// 加载OBJ、MTL模型
loadObjMtl() {
    
    
  const mtlLoader = new MTLLoader()
  const objLoader = new OBJLoader()
  const THIS = this
  mtlLoader.load(`${
      
      THIS.publicPath}models/city.mtl`, material => {
    
    
    material.preload()
    objLoader.setMaterials(material) //mtl文件中的材质设置到obj加载器
    return objLoader.load(
      `${
      
      THIS.publicPath}models/city.obj`,
      loadedMesh => {
    
    
        THIS.mesh = loadedMesh
        loadedMesh.scale.set(0.2, 0.2, 0.2)
        THIS.setRandomColors(loadedMesh, THIS)
        THIS.scene.add(THIS.mesh)
      }
    )
  })
},
//给材质设置红黄绿随机色,并设置透明度
setRandomColors(object, THIS) {
    
    
  const children = object.children

  if (children && children.length > 0) {
    
    
    children.forEach(function(e) {
    
    
      THIS.setRandomColors(e, THIS)
    })
  } else {
    
    
    if (object instanceof THREE.Mesh) {
    
    
      const colorIndex = Math.floor(Math.random() * 3) //0~2的随机数
      const colorArray = [
        new THREE.Color(0xff0000),
        new THREE.Color(0x00ff00),
        new THREE.Color(0x0000ff)
      ]
      object.material.color = colorArray[colorIndex]
      object.material.transparent = true
      object.material.opacity = 0.3
      if (
        object.material.name &&
        object.material.name.indexOf('building') == 0
      ) {
    
    
        object.material.emissive = colorArray[colorIndex]
        object.material.transparent = true
        object.material.opacity = 0.3
      }

      //重新计算面法向量和顶点法向量,修复模型导入为重黑色
      object.geometry.computeFaceNormals()
      object.geometry.computeVertexNormals()
    }
  }
}

4.demo代码

<template>
  <div>
    <div id="container"></div>
    <div class="controls-box">
      <section>
        <el-row>
          <el-button type="primary" class="controls-button" size="mini" @click="resetCamera">resetCamera</el-button>
        </el-row>
      </section>
    </div>
  </div>
</template>
<script>
import * as THREE from 'three'
import {
    
     TrackballControls } from 'three/examples/jsm/controls/TrackballControls.js'
import {
    
     OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
import {
    
     MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'

export default {
    
    
  data() {
    
    
    return {
    
    
      publicPath: process.env.BASE_URL,
      mesh: null,
      camera: null,
      scene: null,
      renderer: null,
      trackballControls: null,
      clock: null
    }
  },
  mounted() {
    
    
    this.init()
  },
  methods: {
    
    
    // 初始化
    init() {
    
    
      this.createScene() // 创建场景
      this.loadObjMtl() // 加载OBJ、MTL模型
      this.createLight() // 创建光源
      this.createCamera() // 创建相机
      this.createRender() // 创建渲染器
      this.createTrackballControls() // 创建轨迹球控件
      this.render() // 渲染
    },
    // 创建场景
    createScene() {
    
    
      this.scene = new THREE.Scene()
    },
    // 加载OBJ、MTL模型
    loadObjMtl() {
    
    
      const mtlLoader = new MTLLoader()
      const objLoader = new OBJLoader()
      const THIS = this
      mtlLoader.load(`${
      
      THIS.publicPath}models/city.mtl`, material => {
    
    
        material.preload()
        objLoader.setMaterials(material) //mtl文件中的材质设置到obj加载器
        return objLoader.load(
          `${
      
      THIS.publicPath}models/city.obj`,
          loadedMesh => {
    
    
            THIS.mesh = loadedMesh
            loadedMesh.scale.set(0.2, 0.2, 0.2)
            THIS.setRandomColors(loadedMesh, THIS)
            THIS.scene.add(THIS.mesh)
          }
        )
      })
    },
    //给材质设置红黄绿随机色,并设置透明度
    setRandomColors(object, THIS) {
    
    
      const children = object.children

      if (children && children.length > 0) {
    
    
        children.forEach(function(e) {
    
    
          THIS.setRandomColors(e, THIS)
        })
      } else {
    
    
        if (object instanceof THREE.Mesh) {
    
    
          const colorIndex = Math.floor(Math.random() * 3) //0~2的随机数
          const colorArray = [
            new THREE.Color(0xff0000),
            new THREE.Color(0x00ff00),
            new THREE.Color(0x0000ff)
          ]
          object.material.color = colorArray[colorIndex]
          object.material.transparent = true
          object.material.opacity = 0.3
          if (
            object.material.name &&
            object.material.name.indexOf('building') == 0
          ) {
    
    
            object.material.emissive = colorArray[colorIndex]
            object.material.transparent = true
            object.material.opacity = 0.3
          }

          //重新计算面法向量和顶点法向量,修复模型导入为重黑色
          object.geometry.computeFaceNormals()
          object.geometry.computeVertexNormals()
        }
      }
    },
    // 创建光源
    createLight() {
    
    
      // 环境光
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.1) // 创建环境光
      this.scene.add(ambientLight) // 将环境光添加到场景

      const spotLight = new THREE.SpotLight(0xffffff) // 创建聚光灯
      spotLight.position.set(200, 200, 200)
      spotLight.castShadow = true
      this.scene.add(spotLight)
    },
    // 创建相机
    createCamera() {
    
    
      const element = document.getElementById('container')
      const width = element.clientWidth // 窗口宽度
      const height = element.clientHeight // 窗口高度
      const k = width / height // 窗口宽高比
      this.camera = new THREE.PerspectiveCamera(35, k, 0.1, 1000)
      this.camera.position.set(-80, 60, 40) // 设置相机位置

      this.camera.lookAt(new THREE.Vector3(10, 0, 0)) // 设置相机方向
      this.scene.add(this.camera)
    },
    // 创建渲染器
    createRender() {
    
    
      const element = document.getElementById('container')
      this.renderer = new THREE.WebGLRenderer({
    
     antialias: true, alpha: true })
      this.renderer.setSize(element.clientWidth, element.clientHeight) // 设置渲染区域尺寸
      this.renderer.shadowMap.enabled = true // 显示阴影
      this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
      this.renderer.setClearColor(0x3f3f3f, 1) // 设置背景颜色
      element.appendChild(this.renderer.domElement)
    },

    render() {
    
    
      const delta = this.clock.getDelta() // 获取自上次调用的时间差
      this.trackballControls.update(delta) // 相机更新
      this.renderer.render(this.scene, this.camera)
      requestAnimationFrame(this.render)
    },
    //相机恢复
    resetCamera() {
    
    
      this.trackballControls.reset()
    },
    // 创建轨迹球控件
    createTrackballControls() {
    
    
      this.clock = new THREE.Clock() //创建THREE.Clock对象,用于计算上次调用经过的时间
      this.trackballControls = new TrackballControls(
        this.camera,
        this.renderer.domElement
      )

      this.trackballControls.rotateSpeed = 1.0 //相机的旋转速度
      this.trackballControls.zoomSpeed = 1.0 //相机的缩放速度
      this.trackballControls.panSpeed = 1.0 //相机的平移速度
      this.trackballControls.staticMoving = true //关闭拖拽惯性移动
      //添加相机移动监听事件
      this.trackballControls.addEventListener('change', function(event) {
    
    
        console.log(event)
        //这里可以做一些基于相机位置变化的事情
      })
    }
  }
}
</script>
<style>
#container {
    
    
  position: absolute;
  width: 100%;
  height: 100%;
}
.controls-box {
    
    
  position: absolute;
  right: 5px;
  top: 5px;
  width: 300px;
  padding: 10px;
  background-color: #fff;
  border: 1px solid #c3c3c3;
}
</style>

猜你喜欢

转载自blog.csdn.net/qw8704149/article/details/114165119