threejs引入glb模型展示3d效果

threejs引入glb模型展示3d效果

<template>
   <!-- 三维画布 -->
 <div class="textbox">
    <div class="draw" ref="draw" id="three_div"/>
    <div v-if="loadSuccess" class="progress">
      <div class="progress-bar progress-bar-danger progress-bar-striped active" :style="{width:progress}">
        <div class="progress-value">60%</div>
      </div>
    </div>

  </div>
</template>
<script>
import * as THREE from "three"; //三维
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; //控制器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; //控制器
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js"; //控制器

import { OBJLoader } from "@/utils/OBJLoader";
import { MTLLoader } from "@/utils/MTLLoader";

export default {
 name: 'drawCanvas',
  data () {
    return {
        // 声明渲染器
        renderer:"",
        // 声明相机
        camera:"",
        // 声明场景
        scene:"",
        // 声明几何体
        geometry:"",
        // 声明材质
        material: "",
        // 声明网格
        mesh: "",
        // 声明相机控制器
       controls:"",
       clientWidth:'',
       clientHeight:'',
       //模型组
       group:[],
       progress:0,
       loadSuccess:false
    }
  },
  mounted(){
   this.init()
  },
  methods:{
   //初始化函数
    init() {
     // 初始化渲染器
     this.initRenderer();
     // 初始化场景
     this.initScene();
     // 初始化相机
     this.initCamera();
     //引入模型
     this.initgltfLoader();
    //  this.initOBJLoader()
     //地面
    //  this.addGround()
     // 初始化光源
      this.initLight();
     // 初始化动画
      this.animate();
      // 添加事件
     this.addmeth();
    },
    //初始化渲染器
    initRenderer(){ 
     //实例化渲染器
     this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      });
     // 设置渲染区域尺寸
     this.renderer.setSize(
        this.$refs.draw.offsetWidth,
        this.$refs.draw.offsetHeight
     );
     // 告诉渲染器需要阴影效果
     this.renderer.shadowMap.enabled = true
     this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
     // 设置背景色
    this.renderer.setClearColor(0x000000, 0) // 设置背景颜色
     this.$refs.draw.appendChild(this.renderer.domElement);
    },
     //初始化场景
    initScene(){
     //实例化场景
      this.scene = new THREE.Scene();
      // this.scene.background = new THREE.Color( 0xe0e0e0 );
      // this.scene.fog = new THREE.Fog( 0xe0e0e0, 20, 100 );
      // 红线是X轴,绿线是Y轴,蓝线是Z轴
      var axesHelper = new THREE.AxesHelper(100);
      // axesHelper.position.set(100,100,100);
      this.scene.add(axesHelper);
    },
    //初始化相机
    initCamera(){
      this.clientWidth = this.$refs.draw.clientWidth
      this.clientHeight = this.$refs.draw.clientHeight
      const k = this.clientWidth / this.clientHeight; //窗口宽高比
      this.camera = new THREE.PerspectiveCamera(45, k, 0.1, 1000);
      // this.camera.lookAt(new THREE.Vector3())
      this.camera.position.set(0, 40, 100);
      // 创建相机控制器
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    },
    //添加坐标
    randAxis(axis){
        return axis + 1;    
    },
    //引入外部模型obj
    initOBJLoader(){
        let _this = this
        // var objLoader = new OBJLoader();
        // objLoader.setPath('/static/obj/');//模型路径
        // objLoader.load('afs_15.obj', function(object) {      
        //     _this.scene.add(object);//加入场景
        //     _this.renderer.render( _this.scene, _this.camera );
        // });
            var onProgress = function(xhr) {
                if (xhr.lengthComputable) {
                    var percentComplete = xhr.loaded / xhr.total * 100;
                    // console.log(percentComplete)
                }
            };
            var onError = function(xhr) {
                console.log(xhr)
            };

            var mtlLoader = new MTLLoader();					// 模型1
            mtlLoader.setPath('/static/obj/');							// obj mtl文件的目录
            mtlLoader.load('96.mtl', function(materials) {
                materials.preload();
                var objLoader = new OBJLoader();
                objLoader.setMaterials(materials);
                objLoader.setPath('/static/obj/');
                objLoader.load('96.obj', function(object) {
                    object.position.x = -10;
                    object.position.y = 0;
                    object.position.z = 0;
                    object.scale.set(0.1, 0.05, 0.1);		// 长 高 宽 比例
                    _this.scene.add(object);
                }, onProgress, onError);
            });
    },
     //引入外部模型 gltf
     initgltfLoader(){
       const gltfLoader = new GLTFLoader();
       const dracoLoader = new DRACOLoader();
       dracoLoader.setDecoderPath('/static/draco/gltf/');// 这个是加载draco算法,这样才能解析压缩后的gltf模型格式.
       gltfLoader.setDRACOLoader( dracoLoader );
       const connture = new THREE.TextureLoader().load("/static/glb/white.jpg");
       const blue2 = new THREE.TextureLoader().load("/static/glb/blue48.png");
       const num = '192'
       gltfLoader.load("/static/glb/AFS"+num+".glb", (gltf) => {//SimpleSkinning  afs_mini
        let model = gltf.scene;
        console.log(model)
        model.scale.set(1,1, 1); //缩放
        model.position.set(0, -30, 0)
        // model.position.copy(new THREE.Vector3(100, -50, -40)); //基于原点的位置,没有需求改为0即可
         const portGroup = model.children && model.children[0].children.find(item => item.name === 'PORT_GROUP')

        
        if(num == '24'){
          portGroup.children.forEach(item => {
            item.children.forEach(i => {
              i.material = new THREE.MeshPhongMaterial({color: 0xFFFFFF});
              i.material.map = blue2
            })
          })
        } else {
          portGroup.children.forEach(item => {
            item.children.forEach(i => {
              i.material = new THREE.MeshPhongMaterial({color: 0xFFFFFF});
              i.material.map = connture
            })
          })
        }
        console.log(portGroup)
        this.scene.add(model);
       }, (xhr) => {
          const percentage = Math.floor(xhr.loaded / xhr.total * 100)
          this.progress = percentage + '%'
        });
     },
    //添加光源
    initLight() {
      // 平行光源
      // const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.7 );//模拟远处类似太阳的光源
      // directionalLight.color.setHSL( 0.1, 1, 0.95 );
      // directionalLight.position.set(50, 100, 100); 
      // this.scene.add(directionalLight)
      

      //全局环境光 
      const ambientLight = new THREE.AmbientLight( '#ffffff', 0.1 );
      this.scene.add( ambientLight );
      // 点光源
      const pointLight = new THREE.PointLight( '#ffffff', 1 );
      pointLight.position.set(100, 300, 300); 
      this.camera.add( pointLight );

      this.scene.add( this.camera );
    },
    //地面
    addGround(){
      const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) );
      mesh.rotation.x = - Math.PI / 2;
      this.scene.add( mesh );

      const grid = new THREE.GridHelper( 200, 40, 0x000000, 0x000000 );
      grid.material.opacity = 0.2;
      grid.material.transparent = true;
      this.scene.add( grid );

    },
    //运行动画
    animate(){
     requestAnimationFrame(this.animate); //循环调用函数
     // 刷新相机控制器
     this.controls.update();
     this.renderer.render(this.scene, this.camera);
    },
    // 添加全局方法
    addmeth() {
      // 监听窗口尺寸变化
      window.addEventListener("resize", this.changeSize, false);
      window.addEventListener("click", this.portClick, false);
    },
    // 监听尺寸变化
    changeSize() {
      // 重置渲染器输出画布canvas尺寸
      this.renderer.setSize(
        this.$refs.draw.offsetWidth,
        this.$refs.draw.offsetHeight
      );
      var k = this.$refs.draw.offsetWidth / this.$refs.draw.offsetHeight; //窗口宽高比
      //重置相机投影的相关参数
      this.camera.aspect = k;
      // 如果相机的一些属性发生了变化,
      // 需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
      this.camera.updateProjectionMatrix();
    },
    //点击端口
    portClick(event){
      //保持原事件
      event.preventDefault();
      this.getIntersects(event.layerX, event.layerY)
    },
    getIntersects(layerX, layerY) {
      // 建立射线
      const raycaster = new THREE.Raycaster()
      // 建立一个空物体
      const mouseVector = new THREE.Vector3()
      const x = (layerX / this.clientWidth) * 2 - 1
      const y = -(layerY / this.clientHeight) * 2 + 1
      mouseVector.set(x, y, 1)
      raycaster.setFromCamera(mouseVector, this.camera)
      raycaster.params.Line.threshold = 0.01
      let intersections = []
      intersections = raycaster.intersectObjects(this.scene.children,true);
      let selectedObject = null, // 被选中的模型
      origin = null  // 被选中的模型与射线的交点,用于确定挂牌位置
      console.log(intersections)

      if(intersections.length>0){
        for ( var i = 0; i < intersections.length; i++ ) {
          // 遍历线相交模型
          if(intersections[i].object instanceof THREE.Mesh){
            // 取第一个(距离最近)的相交Mesh类型模型
            // 如果要排除地面等参照模型,也可在此处添加判断条件
            selectedObject = intersections[i].object
            origin = intersections[i].point
            break
          }
        }
      }
      // console.log(selectedObject, origin)
      // 存在与射线相交模型
      if(selectedObject){
          this.outlineObj(selectedObject)
      }
    },

    outlineObj (selectedObjects) {
      console.log(selectedObjects)
      const occupy = new THREE.TextureLoader().load("/static/glb/occupy.jpg");
      if(selectedObjects.parent.name.includes('Port_')){
        selectedObjects.parent.children.forEach(item => {
          item.material.map = occupy
        })
      }
    },
  }

}

