three.js加载3D模型(glb/gltf/fbx)

2023-05-16

three.js加载3D模型(glb/gltf/fbx)

一、理解three:
1.一个可以在某个3D建模软件打开的东西,通过某种方案在浏览器中打开;
2.不要试图手动去创建3D图形,当然比较闲的话可以这样操作,
3.把three当作一个3D模型播放器,在播放器里可以对模型做一些操作:调色调光,调整坐标,切换视角,播放模型中的动画…;
4.某个建模软件(3dMax…)把已经做好的模型导出了模型文件(ojb,glb,gltf,fbx等格式),通过某个可支持此格式的Loader,最终渲染到场景里 ,就像webpack打包css需要css-loader一样的道理;
5.three 把WebGL的图形引擎封装成了3d api 
 

二、基本思路:
(1)构建场景Scene->

(2)通过Renderer渲染场景到画布->

(3)需要借助**Camera帮忙观察物体->

(4)通过loader(GLTFLoader,FBXLoader…)加载模型 ->

(5)通过requestAnimationFrame 把最新场景数据渲染出来

三、源代码

import  *as THREE from './lib.js';

import BG from './model/environment/bg.jpeg'

//模型
import xsr_fbx from './model/xsr/xsr.fbx'

//纹理贴图
import xsr_fbx_texture from './model/xsr/Stormland Robo 03H.png'

import xsr_fbx_logo_texture from './model/xsr/stormland_logo.png'


 const models = [
                {name:'机器头',path:require('./model/DamagedHelmet.glb').default,position:[0, 0, 5],type:'glb'},
                {name:'像素人',path:xsr_fbx,position:[0, 0, 50],type:'fbx',
                texture:[
                         {name:'gardener,hologram_2,hologram',path:xsr_fbx_texture},
                         {name:'Plane',path:xsr_fbx_logo_texture}],
                 }, 
                
               ]

