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

简介: Three入门 【加载外部模型,使用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>


目录
相关文章
|
4月前
|
图形学
Unity——音频管理器(附例子)
Unity——音频管理器(附例子)
在Qt的主ui文件中嵌入另一个ui文件
在Qt的主ui文件中嵌入另一个ui文件
|
9月前
|
JavaScript C#
Maui 读取外部文件显示到Blazor中
Maui 读取外部文件显示到Blazor中
133 0
WPF界面无法正常显示(资源引用,如转换器),但程序正常运行
WPF界面无法正常显示(资源引用,如转换器),但程序正常运行
WPF界面无法正常显示(资源引用,如转换器),但程序正常运行
|
数据可视化 图形学 流计算
Unity 基础 之 代码动态监听UI交互组件汇总
通过介绍组件面板和代码示例,演示代码监听UI交互组件。
218 0
Unity 基础 之 代码动态监听UI交互组件汇总
|
Android开发 UED
独立于视图的加载控件--让视图更干净
项目源码请参考https://github.com/CarryGanLove/LoadingHelper 背景和问题 在app后sdk开发过程中,如果有遇到延时任务的时候,往往需要添加一个通用的loading控件用来展示给用户,一来为了提示用户当前有耗时的操作,二来降低用户的等待感提升用户体验。
957 0
|
测试技术 图形学 Android开发
|
Android开发 计算机视觉 Windows
基于qml创建最简单的图像处理程序(1)-基于qml创建界面
《基于qml创建最简单的图像处理程序》系列课程及配套代码基于qml创建最简单的图像处理程序(1)-基于qml创建界面http://www.cnblogs.com/jsxyhelu/p/8343310.
1226 0