0
点赞
收藏
分享

微信扫一扫

Three入门 【加载外部模型,使用dat.gui创建动画控制器】



目录

  • 🌟前言
  • 🌟先看效果
  • 🌟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>

🌟写在最后

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

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


举报

相关推荐

0 条评论