const modelScene={
      State:{
        showGrid:false,
        showLightOrigin:false,
        wireframe:false,
      },
      Scene:null,
      Renderer:null,
      Camera:null,
      Model:null,
      Lights:null,
      AnimationMixer:null,
      Tclock:new THREE.Clock(),
      TestGui:null,
      TestStats:null,
      Controls:null,
      GridHelper:new THREE.GridHelper( 300, 50,  0x00FF12, 0xFFFFFF ),
      
      init:{
          //添加场景
           Scene:function(){
                  this.Scene =  new THREE.Scene()
                  this.Scene.background = new THREE.Color(0x282923);
                  
                  this.Scene.background = new THREE.TextureLoader().load(BG)
         
                  // THREE.Cache.enabled = true;
         
           },
           //添加渲染器
           Renderer:function(){
                  this.Renderer = new THREE.WebGLRenderer({antialias: true,alpha: true,premultipliedAlpha:true,precision: 'highp'})
                  this.Renderer.setPixelRatio(window.devicePixelRatio);
                  this.Renderer.setSize(window.innerWidth, window.innerHeight);
                  this.Renderer.setClearColor(0xeeeeee);
                  this.Renderer.shadowMap.enabled = true;
                  this.Renderer.physicallyCorrectLights = true;
                  this.Renderer.outputEncoding = THREE.sRGBEncoding;


                  ThreeApp.appendChild(this.Renderer.domElement); 
           },
           //添加相机
           Camera:function(){
             this.Camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight,1, 10000)
             this.Camera.position.set(0, 0, 50);
             this.Camera.lookAt(this.Scene.position)
           },

         
           TestGui:function(){
                  let _this = this;
                  this.TestGui =  new THREE.Dat.GUI()
                  this.TestGui.add({
                  changeBg: function () {
                    _this.Scene.background = new THREE.Color(0x1A1A1A);

                  }
                }, "changeBg");
           },
           //帧率状态
           Stats() {
              this.TestStats = new THREE.Stats();
              document.body.appendChild(this.TestStats.dom);
            }
      
      },

      //加载模型GLTF FBX
      modelLoader:function(MODEL){

             
              const loadTip = this.addLoadTip();
             
              this.Controls.autoRotate = false;
                //添加环境hdr
              MODEL.hdr && this.HdrLoader(MODEL.hdr);
             
              let Loader = '',MTYPE = MODEL.type || 'glb';
                 
              if('glb,gltf'.indexOf(MTYPE)!=-1){
                Loader = new THREE.GLTFLoader()
              }
               else if('fbx'.indexOf(MTYPE)!=-1){
                Loader = new THREE.FBXLoader()
              }else{
                 loadTip.textContent='请使用glb,gltf,fbx格式模型';
                 return;
              }
        

   
             

               Loader.load(MODEL.path, (geometry)=> {
                
                  loadTip.textContent='加载完成!';
                  //移除模型  
                  this.Model &&  this.Scene.remove(this.Model);
                
                  //设置相机位置
                  this.Camera.position.set(...MODEL.position);
              
                  this.Model = 'fbx'.indexOf(MTYPE)!=-1?geometry:geometry.scene;
                  
                 
                   //遍历模型字节点,获取相关参数设置
                  this.Model.traverse(function(child) {

                      if(MODEL.texture){
                        
                        MODEL.texture.map(item=>{
                           if(item.name.indexOf(child.name)!=-1){
                               child.material = new THREE.MeshPhongMaterial({
                                         map: THREE.ImageUtils.loadTexture(item.path)//颜色贴图
                                       });
                           }
                        }) 
                      }   
                       

                        if (child.isMesh) {
                            
                              // child.material.emissiveMap = child.material.map;
                           
                              //child.material.side = THREE.DoubleSide;
                              child.material.shininess=1;
                        
                              child.castShadow = true
                              child.receiveShadow = true

                              child.material.transparent=true;//材质允许透明 如果有玻璃材质时开启
                              child.material.opacity=1;//材质默认透明度                        
                            
                          }
                      } 
                   );

                  //模型自动居中
                  THREE.ModelAutoCenter(this.Model)
              
                  //查找模型动画,
                  if(this.Model.animations.length>0){

                       this.AnimationMixer = new THREE.AnimationMixer(this.Model);
                       this.AnimationMixer.clipAction(this.Model.animations[0]).play();

                       /* 其他模型动画方案:
                        const animationClip = this.Model.animations.find(animationClip => animationClip.name === "Walk");
                        this.AnimationMixer.clipAction(animationClip).play();
                        */
                  }


                  //把模型放入场景中
                  this.Scene.add(this.Model);

                  //加载完成后开始自动播放
                  setTimeout(()=>{
                     loadTip.style.display='none';
                     this.Controls.autoRotate = true;
                   },1000);
                  
                },
                (xhr)=>{
                   //加载进度
                   loadTip.textContent=(parseInt(xhr.loaded/xhr.total*100))+'%加载中...';
              
                },
                (err)=>{
                    loadTip.textContent='模型加载失败!'
                    console.log('模型加载失败!')
                }
              );
      },
      //加载光源
      addLight:function(){
            this.Lights = [
                  {name:'AmbientLight',obj:new THREE.AmbientLight(0xFFFFFF,1)},
                  {name:'DirectionalLight_top',obj:new THREE.DirectionalLight(0xFFFFFF,3),position:[0, 15, 0]},
                  {name:'DirectionalLight_bottom',obj:new THREE.DirectionalLight(0x1B1B1B,3),position:[0, -200, 0]},
                  {name:'DirectionalLight_right1',obj:new THREE.DirectionalLight(0xFFFFFF,1.5),position:[0, -5, 20]},
                  {name:'DirectionalLight_right2',obj:new THREE.DirectionalLight(0xFFFFFF,1.5),position:[0, -5, -20]},
            ];
           

           this.Lights.map(item=>{
            item.obj.name=item.name;
            item.position && item.obj.position.set(...item.position);
            item.Helper = new THREE.PointLightHelper( item.obj );
            this.Scene.add(item.obj);
           })  
           

      }, 
      //加载HDR贴图环境光
      HdrLoader:function(HDR){
          const pmremGenerator = new THREE.PMREMGenerator(this.Renderer); // 使用hdr作为背景色
                pmremGenerator.compileEquirectangularShader();
          const textureLoader = new THREE.RGBELoader()
               textureLoader.load(HDR,(texture, textureData)=> {
             
                const envMap = pmremGenerator.fromEquirectangular(texture).texture;

                envMap.isPmremTexture = true;
                pmremGenerator.dispose();
           
                this.Scene.environment = envMap; // 给场景添加环境光效果
                this.Scene.background = envMap; // 给场景添加背景图


              });
      },
      //添加事件
      addControls:function() {
         this.Controls = new THREE.OrbitControls(this.Camera, this.Renderer.domElement);
        // 如果使用animate方法时,将此函数删除
        //controls.addEventListener( 'change', render );
        // 使动画循环使用时阻尼或自转 意思是否有惯性
         this.Controls.enableDamping = true;
        //是否可以缩放
         this.Controls.enableZoom = true;
        //设置相机距离原点的最远距离-可以控制缩放程度
         this.Controls.minDistance = 0;
        //设置相机距离原点的最远距离
         this.Controls.maxDistance = 3000;//800
        //是否开启右键拖拽
         this.Controls.enablePan = false;
        //动态阻尼系数 就是鼠标拖拽旋转灵敏度
         this.Controls.dampingFactor = 0.5;
        //是否自动旋转
         this.Controls.autoRotate = false;
         this.Controls.autoRotateSpeed = 1;
      },
      //模型切换
      switchModel(){
           const _scope = this; 
   
          var switchModelStyle = document.createElement('style');
             switchModelStyle.type = "text/css";
             switchModelStyle.innerText +='.modelList{position:fixed;width:100%; display:flex;justify-content:space-around; bottom:0;left:0;color:#0EF4F4;background:rgba(14,14,44,0.9);cursor:pointer;}\
                .modelList li{width:50%;line-height:30px;padding:5px;text-align:center;font-size:14px;}.modelList li:last-child{border:0;}.modelList li:hover,.modelList li.on{background:#0E2440;}'

          const modelUL = document.createElement('ul');
                modelUL.className='modelList'

                models.map((item,index)=>{
                
                    modelUL.innerHTML+='<li class="'+(index==0?'on':'')+'">'+item.name+'</li>';
                }) 


          document.head.insertBefore(switchModelStyle, document.head.lastChild);
          ThreeApp.insertBefore(modelUL,ThreeApp.firstChild);
       

            let LIS = modelUL.children;
      
              for(let i=0;i<LIS.length;i++){
              
                LIS[i].οnclick=function(){
                    for(let j=0; j<LIS.length; j++){
                       LIS[j].className='';
                    }
                     this.className='on'
                     _scope.modelLoader(models[i]);
                     
                  }
              }

      },  
     //添加加载进度
     addLoadTip:function(){
       
            document.querySelector('.loadTip') && ThreeApp.removeChild(document.querySelector('.loadTip'));
           let  loadTip =  document.createElement('div');
                loadTip.className='loadTip'
                loadTip.style.cssText+='position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);border-radius:5px;background-color:rgba(0,0,0,0.5);padding:5px 10px;color:#fff;';
                ThreeApp.appendChild(loadTip);
              return loadTip;
      },
    
      //添加辅助面板
      addPanel:function(){

            const _scope = this;
            
            //帧率状态
            this.init.Stats.call(this)   
    

             // 添加
            const Panels=[
                  {
                    name:'辅助网格',
                    todo:function(){
                    
                        if(_scope.State.showGrid){
                          _scope.Scene.remove(_scope.GridHelper);
                          _scope.State.showGrid=false;
                             
                        }else{
                           _scope.Scene.add(_scope.GridHelper);
                          _scope.State.showGrid=true;
                        }
                       
                    }
                  },
                  {
                    name:'显示光源',
                    todo:function(){
                       
                            if(_scope.State.showLightOrigin){
                               _scope.Lights.map(item=>{_scope.Scene.remove(item.Helper)})
                              _scope.State.showLightOrigin = false
                            }else{
                              _scope.Lights.map(item=>{_scope.Scene.add(item.Helper)})
                              
                              _scope.State.showLightOrigin = true
                            }
                        
                    }
                  },
                   {
                    name:'骨架模式',
                    todo:function(){
                            if(_scope.State.wireframe){
                                _scope.Model.traverse( child=> {
                                     if ( child.isMesh ) {
                                        child.material.wireframe=false 
                                      }
                                })
                              _scope.State.wireframe = false
                            }else{
                               _scope.Model.traverse( child=> {
                                    if ( child.isMesh ) {
                                      child.material.wireframe=true 
                                    }
                                })
                              _scope.State.wireframe = true
                            }
                        
                    }
                  },

             ]
          
            
            //辅助面板DomTree
            var helpPanelStyle = document.createElement('style');
             helpPanelStyle.type = "text/css";
             helpPanelStyle.innerText +='#helpPanel{position:fixed;width:80px;top:50px;left:0;color:#0EF4F4;background:#0E0E2C;cursor:pointer;}\
                 #helpPanel li{border-bottom:1px solid #fff;line-height:30px;text-align:center;font-size:14px;}#helpPanel li:last-child{border:0;}#helpPanel li.on{color:green;}';
            var helpPanel =  document.createElement('ul');
                helpPanel.id='helpPanel'; 
            

            Panels.forEach(item=>{
               let LI  =  document.createElement('li');
               
                LI.innerText=item.name;
                LI.οnclick=function(){
       
                  this.className = this.className=='on'?'':'on'
                  item.todo(this)
                }
                helpPanel.appendChild(LI);
            })
           
         

            document.head.insertBefore(helpPanelStyle, document.head.lastChild);
            ThreeApp.insertBefore(helpPanel,ThreeApp.firstChild)

      },
      animation:function(){

          //更新控制器
            this.Renderer.render(this.Scene, this.Camera);
            this.TestStats.update();
            this.Controls.update();
            this.AnimationMixer && this.AnimationMixer.update(this.Tclock.getDelta());
            requestAnimationFrame(()=>this.animation());
      },
      onWindowResize:function() {
            this.Camera.aspect = window.innerWidth / window.innerHeight;
            this.Camera.updateProjectionMatrix();
            this.Renderer.setSize(window.innerWidth, window.innerHeight);
            this.Renderer.render(this.Scene, this.Camera);
      },
      run:function(){
            this.init.Renderer.call(this) 
            this.init.Scene.call(this)
            this.init.Camera.call(this)

            this.addControls();
             //添加环境光
            this.addLight() 
            this.modelLoader(models[0]);
            
            //添加辅助面板
            this.addPanel();
            this.animation(); 
            this.switchModel()
            
        
            window.onresize = ()=>this.onWindowResize(); 
                 
      }



}


