Bootstrap

学习threejs,导入FBX格式骨骼绑定模型

👨‍⚕️ 主页: gis分享者
👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!
👨‍⚕️ 收录于专栏:threejs gis工程师



一、🍀前言

本文详细介绍如何基于threejs在三维场景中导入FBX格式骨骼绑定模型,亲测可用。希望能帮助到您。一起学习,加油!加油!

1.1 ☘️THREE.FBXLoader fbx 模型加载插件

FBXLoader是ThreeJS的一个扩展,用于加载和处理FBX格式的3D模型文件。FBX是一种通用的3D模型文件格式,可以包含几何形状、材质、动画和其他相关数据。

1.2 ☘️THREE.AnimationMixer 动画混合器

THREE.AnimationMixer动画混合器是用于场景中特定对象的动画的播放器。当场景中的多个对象独立动画时,每个对象都可以使用同一个动画混合器。
创建方法:
AnimationMixer( rootObject : Object3D )
rootObject 是 混合器播放的动画所属的对象
属性:
time:Number 全局的混合器时间(单位秒; 混合器创建的时刻记作0时刻)。
timeScale:全局时间(mixer time)的比例因子。
说明: 将混合器的时间比例设为0, 稍后再设置为1,可以暂停/取消暂停由该混合器控制的所有动作。
方法:
clipAction(clip : AnimationClip, optionalRoot : Object3D):AnimationAction 返回所传入的剪辑参数的AnimationAction, 根对象参数可选,默认值为混合器的默认根对象。第一个参数可以是动画剪辑(AnimationClip)对象或者动画剪辑的名称。
如果不存在符合传入的剪辑和根对象这两个参数的动作, 该方法将会创建一个。传入相同的参数多次调用将会返回同一个剪辑实例。
existingAction (clip : AnimationClip, optionalRoot : Object3D) : AnimationAction 返回传入剪辑的已有AnimationAction, 根对象参数可选,默认值为混合器的默认根对象。
第一个参数可以是动画剪辑(AnimationClip)对象或者动画剪辑的名称。
update (deltaTimeInSeconds : Number) : AnimationMixer 推进混合器时间并更新动画。
deltaTimeInSeconds 参数表示当前帧与前一帧之间的时间差(以秒为单位)。

二、🍀导入FBX格式骨骼绑定模型

1. ☘️实现思路

  • 1、初始化renderer渲染器
  • 2、初始化Scene三维场景
  • 3、初始化camera相机,定义相机位置 camera.position.set,设置相机方向camera.lookAt。
  • 4、初始化THREE.AmbientLight环境光源,scene场景加入环境光源,初始化THREE.PointLight平行光源,设置平行光源位置,设置平行光源投影,scene添加平行光源。
  • 5、加载几何模型:创建THREE.AxesHelper坐标辅助工具,创建THREE.PlaneBufferGeometry平面几何体、THREE.GridHelper地板割线,scene场景中加入创建的平面几何体和地板割线。创建gui控件datGui。通过THREE.FBXLoader类加载 ‘Samba Dancing…fbx’ 模型文件mesh,scene场景中加入mesh。在加载模型的回调函数中,参入mesh创建THREE.AnimationMixer 动画混合器,根据创建的动画混合器的动画片段获取AnimationAction动画调度器,播放动画。datGui加入动画控制。具体实现参考代码样例。
  • 6、加入controls、gui控制,加入stats监控器,监控帧数信息。

2. ☘️代码样例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>learn51(导入FBX格式骨骼绑定模型)</title>
    <script src="lib/threejs/127/three.js-master/build/three.js"></script>
    <script src="lib/threejs/127/three.js-master/examples/js/controls/OrbitControls.js"></script>
    <script src="lib/threejs/127/three.js-master/examples/js/loaders/FBXLoader.js"></script>
    <script src="lib/threejs/127/three.js-master/examples/js/libs/fflate.min.js"></script>
    <script src="lib/threejs/127/three.js-master/examples/js/libs/stats.min.js"></script>
    <script src="lib/threejs/127/three.js-master/examples/js/libs/dat.gui.min.js"></script>
    <script src="lib/js/Detector.js"></script>
</head>
<style type="text/css">
    html, body {
        margin: 0;
        height: 100%;
    }

    canvas {
        display: block;
    }

