three、vue中使用three、three怎么加载obj模型和mtl文件、three自定义800*800大小怎么拾取/点击

以上都是这一个星期碰到的坑,找了很多很多资料,总结归纳一下,希望对你的项目有一点点帮助

先说说需求
1.加载3D模型
2.点击模型的子模型会显示对于子模型名称
3.不全屏展示,还要点击子模型
4.创建控制器的时候注意传入dom
我并没有很全面的学习three,虽然有课程,但是项目进度并不允许,只能踩着石头过河,查找前辈们总结的经验,在这里归纳总结一下

效果图

three、vue中使用three、three怎么加载obj模型和mtl文件、three自定义800*800大小怎么拾取/点击_第1张图片

我这个项目替换自己的obj时可能会报错,因为每个obj文件的子模型的name不一样,解决方法

three、vue中使用three、three怎么加载obj模型和mtl文件、three自定义800*800大小怎么拾取/点击_第2张图片
three、vue中使用three、three怎么加载obj模型和mtl文件、three自定义800*800大小怎么拾取/点击_第3张图片
项目太丑,扎眼了,刚写好,不立刻记下笔记就忘了,自己优化 npm run serve

链接:https://pan.baidu.com/s/1fVZLTfuALxi4Gmpc6hGNfg 
提取码:ee0e 
复制这段内容后打开百度网盘手机App,操作更方便哦--来自百度网盘超级会员V3的分享

记录这几天挣扎的崩溃的内心

1.vue引入three 不用在main中做配置

import * as THREE from "three";
import { OBJLoader, MTLLoader } from "three-obj-mtl-loader";
import { CSS2DRenderer, CSS2DObject } from "three-css2drender";
const OrbitControls = require("three-orbit-controls")(THREE);

或者直接在package.json中dependencies和devDependencies对象中加入下面的
然后 npm install

 "dependencies": {
    "three-css2drender": "^1.0.0",
    "three-obj-mtl-loader": "^1.0.3"
  },
  "devDependencies": {
    "three": "^0.123.0",
    "three-obj-mtl-loader": "^1.0.3"
  },

2.在return中定义若干变量

听说不能直接赋值,vue会监听,也没研究那么深,人家竟然这么说,也就没定义值

data() {
    return {
      axes: "",
      group: "",
      scene: "",
      light: "",
      camera: "",
      controls: "",
      renderer: "",
      directionalLight: "",
      ambient: "",
      loader: "", //地板
      glass_material: "",
      publicPath: process.env.BASE_URL,
      m1: "",
      m2: "",
      m3: "",
      mouse: "",
      objects: [],
      raycaster: "",
      getBoundingClientRect: {},
      offsetWidth: "",
      offsetHeight: "",
      theta: 0, //相机旋转角度
      cord1: {},
      cord2: {},
      cord3: {},
      cord4: {},
      selectObject: {}, //被点击的第一个元素
    };
  },
2.看mounted的钩子有三个方法要执行
mounted() {
    this.init();
    this.loadObj();
    this.animate();
  },

先看第一个方法 this.init();

就是给return中没赋值的变量赋值成new出来的three实例

这里是:因为我加载的obj模型是有四个子模块组成的打印的时候它的child有四个,才定义四个的

      this.cord1 = new THREE.Object3D();
      this.cord2 = new THREE.Object3D();
      this.cord3 = new THREE.Object3D();
      this.cord4 = new THREE.Object3D();

接下来:创建一个场景

this.scene = new THREE.Scene();

接下来:在three.js,可以利用THREE.Raycaster来达到点击与交互,即选择物体的操作。点击事件少不了的东西

   this.raycaster = new THREE.Raycaster();

接下来:因为是小白,就给自己加载的模型放了个长500的坐标轴方便观看

   this.scene.add(new THREE.AxesHelper(500));