</script>
<style lang='less' scoped>
.textbox{
  width:1000px;
  height:1000px;
  position: relative;
}
.draw {
  width: 100%;
  height: 100%;
}
//进度条
.progress {
    height: 17px;
    background: #262626;
    padding: 3px;
    overflow: visible;
    border-radius: 20px;
    border-top: 1px solid #000;
    border-bottom: 1px solid #7992a8;
    margin-top: 50px;
    position: absolute;
    top:45%;
    left:40%;
    width: 300px;

    .progress-bar {
        border-radius: 20px;
        position: relative;
        animation: animate-positive 2s;
        float: left;
        width: 0;
        height: 100%;
        font-size: 12px;
        line-height: 20px;
        color: #fff;
        text-align: center;
        background-color: #2962f9;
        -webkit-transition: width .6s ease;
        -o-transition: width .6s ease;
        transition: width .6s ease;

    }

    .active {
        animation: reverse stripes 0.40s linear infinite, animate-positive 2s;
    }

    .progress-value {
        display: none;
        padding: 3px 7px;
        font-size: 13px;
        color: #fff;
        border-radius: 4px;
        background: #191919;
        border: 1px solid #000;
        position: absolute;
        top: -40px;
        right: -10px;
    }

    .progress-value:after {
        content: "";
        border-top: 10px solid #191919;
        border-left: 10px solid transparent;
        border-right: 10px solid transparent;
        position: absolute;
        bottom: -6px;
        left: 26%;
    }
}

.progress-bar-striped {
    background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
    background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
    background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
    -webkit-background-size: 40px 40px;
    background-size: 40px 40px;
}

@-webkit-keyframes stripes {
    from {
        background-position: 40px 0
    }

    to {
        background-position: 0 0
    }
}

@-o-keyframes stripes {
    from {
        background-position: 40px 0
    }

    to {
        background-position: 0 0
    }
}

@keyframes stripes {
    from {
        background-position: 40px 0
    }

    to {
        background-position: 0 0
    }
}

@-webkit-keyframes animate-positive {
    0% {
        width: 0;
    }
}

@keyframes animate-positive {
    0% {
        width: 0;
    }
}

</style>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值