</style>
<body onload="draw()">
</body>
<script>
  var renderer, camera, scene, gui, light, stats, controls, meshHelper, mixer, action
  var clock = new THREE.Clock()
  var initRender = () => {
    renderer = new THREE.WebGLRenderer({antialias: true})
    renderer.setPixelRatio(window.devicePixelRatio)
    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.setClearColor(0xeeeeee)
    renderer.shadowMap.type = THREE.PCFSoftShadowMap
    renderer.shadowMap.enabled = true
    document.body.appendChild(renderer.domElement)
  }
  var initScene = () => {
    scene = new THREE.Scene()
    scene.background = new THREE.Color( 0xa0a0a0 )
    scene.fog = new THREE.Fog( 0xa0a0a0, 200, 1000 )
  }
  var initCamera = () => {
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000)
    camera.position.set(100, 200, 300)
  }
  var initGui = () => {
    gui = {
      animation: true,
      helper: true
    }
    var datGui = new dat.GUI()
    datGui.add(gui, 'animation').onChange(e => {
      if (e) {
        action.play()
      } else {
        action.stop()
      }
    })
    datGui.add(gui, 'helper').onChange(e => {
      meshHelper.visible = e
    })
  }
  var initLight = () => {
    scene.add(new THREE.AmbientLight(0x444444))
    light = new THREE.DirectionalLight(0xffffff)
    light.position.set(0, 200, 100)
    light.castShadow = true
    light.shadow.camera.top = 180
    light.shadow.camera.bottom = -100
    light.shadow.camera.left = -120
    light.shadow.camera.right = 120
    // light.shadow.camera.near = 20
    // light.shadow.camera.far = 200
    // light.shadow.mapSize.width = 1024
    // light.shadow.mapSize.height = 1024
    light.castShadow = true
    var debug = new THREE.CameraHelper(light.shadow.camera)
    debug.name = 'debug'
    // scene.add(debug)
    scene.add(light)
  }
  var initModel = () => {
    var helper = new THREE.AxesHelper(50)
    scene.add(helper)

    // 地板
    // var mesh = new THREE.Mesh(new THREE.PlaneBufferGeometry(2000, 2000), new THREE.MeshPhongMaterial({
    //   color: 0xffffff,
    //   depthWrite: false
    // }))
    // mesh.rotation.x = Math.PI / 2
    // mesh.position.y = -0
    // mesh.receiveShadow = true
    // scene.add(mesh)
    // 底面
    var planeGeometry = new THREE.PlaneBufferGeometry(2000, 2000)
    // var planeMatarial = new THREE.MeshStandardMaterial({color: 0xaaaaaa})
    var planeMatarial = new THREE.MeshPhongMaterial({
      color: 0xffffff,
      depthWrite: false
    })
    var plane = new THREE.Mesh(planeGeometry, planeMatarial)
    plane.rotation.x = -0.5 * Math.PI
    plane.position.y = -0
    plane.receiveShadow = true
    scene.add(plane)

    // 加载地板割线
    var grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000)
    grid.material.opacity = 0.2
    grid.material.transparent = true
    scene.add(grid)

    // 加载模型
    var loader = new THREE.FBXLoader()
    loader.load('data/model/SambaDancing/Samba Dancing.fbx', mesh => {
      meshHelper = new THREE.SkeletonHelper(mesh)
      scene.add(meshHelper)
      mesh.traverse(function (child) {
        if (child.isMesh) {
          child.castShadow = true
          child.receiveShadow = true
        }
      })
      // mesh.castShadow = true
      mixer = mesh.mixer = new THREE.AnimationMixer(mesh)
      action = mixer.clipAction(mesh.animations[0])
      action.play()
      scene.add(mesh)
    })
  }
  var initStats = () => {
    stats = new Stats()
    document.body.appendChild(stats.dom)
  }
  var initControls = () => {
    controls = new THREE.OrbitControls(camera, renderer.domElement)
    controls.enableDamping = true
    controls.target.set(0, 100, 0)
    //设置相机距离原点的最远距离
    controls.minDistance = 1
    //设置相机距离原点的最远距离
    controls.maxDistance = 2000
  }
  var render = () => {
    var time = clock.getDelta()
    if (mixer) {
      mixer.update(time)
    }
    controls.update()
  }
  var onWindowResize = () => {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
    renderer.setSize(window.innerWidth, window.innerHeight)
  }
  var animate = () => {
    render()
    stats.update()
    renderer.render(scene, camera)
    requestAnimationFrame(animate)
  }
  var draw = () => {
    if (!Detector.webgl) Detector.addGetWebGLMessage()

    initGui()
    initRender()
    initScene()
    initCamera()
    initLight()
    initModel()
    initControls()
    initStats()

    animate()

    window.onresize = onWindowResize
  }
</script>
</html>

效果如下:
在这里插入图片描述

;