接下来:得创建相机了,因为不想弄全屏的模型展示,做个800x800的,要是想做全屏的就用window.innerWidth/window.innnerHeight,因为800/800为1,担心不知道是哪个一,第二个参数

	 /**
       * @param {
       * 初始化相机
       * }
       */
      this.camera = new THREE.PerspectiveCamera(
        1,
        // window.innerWidth / window.innerHeight,
        800/800,
        1,
        10000
      );

接下来设置相机的位置还是什么东西,自己查查这个api就会有介绍,反正我不懂,瞎设置呗,然后就把相机add到scene(场景)中

    this.camera.position.set(0, 1000, 1000);
      this.camera.lookAt(new THREE.Vector3(0, 0, 0));
      // this.camera.lookAt(0, 0, 0);
      this.scene.add(this.camera);

接下来:既然有点击获取当前的obj模型模块,当然得获取下鼠标的位置吧

   //鼠标的位置
   this.mouse = new THREE.Vector2();

接下来:渲染器,干什么的?依次的设置是,alpha: true, antialias: true,加了好像清楚一点,然后就是渲染大小当然我的是800x800的全屏就用window.innerWidth, window.innerHeight,渲染显示比例,渲染背景颜色,最后是追加到dom节点


      /**
       * @param {
       * 渲染器
       * }
       */
      this.renderer = new THREE.WebGLRenderer({
        alpha: true,
        antialias: true,
      });
      // this.renderer.setSize(window.innerWidth, window.innerHeight);
      this.renderer.setSize(800, 800);
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.setClearColor(0x87ceeb, 1.0);
      const container = document.getElementById("container");
      container.appendChild(this.renderer.domElement);

接下来:控制器,不添加模型不能旋转,开始找了好多参数加里面,最后发现还是什么都不加效果控制的更舒坦一点

 /**
       * @param {
       * 控制器
       * }
       */
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      // this.controls.autoRotate=true;
      // this.controls.target.set(0, 0, 0);
      // this.controls.minDistance = 80;
      // this.controls.maxDistance = 400;
      // this.controls.maxPolarAngle = Math.PI / 3;
      this.controls.update();

接下来:定义光线,就是有点光打在引入的3d模型上,让模型能正常显示颜色,要不然就是黑白的,参数乱设置的,也没搞懂

  /**
       * @param {
       * 定义光线
       * }
       */
      this.directionalLight = new THREE.DirectionalLight(0xc1c1c1, 1);
      // 光线照射的方向
      this.directionalLight.position.set(0, 1000, 1000).normalize();
      this.scene.add(this.directionalLight);
      this.ambient = new THREE.AmbientLight(0xffffff, 1);
      this.ambient.position.set(0, 0, 0);
      this.scene.add(this.ambient);

接下来 外部obj,mtl 模型加载方法,碰的坑不亚于点击函数

1.设计人员给了obj mtl 我给放到项目的public/models文件夹下,路径怎么设置?,设置成/models/ssz.obj项目报错,找不到文件,经过查找总算找到了不报错的方法 在return中定义 publicPath: process.env.BASE_URL 在引用路径的时候 mtlLoader.load(${that.publicPath}models/zzs.mtl,function(){})
2.设计人员在做模型的时候用了图片做材质,可是没给我图片包–.--,都是第一次做,采坑
3.设计人员给我材质后还是找不到材质,打开mtl文件一看mtl文件下map_Ka D:\Windo\E\03����.fbm\1.jpg map_Kd D:\Windo\E\03����.fbm\1.jpg 可是在我的电脑完全没有D盘,全部手动修改map_Ka /models/1.jpg map_Kd /models/1.jpg

three、vue中使用three、three怎么加载obj模型和mtl文件、three自定义800*800大小怎么拾取/点击_第4张图片

4.总算引入成功了,可能反正还有一个报错(反正我的报错了)Handlers.get() has been removed. Use LoadingManager.getHandler() instead 错误处理

three、vue中使用three、three怎么加载obj模型和mtl文件、three自定义800*800大小怎么拾取/点击_第5张图片

