vue2怎么使用threejs展示Ready Player Me 提供的3D模型,并加载动画?

我之前发表了一篇展示3d模型的,那个插件比较简单,但是不能加载模型动画,有需要的可以去看看:

Vue项目中怎么展示3D模型(.glb文件)?_Single-Thread的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_48373171/article/details/132345311?spm=1001.2014.3001.5501

接下来要写的就是本文的重点,公司需要,不得不琢磨

ready player me 官网:
Ready Player Me - Playersicon-default.png?t=N7T8https://readyplayer.me/players

安装:npm i threejs (安装threejs)

安装:npm i element-resize-detector(安装一个vue监听元素宽高的插件,为了适配)

注意:element-resize-detector这个插件要在main.js里面全局引入一下(直接复制粘贴到你的main.js文件中就行,😏)


//安装完之后在vue2的main.js文件导入插件
import ElementResizeDetectorMaker from "element-resize-detector";
//全局挂载插件
Vue.prototype.$erd = ElementResizeDetectorMaker();

直接上代码,注释写的比较详细,不明白的可以看注释:

一、template部分:
 

<template>
  <div class="content" style="background: white;">
    <!-- container就是要展示模型的容器 -->
    <div  id="container" class="container"></div>
  </div>
</template>

二、script部分(注释详细,请耐心阅读)

<script>
// 导入 Three.js以及相关需要用的部分
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
export default {
  data() {
    return {
      // 初始化场景,相机,灯光,混合器等巴拉巴拉巴拉
      scene: null,
      light: null,
      camera: null,
      renderer: null,
      model: null,
      group: null,
      mixer:null,
    }
  },
  // 页面渲染完毕之后,启用方法
  mounted() {
    this.init()  //初始化
    this.loadGltf()  //加载模型
  },
  methods: {
    init() {
      let vm = this  //这里设置vm是this指向问题,一定要设置*******
      let container = document.getElementById("container");//获取显示3D模型的DOM结构
      const elementResizeDetectorMaker = require("element-resize-detector"); //导入vue监听指定元素变化的插件
      let erd = elementResizeDetectorMaker();  //实例化插件
      erd.listenTo(container, element => {  //指定要监听的元素
          //这里面是监听变化后操作的
          const width = element.clientWidth;
          const height = element.clientHeight;
          console.log(width,height)
          // 变化之后重新设置渲染器大小
          vm.renderer.setSize(width, height);

          // 变化之后重新设置相机纵横比
          vm.camera.aspect = width / height;
          vm.camera.updateProjectionMatrix();
      });
      // 初始化场景
      this.scene = new THREE.Scene();
      // 加载环境光,不加则什么都看不见
      this.scene.add(new THREE.AmbientLight(0xffffff, 0.4));
      this.light = new THREE.DirectionalLight(0xffffff, 1.0); 
      this.light.position.set(1, 1, 1).normalize();
      this.scene.add(this.light); //场景加载灯光

      // 加载网格
      const grid = new THREE.GridHelper( 50, 10, 0x444444, 0x444444 );
      grid.material.opacity = 0.3;  //设置网格的透明度
      grid.material.transparent = true;  //设置网格是否可以透明
      grid.rotation.y = Math.PI/2.0;  //设置网格方向为模型y轴方向
      grid.position.y = -1  //设置网格位置为模型中心位置向下一格
      grid.rotateY(-60)  //设置网格绕Y轴逆时针旋转
      this.scene.add( grid );  //场景加载网格
      
      /**
       * 相机设置
       */
      // 初始化透视相机
      this.camera = new THREE.PerspectiveCamera(75,container.clientWidth / container.clientHeight, 0.1, 1000);
      // 设置相机位置
      this.camera.position.z = 2;
      this.camera.position.y = 0.5;
      this.camera.up = new THREE.Vector3(0, 1, 0);
      // /**
      //  * 创建渲染器对象
      //  */
      this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }), // 开启抗锯齿和背景透明
      this.renderer.setSize(container.clientWidth,container.clientHeight);
      this.renderer.setPixelRatio(devicePixelRatio); //干啥的不清楚,反正开启之后画质直接起飞

      // 相机的控件:加上它就能实现鼠标控制模型旋转、缩放
      const controls = new OrbitControls(this.camera, this.renderer.domElement);
      
      // 最后就是把所有加载到指定的dom结构上
      container.appendChild(this.renderer.domElement);

    },
    loadGltf() {
      let vm = this;  //也是防止this指向问题
      const loader = new GLTFLoader();  //模型加载器,有了它才能加载.glb等后缀文件并解析

      // 开始加载模型文件(可替换自己的模型文件)
      loader.load("https://models.readyplayer.me/64f1b1fefe61576b46f28fbb.glb?cacheControl=true&uat=1693561341962", function (gltf) {
        // 加载完之后
        vm.model = gltf.scene;
        vm.model.position.set(0,-1,0)  //设置模型初始的位置
        vm.scene.add(vm.model)  //在场景中加载这个模型

        const fbxLoader = new FBXLoader()  //这个是读取.fbx文件的,有了它才能读取.fbx的动画文件

        // 加载动画文件(我这个动画文件是放在vue项目public中的,可替换成自己的.fbx文件)
        fbxLoader.load('F_Standing_Idle_001.fbx', (animation)=>{
          //加载动画混合器,将模型和读取.fbx文件得到的动画参数混合到一起
          vm.mixer = new THREE.AnimationMixer(vm.model)  
          
          // 默认读取第一个动画
          const animClip = animation.animations[0];

          // 调用Test方法
          let res = vm.Test(animClip)

          const action = vm.mixer.clipAction(res);
          //  开启动画
          action.play()
          
        })
        // 执行动画
        function animate() {
          
          // 渲染器添加场景和相机
          vm.renderer.render(vm.scene, vm.camera);
          requestAnimationFrame(animate);
          if (vm.mixer) {
            vm.mixer.update(0.016);
          }
        }
        animate();
      }
        
      );
    },
    // 这段是我从readyplayerme官方提供的react封装的方法上扣出来的
    Test(value){
    
      const { tracks } = value;
      for (let i = 0; i < tracks.length; i+=1) {
        const hasMixamoPrefix = tracks[i].name.includes('mixamorig');
        if (hasMixamoPrefix) {
          tracks[i].name = tracks[i].name.replace('mixamorig', '');
        }
        if (tracks[i].name.includes('.position')) {
          for (let j = 0; j < tracks[i].values.length; j += 1) {
            // Scale the bound size down to match the size of the model
            // eslint-disable-next-line operator-assignment
            tracks[i].values[j] = tracks[i].values[j] * 0.01;
          }
        }
        return value;
      }
    }
  }
}
</script>

