three文档:Three.js电子书《Three.js零基础入门教程》_郭隆邦

先看代码

template>
  <div class="three" v-loading="loading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)">
    <!-- 检查站模型  -->
    <div id="three_div" @click="onMouseClick"></div>
  </div>
</template>
<script>
import * as THREE from 'three';
import { OBJLoader, MTLLoader } from 'three-obj-mtl-loader'; //obj
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; //gltf
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; //gltf
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
// 引入桑基图
export default {
  name: 'three',
  components: {
  },
  data() {
    return {
      scene: null, //场景对象实例
      camera: null, //相机实例
      renderer: null, //渲染器实例
      renderEnabled: true,
      timer: null,//定时器
      objectModal: null, //模型实例
      mixer: null,//混合器实例
      clock: new THREE.Clock(),//时钟对象
      loading:true
    };
  },
  beforeDestroy() {
    this.clearCache(this.objectModal); //清除模型
    this.clearRenderer(); //清除模型
    window.removeEventListener('resize', this.ResizeFun, false); //监听页面的变化
  },
  methods: {
    ResizeFun() {
      let container = document.getElementById('three_div');
      this.camera.aspect = container.clientWidth / container.clientHeight;
      this.camera.updateProjectionMatrix(); // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
      this.renderer.setSize(container.clientWidth, container.clientHeight);
    },
    timeRender() {
      this.renderEnabled = true;
      if (this.timer) {
        clearTimeout(this.timer);
      }
      this.timer = setTimeout(() => {
        this.renderEnabled = false;
      }, 3000);
    },
    clearCache(obj) {
      obj.geometry.dispose();
      obj.material.dispose();
    },
    clearRenderer() {
      if (this.renderer) {
        this.renderer.dispose();
        this.renderer.forceContextLoss();
        this.renderer.context = null;
        this.renderer.domElement = null;
        this.renderer = null;
      }
    },
    // 初始化场景
    init() {
      let container = document.getElementById('three_div');
      //场景
      this.scene = new THREE.Scene();
      // 2、创建摄像机 PerspectiveCamera('视角', '指定投影窗口长宽比', '从距离摄像机多远开始渲染', '截止多远停止渲染100')
      this.camera = new THREE.PerspectiveCamera(
        75,
        container.clientWidth / container.clientHeight,
        100,
        10000
      );

      this.camera.position.set(750, 150, 500); //设置相机位置
      this.camera.lookAt(this.scene.position); //设置相机方向(指向的场景对象)
      // 渲染器
      this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); //创建渲染器对象
      this.renderer.setSize(container.clientWidth, container.clientHeight); //设置渲染区域尺寸
      this.renderer.setClearColor(0xeeeeee, 0); //设置背景颜色
      container.appendChild(this.renderer.domElement); //body元素中插入canvas对象
      //控制器
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.addEventListener('change', () => {
        // this.timeRender();
      }); //监听页面的缩放,拖拽,旋转
      this.controls.target.set(400, -200, 0);//设置加载位置 左右  上下  前后
      this.controls.minDistance = 100; //设置相机距离原点的最远距离
      this.controls.maxDistance = 1000; //设置相机距离原点的最远距离
      this.controls.maxPolarAngle = Math.PI / 1; //设置可旋转的范围
      this.controls.enableZoom = true; //是否可以缩放
      this.controls.enablePan = true; //是否可拖拽
      this.controls.update();
      // 三维坐标轴
      // var axis = new THREE.AxisHelper(3);//定位中心点
      // this.scene.add(axis);
    },

    //给模型添加点击事件za
    onMouseClick(event) {
      let raycaster = new THREE.Raycaster();
      let mouse = new THREE.Vector2();
      let div3D = document.getElementById('three_div');
      // 通过鼠标点击的位置计算出raycaster所需要的点的位置,以屏幕中心为原点,值的范围为-1到1.
      let div3DLeft = div3D.getBoundingClientRect().left;
      let div3DTop = div3D.getBoundingClientRect().top;
      mouse.x = (event.clientX - div3DLeft) / div3D.clientWidth * 2 - 1;
      mouse.y = -((event.clientY - div3DTop) / div3D.clientHeight) * 2 + 1;
      // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
      raycaster.setFromCamera(mouse, this.camera);
      // 获取raycaster直线和所有模型相交的数组集合
      this.raycastMeshes(this.clickApp, raycaster);
    },
    raycastMeshes(callback, raycaster) {
      let intersects = [];
      // 获取整个场景
      let theScene = this.scene || new THREE.Scene();
      // 获取鼠标点击点的射线
      let theRaycaster = raycaster || new THREE.Raycaster();
      // 对场景及其子节点遍历
      for (let i in theScene.children) {
        // 如果场景的子节点是Group或者Scene对象
        if (
          theScene.children[i] instanceof THREE.Group ||
          theScene.children[i] instanceof THREE.Scene
        ) {
          // 场景子节点及其后代,被射线穿过的模型的数组集合
          // intersects = theRaycaster.intersectObjects(theScene.children[i].children, true)
          let rayArr = theRaycaster.intersectObjects(
            theScene.children[i].children,
            true
          );
          intersects.push(...rayArr);
        } else if (theScene.children[i] instanceof THREE.Mesh) {
          // 如果场景的子节点是Mesh网格对象,场景子节点被射线穿过的模型的数组集合
          // intersects.push(theRaycaster.intersectObject(theScene.children[i]))
        }
      }
      intersects = this.filtersVisibleFalse(intersects); // 过滤掉不可见的
      // 被射线穿过的模型的数组集合
      if (intersects && intersects.length > 0) {
        return callback(intersects);
      }
      // this.hiddenDetailDiv()
      return null;
    },
    // 有些对象是包含了很多children的所以需要遍历,并push到intersects数组去重。
    filtersVisibleFalse(arr) {
      let arrList = arr;
      if (arr && arr.length > 0) {
        arrList = [];
        for (let i = 0; i < arr.length; i++) {
          if (arr[i].object.visible) {
            arrList.push(arr[i]);
          }
        }
      }
      return arrList;
    },
    // 点击到位置的
    clickApp(intersects) {
      if (intersects[0].object !== undefined) {
 
       console.log(intersects[0].object, '这就是成功点击到的对象了~');
      }
    },
    // 加载模型obj+mtl
    loadObj() {
      // let manager = new THREE.LoadingManager();
      // // eslint-disable-next-line no-undef
      // new MTLLoader(manager).load('/static/models/c4d视频文件.mtl', (materials) => {
      //   materials.preload();
      //   // eslint-disable-next-line no-undef
      //   new OBJLoader(manager)
      //     .setMaterials(materials)
      //     .load('/static/models/c4d.obj', (obj) => {
      //       //初始展示的模型的缩放比例
      //       obj.scale.set(1, 1, 1);
      //       this.objectModal = obj;
      //       this.scene.add(obj);
      //       this.timeRender();
      //     });
      // });
      // 加载单个obj
      let objloader = new OBJLoader();
      objloader.load('./static/models/male02.obj', (obj) => {
        //初始展示的模型的缩放比例
        obj.scale.set(1, 1, 1);
        this.objectModal = obj;
        this.scene.add(obj);
        this.timeRender();
      });
    },
    //加载gltf模型
    loadgltf() {
      let loader = new GLTFLoader();
      let dracoLoader = new DRACOLoader();
      dracoLoader.setDecoderPath('./static/gltf/');// 这个是加载draco算法,这样才能解析压缩后的gltf模型格式.
      loader.setDRACOLoader(dracoLoader);
      return new Promise(() => {
        loader.load('./static/models/zzb.glb', (gltf) => {
          this.loading=false;
          //初始展示的模型的缩放比例
          gltf.scene.scale.set(1, 1, 1);
          this.objectModal = gltf;
          this.scene.add(gltf.scene);
           //骨骼网格模型作为参数创建一个混合器
          this.mixer = new THREE.AnimationMixer(gltf.scene);
          const clip = gltf.animations[0];//将第1帧动画设置为动画剪辑对象
          const action = this.mixer.clipAction(clip);//使用动画剪辑对象创建AnimationAction对象
          this.mixer.timeScale = 0.5;//默认1,可以调节播放速度
          action.setDuration(2).play();//设置单此循环的持续时间(setDuration也可以调节播放速度)并开始动画
          // this.timeRender();
        });
      });

    },

    // 灯光
    light() {
      let ambientLight = new THREE.AmbientLight(0xffffff); //环境光
      this.scene.add(ambientLight);
      // eslint-disable-next-line vars-on-top
      var directionalLight = new THREE.DirectionalLight(0xfffffe, 2); //点散光
      directionalLight.position.set(100, 100, 100);
      directionalLight.position.multiplyScalar(0.3);
      this.scene.add(directionalLight);
      //点光源
      let point = new THREE.PointLight(0xffffff);
      point.position.set(400, 200, 300); //点光源位置
      this.scene.add(point); //点光源添加到场景中
    },
    // 动画效果
    animate() {
      if (this.renderEnabled) {
        if (this.renderer) {
          //执行渲染操作   指定场景、相机作为参数
          this.renderer.render(this.scene, this.camera);
        }
      }
      requestAnimationFrame(this.animate);
     if (this.mixer) {
        const delta = this.clock.getDelta(); // 获取自上次调用的时间差
        this.mixer.update(delta);
      }
    }
  },
  mounted() {
    this.init();
    this.light();
    this.loadgltf();
    this.animate();
  }
};
</script>

一般就加载glb的模型 因为它是支持动画的,我这里的模型是压缩过的不然原始文件很大的浏览器会很卡的,压缩过的必须用draco算法,这样才能解析压缩后的gltf模型格式,压缩相关请看这里

模型要放到静态文件里边不然会加载不出来的。模型点击事件有点特殊相关注释也写了,有任何问题请留言或者私信我。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