需要在node_module > three-obj-mtl-loader > index.js中找到第543行并注释掉。在 545行重新定义loader
// var loader = THREE.Loader.Handlers.get( url );

var loader = manager.getHandler(url);

three、vue中使用three、three怎么加载obj模型和mtl文件、three自定义800*800大小怎么拾取/点击_第6张图片
在methods中定义的loadObj(加载obj模型)函数代码

loadObj() {
      let that = this;
      let objLoader = new OBJLoader();
      let mtlLoader = new MTLLoader();
      let textureLoader = new THREE.TextureLoader();
      // mtlLoader.load(`${that.publicPath}models/zzs.mtl`, function (materials) {
      mtlLoader.load(
        `${that.publicPath}models/zzs.mtl`,
        // `${that.publicPath}models/object.mtl`,
        function (materials) {
          objLoader.setMaterials(materials);
          objLoader.load(
            // `${that.publicPath}models/object.obj`,
            `${that.publicPath}models/zzs.obj`,
            function (obj) {
              obj.scale.multiplyScalar(1);
              obj.traverse(function (child) {
                console.log(child.name, "ddddd");
                // 这里把匹配的模块单独提取出来,如果想让其中一个子模块动
                // 到时候直接可以在函数外面methed中写一个方法设置,比方说
                //就单独让m3这个子模块单独动了
                // that.m3.position.x -= 0.1;
                // that.m3.position.y -= 0.1;
                // that.m3.position.z -= 0.1;
                if (child instanceof THREE.Mesh) {
                  if (child.name == "Box001") {
                    that.m1 = child;
                  } else if (child.name == "Cylinder002") {
                    that.m2 = child;
                  } else if (child.name == "Cylinder003") {
                    //圆柱
                    that.m3 = child;
                  } else if (child.name == "Cylinder004") {
                    //圆柱
                    that.m4 = child;
                  }
                }
              });
              // 这里是做关联,比方说我要移动m4模块,整个模块都会跟着动
              // 我要移动m3模块,只有m3,m2,m1模块会动
              that.cord4.add(that.m4);
              that.cord3.add(that.m3, that.cord4);
              that.cord2.add(that.m2, that.cord3);
              that.cord1.add(that.m1, that.cord2);
              that.scene.add(that.cord1);
              that.render();
              return obj;
              /**
               * @param {
               * 将模型加入场景中
               * }
               */
              // that.scene.add(obj);
              /**
               * @param {
               * 将纹理加入模型中
               * }
               */
              // that.renderer.render(that.scene, that.camera);
            },
            function (xhr) {
              console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
            },
            function (error) {
              console.log("An error happened");
            }
          );
        }
      );
    },

接下来:添加窗口监听事件,固定宽高的就不用了了吧,800x800监听window窗口大小不还是800x800展示么,不过像是用window.innerWidth做长window.innerHeight做宽全屏展示的还是要监听的

window.addEventListener("resize", this.onWindowResize, false);

然后这是this.onWindowResize的方法,卸载methods中就行

 /**
     * @param {
     * 窗口监听函数
     * }
     */
    onWindowResize() {
      this.camera.aspect = window.innerWidth / window.innerHeight;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(window.innerWidth, window.innerHeight);
    },

接下来:点击总得有个点击事件,
一、这个是800x800固定宽高的点击函数写法,因为不需要监听点击全屏啊,监听当前的dom容器就行了

this.$refs.container.addEventListener("click", this.clickTest, false);

二、这个是设置长window.innerWidth宽window.innerHeight的写法

window.addEventListener("click", this.clickTest, false);

三、点击函数方法:在methods中定义clickTest方法
开始只知道全屏的点击可以点击到模型本身并不知道800x800这种自定义宽高的怎么点击到当前obj模型,总是匹配不到,全网都没找到这个方法,可能全网就我是最笨的,
window.innerWidth x window.innerHeight全屏大小的写法