modelScene.run();

四、演示效果

演示效果:source.nullno.com/three-demo/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

three.js加载3D模型(glb/gltf/fbx) 的相关文章

随机推荐

  • 制作一个有趣的涂鸦物联网小项目(涂鸦模组SDK开发 CBU BK7231N WiFi+蓝牙模组 HSV彩色控制)

    实现的功能 xff1a l APP控制月球灯 l 本地月球灯控制 l APP控制 大白 颜色 xff0c 实现各种颜色变身 l 门状态传感器状态APP显示 l 网络状态指示灯 xff0c 连接服务器长亮 xff0c 断开后闪烁 l 配网按键
  • 有关于串口通信程序的编写

    Win 32系统把文件的概念进行了扩展 无论是文件 通信设备 命名管道 邮件槽 磁盘 还是控制台 xff0c 都是用API函数CreateFile来打开或创建的 该函数的声明为 xff1a HANDLE CreateFile LPCTSTR
  • xilinx zynq-7000 linux下rs422串口通信测试步骤

    这几天使用xilinx的zynq 7000系列调试rs422串口 xff0c 期间遇到不少问题 xff0c 好在最终调试完成 xff0c 在这里记录一下调试步骤 我用的soc型号是xc7z100 xff0c 板载了2个rs422外设 xff
  • C++ 输出Unicode字符的正确方法

    正确输出 Unicode 字符 要输出特殊符号首先需要知道它的 Unicode 编码 xff0c 当然 xff0c 直接在剪切板里 copy 对应符号也是可以的 要查询字符对应的 Unicode 编码 xff0c 可以在如下网站 xff1a
  • TVS管与ESD保护二极管的区别

    TVS管与ESD保护二极管的区别 ESD 静电放电 xff08 Electro Static discharge xff09 TVS 瞬变电压抑制二极管 xff08 Transient Voltage Suppressors xff09 T
  • Linux下使用C++连接MySql8.0一二三事

    一 连接前准备 1 正确安装数据库 MySql 8 0 官网下载对应自己操作系统以及版本的包即可 xff0c 这一步一定要正确安装 推荐在官网https dev mysql com downloads 下载包 xff0c 之后使用 dpkg
  • 操作系统 基础篇

    基础篇 注 xff1a 本文内容总结自 计算机操作系统 第四版 xff08 汤小丹等人编著 xff09 第一章 引论 1 1 什么是操作系统 xff1f 操作系统是配置在计算机硬件上的第一层软件 其作用是 xff1a 管理好硬件设备 xff
  • 正确地写出二分查找代码 确定二分查找的边界

    一 Basic 二分查找一般用于在有序数组中查找某个特定的值 一般来说 xff0c 它需要确定 low 与 high 用于限定每轮迭代的范围 xff0c 用 mid 位置处元素与 target 比较 xff0c 从而降低每轮迭代的搜索空间
  • C++ 在构造函数和析构函数中调用虚函数究竟会怎么样?

    首先摘取一段来自 Effective C 43 43 的解释 xff1a 派生类对象构造期间进入基类的构造函数时 xff0c 对象类型变成了基类类型 xff0c 而不是派生类类型 同样 xff0c 进入基类析构函数时 xff0c 对象也是基
  • VS2015运行项目时提示:.exe不是内部或外部命令,也不是可运行程序或批处理文件

    VS2015运行项目时提示 xff1a exe不是内部或外部命令 xff0c 也不是可运行程序或批处理文件 解决方法 xff08 1 xff09 添加系统变量 xff08 即 xff0c 将该exe文件所在目录添加到系统Path中 xff0
  • 二维码分类

    二维码分类 最近在做二维码解码的项目 用的是zxing库 43 VS2015 43 OPENCV3 4 xff0c 网上说ZBAR库也ok xff1b 概念 xff1a 二维条码 二维码 xff08 2 dimensional bar co
  • Labview 编写TCP/IP 客户端断线重连机制程序,亲测可用

    程序面板如下图 xff1a 此程序支持任意一方断线重连机制 xff0c 仅供大家参考 xff01 实际工程中 xff0c 如果出现服务器出现宕机 xff0c 那么我们的客户端要有重连的机制 xff0c 不然软件不会自动连接服务器 xff0c
  • C++ STL 库函数大全

    include lt assert h gt 设定插入点 include lt ctype h gt 字符处理 include lt errno h gt 定义错误码 include lt float h gt 浮点数处理 include
  • 多旋翼飞行器电机旋转方向图示

  • FreeRTOS浅析:解决两个任务运行冲突,系统停止一个任务唤醒另一个任务的方法

    FreeRTOS中的任务和多线程的概念差不多 xff0c 但是任务的本质是把时间片无限的切小 xff0c 小到人分辨不出来 xff0c 其实还是一个时间只能运行一个任务 xff0c 这是和多线程的根本区别 FreeRTOS中的任务有几种运行
  • 各种路由器接口与连接方法

    转自于 http bbs pcsoft com cn thread 138952 1 4 html 路由器所在的网络位置比较复杂 xff0c 既可是内部子网边缘 xff0c 也可位于内 外部网络边缘 同时为了实现强大的适用性 xff0c 它
  • 迭代器(Iterator)

    迭代器 Iterator 是一个对象 xff0c 它的工作是遍历并选择序列中的对象 xff0c 它提供了一种访问一个容器 container 对象中的各个元素 xff0c 而又不必暴露该对象内部细节的方法 通过迭代器 xff0c 开发人员不
  • 单片机串口发送数据很慢?这种方法帮助你提高!

    本文介绍如何使用带FIFO的串口来减少接收中断次数 xff0c 通过一种自定义通讯协议格式 xff0c 给出帧打包方法 xff1b 之后介绍一种特殊的串口数据发送方法 xff0c 可在避免使用串口发送中断的情况下 xff0c 提高系统的响应
  • 2020-11-21

    xftp 提示无法显示远程文件夹 不是什么被动不被动的问题 是权限的问题 xff0c 如果这个文件夹有 34 x 34 权限 就可以打开 没有就不行
  • three.js加载3D模型(glb/gltf/fbx)

    three js加载3D模型 glb gltf fbx 一 理解three 1 一个可以在某个3D建模软件打开的东西 xff0c 通过某种方案在浏览器中打开 xff1b 2 不要试图手动去创建3D图形 xff0c 当然比较闲的话可以这样操作