vue + threejs 给3D模型添加label标签(dom的方式)

 webGL.js封装的代码。

const THREE = window.THREE;
// webGL对象配置
export const webglOBJ = {
  renderDom: null,
  Scene: null, // 场景 
  camera: null, // 摄像头
  renderer: null, // 渲染器
  senceAdd (objList = []) {
    objList.forEach(v => {
      webglOBJ.Scene.add(v);
    });
  },
  // 创建场景
  createSence (renderDom) {
    this.renderDom = renderDom;
    webglOBJ.Scene = new THREE.Scene();
    return webglOBJ.Scene;
  },
  // 创建摄像机
  createCamera ({innerWidth, innerHeight, position} = {}) {
    const { width, height } = this.renderDom.getBoundingClientRect();
    let camera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
    camera.position.x = -50;
    camera.position.y = 30;
    camera.position.z = 50;
    camera.lookAt(webglOBJ.Scene.position); // 视角
    webglOBJ.camera = camera; // 视角
    return webglOBJ.camera;
  },
  createRenderer () {
    let renderer = new THREE.WebGLRenderer();
    const {width, height} = this.renderDom.getBoundingClientRect();
    renderer.setSize(width, height);
    renderer.setClearColor(new THREE.Color(0xcccccc));
    renderer.shadowMap.enabled = true;
    this.renderDom.appendChild(renderer.domElement);
    webglOBJ.renderer = renderer;
    return webglOBJ.renderer;
  },
  createPlane (textureLoaderUrl, textureNormalUrl) {
    let planeGeometry = new THREE.PlaneGeometry(60, 60, 1, 1); // 平面网格
    let textureLoader = new THREE.TextureLoader();
    let texture = textureLoader.load(textureLoaderUrl);
    let textureNormal = textureLoader.load(textureNormalUrl);
    // 加载高光贴图
    let planeMaterial = new THREE.MeshPhongMaterial({
      // specular: 0xff0000,//高光部分的颜色
      shininess: 30, //高光部分的亮度,默认30
      map: texture, // 普通纹理贴图
      roughness: 0.3,
      lightMap: textureNormal,
      // normalMap: textureNormal, //法线贴图
      bumpScale: 3
    }); // 材质对象Material
    // let planeMaterial = new THREE.MeshLambertMaterial({color: 0xcccccc});
    let plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.rotation.x = -0.5 * Math.PI;
    plane.position.x = 0;
    plane.name = '平面物体ID=' + 1;
    plane.position.y = 0;
    plane.position.z = 0;
    plane.receiveShadow = true;
    return plane;
  },
  createBoxGeometry (textureLoaderUrl, {x, y, z}) {
    // 创建立方体
    let textureLoader = new THREE.TextureLoader();
    let textureNormal = textureLoader.load(textureLoaderUrl);
    let boxGeometry = new THREE.BoxGeometry(10, 10, 10, 200);
    let texture1 = textureLoader.load(textureLoaderUrl);
    let boxGeometryMaterial = new THREE.MeshLambertMaterial({
      // specular: 0xff0000,//高光部分的颜色
      shininess: 10, //高光部分的亮度,默认30
      normalScale: new THREE.Vector2(2.2, 2.2),
      map: texture1, // 普通纹理贴图
      normalMap: textureNormal, //法线贴图
      bumpMap: textureNormal,
      bumpScale: 0.3
    });
    let box = new THREE.Mesh(boxGeometry, boxGeometryMaterial);
    box.name = '正方物体ID=' + 2;
    box.position.x = x;
    box.position.y = y;
    box.position.z = z;
    box.castShadow = true;
    return box;
  },
  // 点光源
  createSpotLight () {
    // 点光源
    let spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-60, 40, -20);
    spotLight.castShadow = true;
    return spotLight;
  },
  // 平行光
  createDirectionalLight (target) {
    // 平行光
    let directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    // 设置光源的方向:通过光源position属性和目标指向对象的position属性计算
    directionalLight.position.set(-90, 80, -20);
    // 方向光指向对象网格模型mesh2,可以不设置,默认的位置是0,0,0
    // directionalLight.target = target;
    return directionalLight;
  },
  // 环境光
  createAmbient (color = 0x444444) {
    let ambient = new THREE.AmbientLight(color);
    // ambient.castShadow = true;
    return ambient;
  },
  createDatGui () {
    let gui = {
      bump: 0.03,
      animation: false,
    };
    let datGui = new dat.GUI();
    //将设置属性添加到gui当中,gui.add(对象,属性,最小值,最大值)
    datGui.add(gui, "bump", -1, 1).onChange(function (e) {
      box.material.bumpScale = e;
    });
    datGui.add(gui, "animation");
    return datGui;
  },
  // 创建控制轴
  createControls () {
    let controls = new THREE.OrbitControls(webglOBJ.camera, webglOBJ.renderDom);
    return controls;
  },
  // 创建帮助
  createAxisHelper () {
    let axisHelper = new THREE.AxisHelper(250);
    return axisHelper;
  },
  // 初始化webGL对象
  webglRender (Scene, camera) {
    webglOBJ.renderer.render(Scene, camera);
    window.requestAnimationFrame(webglOBJ.webglRender);
  }
};

