目录
  • 🌟前言
  • 🌟先看效果
  • 🌟dat.gui
  • 🌟实现代码
  • 🌟写在最后


🌟前言

哈喽小伙伴们,最近工作比较忙一直没有给大家更新,新的专栏 Three.js第二弹,记录一下博主学习Three.js的过程;一起来看下吧。

🌟先看效果


Three加载尾部模型并执行动画


🌟dat.gui

dat.GUI 是一个轻量级的图形用户界面库(GUI 组件),使用这个库可以很容易地创建出能够改变代码变量的界面组件。

 dat.gui文档

🌟实现代码

<template>
  <div ref="canvas" class="canvas" />
</template>

<script>
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import Stats from 'three/examples/jsm/libs/stats.module'
import * as dat from 'dat.gui'

export default {
  data() {
    return {
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      animationMixer: null,
      clock: null
    }
  },
  computed: {},
  watch: {},
  mounted() {
    this.initThree()
  },
  methods: {
    initThree() {
      // 1、创建场景
      this.scene = new THREE.Scene()

      // 2、创建相机  透视相机
      // fov:角度  aspect:宽高比  near:近端  far:远端
      this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000)
      // 设置相机位置
      this.camera.position.set(0, 30, -34)
      // this.camera.lookAt(0, 100, 0)
      this.scene.add(this.camera)
      // 创建网格对象
      const mesh = new THREE.Mesh(
        // 创建矩形平面 => 假人脚下的地板
        new THREE.PlaneBufferGeometry(500, 500),
        // 光亮的着色材质
        new THREE.MeshPhongMaterial({
          color: 0x999999,
          depthWrite: false,
          side: THREE.DoubleSide
        })
      )
      mesh.rotation.x = -Math.PI / 2 // 网格旋转角度
      mesh.receiveShadow = true
      this.scene.add(mesh)

      const grid = new THREE.GridHelper(500, 20, 0x000000, 0x000000)
      grid.material.opacity = 0.2 // 材质透明度
      grid.material.transparent = true
      this.scene.add(grid) // 添加分割线

      this.animationMixer = new THREE.AnimationMixer(this.scene) // 常见动画混合器
      // 时钟
      this.clock = new THREE.Clock()

      // 给场景增加环境光
      const Ambient = new THREE.AmbientLight(0XFFFFFF, 1)
      this.scene.add(Ambient)

      const modelUrl = '/3DModel/Soldier.glb' // 定义所使用模型路径路径
      // var modelUrl = '../assest/Soldier.glb' //定义所使用模型路径路径
      const loader = new GLTFLoader()
      const that = this
      loader.load(modelUrl, function(glb) {
        glb.scene.traverse(function(child) {
          if (child.isMesh) {
            child.material.emissive = child.material.color
            child.material.emissiveMap = child.material.map
          }
        })
        glb.scene.scale.set(15, 15, 15)
        glb.scene.name = '3dmodel'
        // 将模型的场景添加到Three的主场景里
        that.scene.add(glb.scene)
        // 使用dat.gui可视化控制
        const gui = new dat.GUI()
        // 使用dat.gui,添加一个控制模型在X轴移动的操作
        gui.add(glb.scene.position, 'x').min(-20).max(20).step(0.01).name('移动x轴')
        // 使用dat.gui,添加一个控制模型是否显示的操作
        gui.add(glb.scene, 'visible').name('是否显示')
        // 执行模型动画
        let action1 = null // 走路
        let action2 = null // 奔跑
        let action3 = null // 停下
        let action4 = null // 战斗姿态
        const options = {
          walk: function() {
            //  动画剪辑,是一个可重用的关键帧轨道集,它代表动画。
            const animationClip = glb.animations.find(animationClip => animationClip.name === 'Walk')

            // .clipAction(AnimationClip) 返回所传入的剪辑参数的AnimationAction对象。AnimationAction用来调度存储在AnimationClip中的动画。
            action1 = that.animationMixer.clipAction(animationClip)
            if (action2) {
              action2.stop()
              action2 = null
            }
            if (action3) {
              action3.stop()
              action3 = null
            }
            if (action4) {
              action4.stop()
              action4 = null
            }
            action1.play()
          },
          run: function() {
            const animationClip = glb.animations.find(animationClip => animationClip.name === 'Run')
            action2 = that.animationMixer.clipAction(animationClip)
            if (action1) {
              action1.stop()
              action1 = null
            }
            if (action3) {
              action3.stop()
              action3 = null
            }
            if (action4) {
              action4.stop()
              action4 = null
            }
            action2.play()
          },
          stop: function() {
            const animationClip = glb.animations.find(animationClip => animationClip.name === 'TPose')
            action3 = that.animationMixer.clipAction(animationClip)
            if (action1) {
              action1.stop()
              action1 = null
            }
            if (action2) {
              action2.stop()
              action2 = null
            }
            if (action4) {
              action4.stop()
              action4 = null
            }
            action3.play()
          },
          Idle: function() {
            const animationClip = glb.animations.find(animationClip => animationClip.name === 'Idle')
            action4 = that.animationMixer.clipAction(animationClip)
            if (action1) {
              action1.stop()
              action1 = null
            }
            if (action2) {
              action2.stop()
              action2 = null
            }
            if (action3) {
              action3.stop()
              action3 = null
            }
            action4.play()
          }
        }
        gui.add(options, 'walk').name('走路')
        gui.add(options, 'run').name('奔跑')
        gui.add(options, 'stop').name('停下')
        gui.add(options, 'Idle').name('战斗姿势')
      }, undefined, function(e) {
        console.error(e)
      })

      // 初始化渲染器
      this.renderer = new THREE.WebGLRenderer()
      this.renderer.shadowMap.enabled = true
      // 设置渲染的尺寸大小
      this.renderer.setSize(window.innerWidth, window.innerHeight)
      this.renderer.setClearColor(0xffffff, 1.0)
      // 监听屏幕大小的改变,修改渲染器的宽高和相机的比例:
      window.addEventListener('resize', () => {
        this.renderer.setSize(window.innerWidth, window.innerHeight)
        this.camera.aspect = window.innerWidth / window.innerHeight
        this.camera.updateProjectionMatrix()
      })

      // 这是一个FPS的指示器
      const stats = new Stats()
      stats.showPanel(0)
      // 加入到页面之中
      this.$refs.canvas.appendChild(stats.dom)

      // 将webgl渲染的canvas内容添加到body上
      this.$refs.canvas.appendChild(this.renderer.domElement)

      // 创建轨道控制器
      this.controls = new OrbitControls(this.camera, this.renderer.domElement)
      this.controls.enableDamping = true // 开启阻尼
      // 添加坐标轴辅助器
      // const axesHelper = new THREE.AxesHelper(5)
      // this.scene.add(axesHelper)

      this.render()
    },
    render() {
      this.controls && this.controls.update()// 每一帧都需要更新
      this.animationMixer.update(this.clock.getDelta()) // 更新动画
      this.renderer.render(this.scene, this.camera)
      // 渲染下一帧的时候就会调用render函数
      requestAnimationFrame(this.render)
    }
  }
}
</script>

<style scoped>
  html, body {
    overflow-y: hidden !important;
  }

  .canvas {
    overflow-y: hidden;
    overflow-x: hidden !important;
  }
</style>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.

🌟写在最后

更多Three知识以及API请大家持续关注,尽请期待。各位小伙伴让我们 let’s be prepared at all times!

✨原创不易,还希望各位大佬支持一下!
👍 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!