项目使用到3D模型加载渲染,故初学习了解之,

简单封装 代码如下,相关依赖自行npm安装

import * as THREE from "three";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import {ShaderPass} from "three/examples/jsm/postprocessing/ShaderPass";
import {FXAAShader} from "three/examples/jsm/shaders/FXAAShader";
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader";
import {DRACOLoader} from "three/examples/jsm/loaders/DRACOLoader";
import {EffectComposer} from "three/examples/jsm/postprocessing/EffectComposer";
import {RenderPass} from "three/examples/jsm/postprocessing/RenderPass";
import {OutlinePass} from "three/examples/jsm/postprocessing/OutlinePass";

class ThreeJsLoder {
  constructor(ref) {
    this.webglRef = ref;
    this.canvasSetting = {width: 960, height: 500}
    this.camera = new THREE.PerspectiveCamera(50, this.canvasSetting.width /   	this.canvasSetting.height, 1, 2000);
    this.renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
    this.HemisphereLight = new THREE.HemisphereLight(0xffffff, 0xaaaaaa, 1);
    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    this.scene = new THREE.Scene();
    this.devices = [];
    this.rayCaster = new THREE.Raycaster();
    this.effectFXAA = new ShaderPass(FXAAShader);
    this.gltfLoader = new GLTFLoader();
    this.dracoLoader = new DRACOLoader();
    this.dracoLoader.setDecoderPath("/draco/");
    this.dracoLoader.setDecoderConfig({type: 'js'});
    this.gltfLoader.setDRACOLoader(this.dracoLoader);
    this.composer = null;
  }

  /**
   * 加载模型
   * @param link 模型地址
   * @param eleId domId
   * @param onSuccess 成功回调
   * @param onProgress 加载过程回调
   * @param onError 失败回调
   */
  loadModel(link, eleId, onSuccess, onProgress, onError) {
    this.clear();
    this.gltfLoader.load(link, (gltf) => {
      gltf.scene.position.set(0, 20, 0);
      gltf.scene.scale.set(2.2, 2.2, 2.2);
      this.camera.position.set(10, 80, -80);
      
      this.scene.add(gltf.scene)   
      this.scene.add(this.HemisphereLight);
      this.scene.add(this.camera);
      
      this.renderer.setClearColor(new THREE.Color(0x000000));
      this.renderer.setSize(this.canvasSetting.width, this.canvasSetting.height);
      this.webglRef.appendChild(this.renderer.domElement);


      this.controls.addEventListener('change', () => {
        this.renderer.render(this.scene, this.camera)
      });
      this.controls.autoRotateSpeed = 1.5;
      this.controls.target.set(0, 0, 0);
      this.controls.update();
      this.renderer.render(this.scene, this.camera);
      gltf.scene.traverse((r) => {
        if (r instanceof THREE.Mesh) {
          this.devices.push(r);
        }
      });

      if (onSuccess) {
        onSuccess();
      }

    }, (xhr) => {
      if (onProgress) {
        let percentComplete = null;
        if (xhr.lengthComputable) {
          percentComplete = xhr.loaded / xhr.total * 100;
        }
        onProgress(xhr, percentComplete);
      }

    }, (e) => {
      if (onError) {
        onError(e);
      }
    });
  }


  /**
   * 获取点击的模型构件,创建选区效果,返回首个mesh对象
   * @param event
   * @param eleRef
   * @returns {boolean}
   */
  getChoose(event) {
    let x = (event.offsetX  / this.canvasSetting.width) * 2 - 1;
    let y = -(event.offsetY  / this.canvasSetting.height) * 2 + 1;
    this.rayCaster.setFromCamera(new THREE.Vector2(x, y), this.camera);
    let intersects = this.rayCaster.intersectObjects(this.devices);
    let mesh = false;
    if (intersects.length > 0) {
    //匹配首个目标对象
      mesh = intersects[0].object;     
      this.outlineObj(mesh)
    } else {
      this.renderer.render(this.scene, this.camera);
    }
    return mesh;
  }

  //给选中的构件模型 加个边框
  outlineObj(obj) {
    if (obj) {
      this.composer = new EffectComposer(this.renderer);
      const renderPass = new RenderPass(this.scene, this.camera);
      this.composer.addPass(renderPass);
      const outlinePass = new OutlinePass(
        new THREE.Vector2(this.canvasSetting.width, this.canvasSetting.height),
        this.scene,
        this.camera
      );
      outlinePass.selectedObjects = [obj];
      outlinePass.edgeStrength = 30.0;
      outlinePass.edgeGlow = 0;
      outlinePass.usePatternTexture = false;
      outlinePass.edgeThickness = 3.0;
      outlinePass.downSampleRatio = 1;
      outlinePass.pulsePeriod = 1;
      outlinePass.visibleEdgeColor.set(parseInt(0xff6347));
      outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0);

      outlinePass.clear = true;
      this.composer.addPass(outlinePass);

      // 自定义着色器通道
      this.effectFXAA.uniforms["resolution"].value.set(
        1 / window.innerWidth,
        1 / window.innerHeight
      );
      this.effectFXAA.renderToScreen = true;
      this.composer.addPass(this.effectFXAA);
      this.composer.render()
    } else {
      this.composer = null;
    }
  }

  clear() {
    this.devices.splice(0, this.devices.length);
    this.scene.clear();
    this.camera.clear();
    this.renderer.clear();
    this.controls.update();
    this.renderer.render(this.scene, this.camera)
    this.webglRef.innerHTML = " ";
  }

}


export {ThreeJsLoder}

#obj转gltf文件 
obj2gltf -i luyu.obj -o luyu.gltf
#压缩gltf文件
gltf-pipeline -i luyu.gltf -o out.gltf -d 

vue2.x 使用

1.node_modules文件下 找到three文件夹, 找到/examples/js/libs/draco/ 将draco整个文件夹复制到public路径下
2.threeJS中的渲染对象 不可用vue的数据方向绑定,会造成卡顿

<template>
  <div v-loading="!webglShow">
    <div
      id="webgl"
      ref="webgl"
      @click="clickEvent"
    ></div>
  </div>
</template>

<script>
import {ThreeJsLoder} from "@/utils/ModelConfig/ThreeJsLoder";
let threeJsLoder;


export default {
  props: ["modelForm"],
  name: "ModelConfig",
  data() {
    return {
      webglShow: false,
      mesh: {},
    }
  },
  mounted() {
    this.init({link:"压缩gltf的路径地址"});
  },
  methods: {
    init(item) {
      modelConfig = new ThreeJsLoder(this.$refs.webgl);
      modelConfig.loadModel(item.link,()=>{
        this.webglShow = true;
      })
    },
    clickEvent(event) {
      this.mesh = modelConfig.getChoose(event);
      if(this.mesh){
       console.log(this.mesh);
      }
    },



};
</script>
<style scoped>

</style>

效果如图,

在这里插入图片描述
参考
Threejs大型obj文件的秒加载实现

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