this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

800x800写法:因为监听的是当前的dom元素的点击区域,发现event对象中有layerX 和layerY 两个参数非常可疑,正是鼠标点击dom元素上在dom元素自身上的坐标,参考上面全屏写法所以就懂了。
this.raycaster.intersectObjects(this.objects);是判断当前点击的位置与照相机的位置相链接成一条直线,哪些模型会在这条线上,返回一个数组,这个数组如果有值,第[0]位就是最上面的,也就是所谓点击的这么模型中的子模块

 	  this.mouse.x = (event.layerX / 800) * 2 - 1;
      this.mouse.y = -(event.layerY / 800) * 2 + 1;
    //点击事件
    clickTest(event) {
      console.log(event)
      event.preventDefault();
      this.objects = [];
      // this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      this.mouse.x = (event.layerX / 800) * 2 - 1;
      this.mouse.y = -(event.layerY / 800) * 2 + 1;
      // this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
      this.raycaster.setFromCamera(this.mouse, this.camera);
      this.scene.children.forEach((child) => {
        //根据需求判断哪些加入objects,也可以在生成object的时候push进objects
        if (
          !(child instanceof THREE.GridHelper) &&
          !(child instanceof THREE.DirectionalLight) &&
          !(child instanceof THREE.AmbientLight)
        ) {
          child.children.forEach((childv) => {
            if (childv instanceof THREE.Mesh) {
              //根据需求判断哪些加入objects,也可以在生成object的时候push进objects
              this.objects.push(childv);
            } else if (childv instanceof THREE.Object3D) {
              this.meshDg(childv.children);
            }
          });
        }
      });
      console.log(this.objects, 6666666 + "obj");
      var intersects = this.raycaster.intersectObjects(this.objects);
      console.log(intersects, 8888 + "intersects");

      if (
        intersects.length != 0 &&
        intersects[0].object instanceof THREE.Mesh
      ) {
        this.selectObject = intersects[0].object;
        this.changeMaterial(this.selectObject);
        console.log(this.selectObject.name);
        // $("#alert_center").css({
        //   display: "block",
        // });
        // $("#alert_font").html(
        //   "当前点击的模块名字是:" + this.selectObject.name
        // );
        this.render();
      }
    },

点击事件,点击后改变当前对象的方法:在methods中定义,点击了给它变个颜色看看,纯属个人无聊

    /**
     * @param {
     * 改变当前对象属性
     * }
     */
    changeMaterial(object) {
      var material = new THREE.MeshLambertMaterial({
        color: 0xffffff * Math.random(),
      });
      object.material = material;
    },

动画:写完后面,就忘了前面,写着不报错无关痛痒,在methods中定义

    /**
     * @param {
     * 动画
     * }
     */
    animate() {
      requestAnimationFrame(this.animate);
      this.render();
    },

渲染:忘了具体干嘛的,应该少不了:在methos中定义

 /**
     * @param {
     * 渲染
     * }
     */
    render() {
      this.renderer.render(this.scene, this.camera);
    },

下面代码复制下来也跑不了的,模型什么的都需要更改

   <template>
  <div class="spring">
    <!-- <div class="box"></div> -->
    <div id="container" ref="container"></div>
  </div>
