three.js基础案例day05——加载gltf(glb)模型

今天主要学习如何加载glb模型,这个案例主要用到的相关知识点:加载glb模型,动画的使用、阴影和gui控制面板。具体效果如下:

 首先需要基础的场景、相机、渲染器、控制器,不清楚的话请转至three.js基础案例day01

1. 创建平面和灯光

  // 灯光
  const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444)
  scene.add(hemiLight)
  dirLight = new THREE.DirectionalLight(0xffffff)
  dirLight.position.set(-5, 2, 0)
  scene.add(dirLight)
  // 平面
  const planeGeometry = new THREE.PlaneGeometry(10, 10)
  const planeMaterial = new THREE.MeshPhongMaterial({
    color: 0x808080,
    side: THREE.DoubleSide,
  })
  plane = new THREE.Mesh(planeGeometry, planeMaterial)
  plane.rotateX(Math.PI / 2)
  scene.add(plane)

2.引入GLTFLoader和加载模型

// 引入
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
const loader = new GLTFLoader()
  loader.load('static/Soldier.glb', (glb) => {
    model = glb.scene
    // 模型显示在场景中是背对着的,所以旋转180度,面对相机
    model.rotateY(Math.PI)
    scene.add(model)
 })

 3.阴影(这个之前的案例中使用过)

  // 遍历模型的每一个子模型,判断子模型是是否是mesh,是mesh设置阴影为true    
  model.traverse((item) => {
      if (item.isMesh) {
        item.castShadow = true
      }
   })

  plane.receiveShadow = true
  dirLight.castShadow = true
  renderer.shadowMap.enabled = true

4.动画

// 将模型中的动画存储到animations变量中,方便后面使用
animations = glb.animations

5.引入GUI面板并使用

// 引入
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js'

// 使用
gui = new GUI()

6.将模型动画和GUI面板结合

  let modelControls = {
    run() {
      const clip = animations[1]
      mixer = new THREE.AnimationMixer(model)
      const action = mixer.clipAction(clip)
      action.play()
    },
    walk() {
      const clip = animations[3]
      mixer = new THREE.AnimationMixer(model)
      const action = mixer.clipAction(clip)
      action.play()
    },
    TPose() {
      const clip = animations[2]
      mixer = new THREE.AnimationMixer(model)
      const action = mixer.clipAction(clip)
      action.play()
    },
    Idle() {
      const clip = animations[0]
      mixer = new THREE.AnimationMixer(model)
      const action = mixer.clipAction(clip)
      action.play()
    },
  }

  gui.add(modelControls, 'Idle').name('蹲')
  gui.add(modelControls, 'run').name('跑')
  gui.add(modelControls, 'walk').name('走')
  gui.add(modelControls, 'TPose').name('站立')

7.在render中更新动画

function render() {
  requestAnimationFrame(render)
  // 获取帧时间,在最开始的时候有设置clock = new THREE.Clock()
  const delta = clock.getDelta()
  controls.update()
  renderer.render(scene, camera)
  // 更新
  mixer.update(delta)
}

全部代码:

<template>
  <div id="threeId" ref="elementRef"></div>
</template>

<script setup>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js'
import { ref, onMounted, onBeforeUnmount } from 'vue'

let width, height, scene, camera, renderer, controls
let model, dirLight, plane
let animations, gui
let mixer = new THREE.AnimationMixer(model)
let clock = new THREE.Clock()
const elementRef = ref(null)

onMounted(() => {
  const element = elementRef.value
  width = element.offsetWidth
  height = element.offsetHeight
  initScene()
  initModel()
  initGUI()
  initShadow()
  render()
})

function initScene() {
  // 初始化场景: 创建场景,相机,物体,渲染器
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0x888888)
  camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000)
  camera.position.set(0, 2, 5)
  scene.add(camera)

  // 三维坐标
  // const axesHelper = new THREE.AxesHelper(40)
  // scene.add(axesHelper)

  renderer = new THREE.WebGLRenderer()
  renderer.setSize(width, height)
  elementRef.value.appendChild(renderer.domElement)
  controls = new OrbitControls(camera, renderer.domElement)

  // 光
  const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444)
  scene.add(hemiLight)
  dirLight = new THREE.DirectionalLight(0xffffff)
  dirLight.position.set(-5, 2, 0)
  scene.add(dirLight)
}
function initModel() {
  const planeGeometry = new THREE.PlaneGeometry(10, 10)
  const planeMaterial = new THREE.MeshPhongMaterial({
    color: 0x808080,
    side: THREE.DoubleSide,
  })
  plane = new THREE.Mesh(planeGeometry, planeMaterial)
  plane.rotateX(Math.PI / 2)
  scene.add(plane)
  const loader = new GLTFLoader()
  loader.load('static/Soldier.glb', (glb) => {
    model = glb.scene
    animations = glb.animations
    model.rotateY(Math.PI)
    model.traverse((item) => {
      if (item.isMesh) {
        item.castShadow = true
      }
    })
    scene.add(model)
  })
}
function initGUI() {
  let modelControls = {
    run() {
      const clip = animations[1]
      mixer = new THREE.AnimationMixer(model)
      const action = mixer.clipAction(clip)
      action.play()
    },
    walk() {
      const clip = animations[3]
      mixer = new THREE.AnimationMixer(model)
      const action = mixer.clipAction(clip)
      action.play()
    },
    TPose() {
      const clip = animations[2]
      mixer = new THREE.AnimationMixer(model)
      const action = mixer.clipAction(clip)
      action.play()
    },
    Idle() {
      const clip = animations[0]
      mixer = new THREE.AnimationMixer(model)
      const action = mixer.clipAction(clip)
      action.play()
    },
  }
  gui = new GUI()
  gui.add(modelControls, 'Idle').name('蹲')
  gui.add(modelControls, 'run').name('跑')
  gui.add(modelControls, 'walk').name('走')
  gui.add(modelControls, 'TPose').name('站立')
}
function initShadow() {
  plane.receiveShadow = true
  dirLight.castShadow = true
  renderer.shadowMap.enabled = true
}
function render() {
  requestAnimationFrame(render)
  const delta = clock.getDelta()
  controls.update()
  renderer.render(scene, camera)
  mixer.update(delta)
}
onBeforeUnmount(() => {
  if (gui) {
    // 销毁操作-清空场景、从DOM上删除渲染器等
    scene.remove(camera)
    // 停止渲染循环
    cancelAnimationFrame(render)
    // 释放渲染器的内存
    renderer.forceContextLoss() // 强制上下文丢失
    // 释放所有相关内容
    renderer.dispose()
    gui.destroy()
    gui = null
  }
})
</script>

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值