三、style部分:
 

<style  scoped>
  *{
    margin: 0;
    padding: 0;
  }
  .content {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .container {
    width: 60%;  
    height: 700px; 
    background: white;
    border: 1px solid black;
  }
  .container :hover {
    cursor: pointer;
  }

四、展示(元素宽度适配的哦,因为用了vue的插件,上边有介绍):

以上就是文本有价值的信息,我也不会threejs,就是四处查看文档,会了点皮毛,希望对你有帮助,看完有帮助记得点个赞,不为别的,只为了记录自己工作遇到的疑难杂症!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Three.js 中的 3D 模型存储到 IndexedDB 中可以通过以下步骤实现: 1. 创建 IndexedDB 数据库:使用 IndexedDB API 创建一个数据库,并在其中创建一个存储对象来存储 3D 模型数据。 2. 将 3D 模型转换为二进制数据:使用 Three.js 提供的 GLTFExporter 将 3D 模型转换为二进制数据。 3. 存储二进制数据:将转换后的二进制数据存储到 IndexedDB 中的存储对象中。 4. 从 IndexedDB 中获取数据:使用 IndexedDB API 获取存储对象中的数据,并使用 Three.js 提供的 GLTFLoader 将二进制数据转换为 3D 模型。 下面是一个示例代码,用于将 Three.js 中的 3D 模型存储到 IndexedDB 中并从 IndexedDB 中获取数据: ```javascript // 创建 IndexedDB 数据库 const request = window.indexedDB.open('3d-models', 1); let db; request.onerror = (event) => { console.log('Database error: ' + event.target.errorCode); }; request.onsuccess = (event) => { db = event.target.result; }; request.onupgradeneeded = (event) => { const db = event.target.result; const objectStore = db.createObjectStore('models', { keyPath: 'id' }); }; // 将 3D 模型转换为二进制数据并存储到 IndexedDB 中 const exporter = new THREE.GLTFExporter(); exporter.parse(scene, (gltf) => { const binaryData = new Blob([gltf], { type: 'application/octet-stream' }); const transaction = db.transaction(['models'], 'readwrite'); const objectStore = transaction.objectStore('models'); const request = objectStore.put({ id: 'my-3d-model', data: binaryData }); request.onerror = (event) => { console.log('Error storing 3D model: ' + event.target.error); }; request.onsuccess = (event) => { console.log('3D model stored successfully'); }; }); // 从 IndexedDB 中获取数据并将二进制数据转换为 3D 模型 const loader = new THREE.GLTFLoader(); const transaction = db.transaction(['models'], 'readonly'); const objectStore = transaction.objectStore('models'); const request = objectStore.get('my-3d-model'); request.onerror = (event) => { console.log('Error retrieving 3D model: ' + event.target.error); }; request.onsuccess = (event) => { const binaryData = event.target.result.data; const reader = new FileReader(); reader.onload = (event) => { const gltfBlob = new Blob([event.target.result], { type: 'application/octet-stream' }); const fileReader = new FileReader(); fileReader.onload = (event) => { const gltf = JSON.parse(event.target.result); loader.parse(gltf, '', (object) => { scene.add(object.scene); }); }; fileReader.readAsText(gltfBlob); }; reader.readAsArrayBuffer(binaryData); }; ``` 需要注意的是,IndexedDB 的 API 使用起来比较复杂,需要注意处理异步操作和错误处理。同时,存储和读取大型的二进制数据可能会影响性能,因此需要根据具体情况进行优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值