</template>
<script>
import * as THREE from "three";
import { OBJLoader, MTLLoader } from "three-obj-mtl-loader";
import { CSS2DRenderer, CSS2DObject } from "three-css2drender";
const OrbitControls = require("three-orbit-controls")(THREE);
export default {
  name: "",
  components: {},
  props: {},
  data() {
    return {
      axes: "",
      group: "",
      scene: "",
      light: "",
      camera: "",
      controls: "",
      renderer: "",
      directionalLight: "",
      ambient: "",
      loader: "", //地板
      glass_material: "",
      publicPath: process.env.BASE_URL,
      m1: "",
      m2: "",
      m3: "",
      mouse: "",
      objects: [],
      raycaster: "",
      getBoundingClientRect: {},
      offsetWidth: "",
      offsetHeight: "",
      theta: 0, //相机旋转角度
      cord1: {},
      cord2: {},
      cord3: {},
      cord4: {},
      selectObject: {}, //被点击的第一个元素
    };
  },
  created() {},
  mounted() {
    this.init();
    this.loadObj();
    this.animate();
  },
  methods: {
    /**
     * @param {
     * 初始化three.js相关内容}
     */
    init() {
      /**
       * @param {
       * 场景
       * }
       */
      this.cord1 = new THREE.Object3D();
      this.cord2 = new THREE.Object3D();
      this.cord3 = new THREE.Object3D();
      this.cord4 = new THREE.Object3D();
      this.scene = new THREE.Scene();
      this.raycaster = new THREE.Raycaster();
      /**
       * @param {
       * 世界坐标系
       * }
       */
      this.scene.add(new THREE.AxesHelper(500));
      // var helper = new THREE.GridHelper(3000, 50, 0xcd3700, 0x4a4a4a);
      // this.scene.add(helper);
      /**
       * @param {
       * 初始化相机
       * }
       */
      this.camera = new THREE.PerspectiveCamera(
        1,
        // window.innerWidth / window.innerHeight,
        1,
        1,
        10000
      );

      this.camera.position.set(0, 1000, 1000);
      this.camera.lookAt(new THREE.Vector3(0, 0, 0));
      // this.camera.lookAt(0, 0, 0);
      this.scene.add(this.camera);
      //鼠标的位置
      this.mouse = new THREE.Vector2();

      /**
       * @param {
       * 渲染器
       * }
       */
      this.renderer = new THREE.WebGLRenderer({
        alpha: true,
        antialias: true,
      });
      // this.renderer.setSize(window.innerWidth, window.innerHeight);
      this.renderer.setSize(800, 800);
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.setClearColor(0x87ceeb, 1.0);
      const container = document.getElementById("container");
      container.appendChild(this.renderer.domElement);

      /**
       * @param {
       * 获取当前dom元素坐标
       * }
       */
      this.getBoundingClientRect = this.$refs.container.getBoundingClientRect();
      this.offsetWidth = this.$refs.container.offsetWidth;
      this.offsetHeight = this.$refs.container.offsetHeight;

      /**
       * @param {
       * 控制器
       * }
       */
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.update();

      /**
       * @param {
       * 定义光线
       * }
       */
      this.directionalLight = new THREE.DirectionalLight(0xc1c1c1, 1);
      // 光线照射的方向
      this.directionalLight.position.set(0, 1000, 1000).normalize();
      this.scene.add(this.directionalLight);
      this.ambient = new THREE.AmbientLight(0xffffff, 1);
      this.ambient.position.set(0, 0, 0);
      this.scene.add(this.ambient);

      /**
       * @param {
       * 添加窗口监听事件(resize-onresize即窗口或框架被重新调整大小)
       * }
       */
      this.$refs.container.addEventListener("click", this.clickTest, false);
        // window.addEventListener("resize", this.onWindowResize, false);
    },
    /**
     * @param {
     * 外部模型加载函数
     * }
     */
    loadObj() {
      let that = this;
      let objLoader = new OBJLoader();
      let mtlLoader = new MTLLoader();
      let textureLoader = new THREE.TextureLoader();
      // mtlLoader.load(`${that.publicPath}models/zzs.mtl`, function (materials) {
      mtlLoader.load(
        `${that.publicPath}models/zzs.mtl`,
        // `${that.publicPath}models/object.mtl`,
        function (materials) {
          objLoader.setMaterials(materials);
          objLoader.load(
            // `${that.publicPath}models/object.obj`,
            `${that.publicPath}models/zzs.obj`,
            function (obj) {
              obj.scale.multiplyScalar(1);
              obj.traverse(function (child) {
                console.log(child.name,'ddddd')
                if (child instanceof THREE.Mesh) {
                  if (child.name == "Box001") {
                    that.m1 = child;
                  } else if (child.name == "Cylinder002") {
                    that.m2 = child;
                  } else if (child.name == "Cylinder003") {
                    that.m3 = child;
                  }else if (child.name == "Cylinder004") {
                    that.m4 = child;
                  }
                }
              });
              that.cord4.add(that.m4);
              that.cord3.add(that.m3, that.cord4);
              // console.log(999);
              // that.cord3.add(that.m3);
              that.cord2.add(that.m2, that.cord3);
              that.cord1.add(that.m1, that.cord2);
              that.scene.add(that.cord1);
              that.render();
              return obj;
            },
            function (xhr) {
              console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
            },
            function (error) {
              console.log("An error happened");
            }
          );
        }
      );
    },
    //递归  收集  THREE.Mesh
    meshDg(child) {
      child.forEach((childv) => {
        if (childv instanceof THREE.Mesh) {
          //根据需求判断哪些加入objects,也可以在生成object的时候push进objects
          this.objects.push(childv);
        } else if (childv instanceof THREE.Object3D) {
          this.meshDg(childv.children);
        }
      });
    },
    //点击事件
    clickTest(event) {
      console.log(event)
      event.preventDefault();
      this.objects = [];
      // this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      this.mouse.x = (event.layerX / 800) * 2 - 1;
      this.mouse.y = -(event.layerY / 800) * 2 + 1;
      // this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
      this.raycaster.setFromCamera(this.mouse, this.camera);
      this.scene.children.forEach((child) => {
        //根据需求判断哪些加入objects,也可以在生成object的时候push进objects
        if (
          !(child instanceof THREE.GridHelper) &&
          !(child instanceof THREE.DirectionalLight) &&
          !(child instanceof THREE.AmbientLight)
        ) {
          child.children.forEach((childv) => {
            if (childv instanceof THREE.Mesh) {
              //根据需求判断哪些加入objects,也可以在生成object的时候push进objects
              this.objects.push(childv);
            } else if (childv instanceof THREE.Object3D) {
              this.meshDg(childv.children);
            }
          });
        }
      });
      console.log(this.objects, 6666666 + "obj");
      var intersects = this.raycaster.intersectObjects(this.objects);
      console.log(intersects, 8888 + "intersects");

      if (
        intersects.length != 0 &&
        intersects[0].object instanceof THREE.Mesh
      ) {
        this.selectObject = intersects[0].object;
        this.changeMaterial(this.selectObject);
        console.log(this.selectObject.name);
        //这里找的代码,用到了jq,用vue就更简单了
        // $("#alert_center").css({
        //   display: "block",
        // });
        // $("#alert_font").html(
        //   "当前点击的模块名字是:" + this.selectObject.name
        // );
        this.render();
      }
    },
    /**
     * @param {
     * 改变当前对象属性
     * }
     */
    changeMaterial(object) {
      var material = new THREE.MeshLambertMaterial({
        color: 0xffffff * Math.random(),
      });
      object.material = material;
    },
    /**
     * @param {
     * 动画
     * }
     */
    animate() {
      requestAnimationFrame(this.animate);
      this.render();
    },

    /**
     * @param {
     * 渲染
     * }
     */
    render() {
      this.renderer.render(this.scene, this.camera);
    },

    /**
     * @param {
     * 窗口监听函数
     * }
     */
    onWindowResize() {
      this.camera.aspect = window.innerWidth / window.innerHeight;
      this.camera.updateProjectionMatrix();
      // this.renderer.setSize(window.innerWidth, window.innerHeight);
      this.renderer.setSize(800, 800);
    },
  },
  computed: {},
};
</script>
<style lang='scss' scoped>
.spring {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.box {
  width: 100%;
  height: 100px;
}
</style>

你可能感兴趣的:(three,three.js)