/**
 * 添加标签:dom方式
 * @param {*} targePosition :需要传递当前标签的位置
 * @param {*} targetId :标签对应的dom的唯一ID,暂且用时间戳代替,避免重复
 * @param {*} innerHTML :标签对应html
 */
export function labelTag (camera, targePosition, targetId, innerHTML, webGLdom) {
  const { width, height } = webGLdom.getBoundingClientRect();
  let worldVector = new THREE.Vector3(targePosition.x, targePosition.y, targePosition.z);
  let vector = worldVector.project(camera);
  let halfWidth = width / 2,
    halfHeight = height / 2;
  let x = Math.round(vector.x * halfWidth + halfWidth);
  let y = Math.round(-vector.y * halfHeight + halfHeight);
  /**
   * 更新立方体元素位置
   */
  let div = document.getElementById(targetId);
  div.style.left = x + 'px';
  div.style.top = y + 'px';
  // div.innerHTML = `uuid:${innerHTML.uuid}`;
}

vue中引用: 

<template>
<div>
  <div class="three-box_wrapper"></div>
  <div :id="`sign${idx + 1}`" style="position: absolute;" v-for="(v, idx) in labels" :key="idx">
    <div class="sign" :uuid="v.uuid">
      <div class="name">我是标签1</div>
      <div class="data">数据: {{v.uuid}}</div>
    </div>
  </div>
</div>
</template>

<script>
import {webglOBJ, labelTag} from '@/utils/webGL/webGL.js';
export default {
  name: 'threeBox',
  data () {
    return {
      labels: []
    };
  },
  mounted () {
    this.int();
  },
  methods: {
    createLabel () {

    },
    int () {
      const imgBG = require('./img.jpg');
      const webGLdom = document.querySelector('.three-box_wrapper');
      const sence = webglOBJ.createSence(webGLdom);
      const camera = webglOBJ.createCamera();
      const renderer = webglOBJ.createRenderer();
      const plane = webglOBJ.createPlane(imgBG, imgBG);
      const boxGeometry = webglOBJ.createBoxGeometry(imgBG, {x: 10, y: 5, z: 10});
      const boxGeometry1 = webglOBJ.createBoxGeometry(imgBG, {x: -10, y: 5, z: 10});
      const spotLight = webglOBJ.createSpotLight();
      const directionalLight = webglOBJ.createDirectionalLight(boxGeometry);
      const ambient = webglOBJ.createAmbient();
      const datGui = webglOBJ.createDatGui();
      const controls = webglOBJ.createControls();
      const axisHelper = webglOBJ.createAxisHelper();
      // 将对象添加到场景中去
      webglOBJ.senceAdd([plane, boxGeometry, boxGeometry1, spotLight, directionalLight, ambient, datGui, controls, axisHelper]);
      // webglOBJ.webglRender(sence, camera, renderer);
      console.log(sence, 'sence');
      const vm = this;
      function render(html) {
        vm.labels = sence.children.filter(v => v.type == 'Mesh');
        vm.$nextTick(() => {
          sence.children.forEach((val, idx) => {
            if (val.type == 'Mesh') {
              const {x, y, z} = val.position;
              labelTag(camera, {x,  y, z}, `sign${idx + 1}`, val, webGLdom);
            }
          });
        });

        renderer.render(sence, camera);
        requestAnimationFrame(render);
      };
      render();

      // 监听点击事件查看点击的元素
      let raycaster = new THREE.Raycaster();
      let mouse = new THREE.Vector2();
      // 点击了哪个模型
      function clickEvent() {
        if (event.target.tagName == 'CANVAS') {
          mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
          mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
          sence.updateMatrixWorld(true);
          // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
          raycaster.setFromCamera(mouse, camera);
          // 获取raycaster直线和所有模型相交的数组集合
          let intersects = raycaster.intersectObjects(sence.children, true);
          if (intersects[0]) {
            console.log(intersects[0]);
          }
        }
      }
      window.addEventListener('click', clickEvent, false);
    }
  }
};
</script>

<style lang="scss" scoped>
  .three-box_wrapper {
    position: relative;
    width: 100%;
    height: 800px;
    border: 1px solid #ccc;
  }
  div[id *= "sign"] {
    width: 250px;
    height: 100px;
    background: rgba(0, 0, 0, .65);
    .sign{
      div {
        color: #fff;
        text-align: left;
        padding: 0 5px;
      }
    }
  }
</style>

  • 3
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值