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

2023-11-19

 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>

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • g2o的基本使用

    参考 https www jianshu com p e16ffb5b265d 参考 https zhuanlan zhihu com p 36889150 图是一种数据结构 在图优化中 用顶点 vertex 表示优化变量 用边 edge
  • Smali--Dalvik虚拟机指令语言-->【android_smali语法学习一】

    最近一周在研究rom移植 所以就对Smali语言学习了一下 Smali语言其实就是Davlik的寄存器语言 Smali语言就是android的应用程序 apk通过apktool反编译出来的都有一个smali文件夹 里面都是以 smali结尾
  • 基于当前系统制作docker镜像

    1 通过tar 备份目录 tar cvpf home buildrpm tar directory exclude proc exclude sys exclude dev exclude run root localhost home l
  • 不能安装64位office提示已安装32位的

    问题描述 安装64位office办公软件的时候提示已经安装32位的office办公软件所以无法继续安装 但实际上之前安装的32位的office办公软件已经卸载了 问题现象截图如下 解决办法 从问题描述中 我们其实已经能够看出问题原因了 类似
  • React的超详细讲解

    React React的重点 webpack webpack 是一个现代 JavaScript 应用程序的静态模块打包器 module bundler 当 webpack 处理应用程序时 它会递归地构建一个依赖关系图 dependency
  • 虚幻引擎程序化资源生成框架PCG 之 UPCGBlueprintElement源码笔记(一)

    UPCGBlueprintElement是PCGGraph中自定义节点的基类 但官方目前还没有给出详细的文档 所以从源代码里找点答案 文章目录 可覆盖函数 Override Functions Excute 和 Excute with Co
  • web.xml加载顺序

    web xml加载顺序 应用服务器启动时web xml加载过程 至于这些节点在xml文件中的前后顺序没有关系 不过有些应用服务器 我曾碰到过的 websphere就严格要求web xml的节点顺序 否则部署不成功 所以还是赞成按照web x
  • CSS 样式穿透

    1 穿透语法 如果给样式中声明scop 那么页面的类会增加data 562e3wue8等属性 此时就需要用到 gt gt gt 或者 deep 或者 v deep 以后用后两个比较多 如果在样式类前面 v deep 则会自动找到 data
  • 快速搭建一个自己的服务器详解(java环境)

    一 服务器的购买 1 我选择的是阿里云的服务器 学生价9 5元一个月 百度直接搜索阿里云 然后点击右上角登录 推荐大家用支付宝扫码登录 方便快捷 阿里云官网的东西比较多 登录后我找了很久也没有找到学生服务器在哪里卖 最后在咨询里找到了这个网
  • 喜爱夜蒲3_百度百科

    喜爱夜蒲3 百度百科 喜爱夜蒲3
  • LeetCode707.设计链表

    题目描述 707 设计链表 力扣 LeetCode 这道题用单向链表和双向链表都能写 因为对双向链表不是很熟 所以就用双向链表来写了 由于初始化的链表并没有放入任何元素 所以这个结点就当成哑结点来处理 而且这样对之后的增删操作也更便利 C语
  • 通配符的使用

    通配符是一种特殊语句 主要有星号 和问号 用来模糊搜索文件 当查找文件夹时 可以使用它来代替一个或多个真正字符 当不知道真正字符或者懒得输入完整名字时 常常使用通配符代替一个或多个真正的字符 SQL 通配符 在搜索数据库中的数据时 SQL
  • java最大线程数

    Xms 初始堆大小 如 Xms256m Xmx 最大堆大小 如 Xmx512m Xmn 新生代大小 通常为 Xmx 的 1 3 或 1 4 新生代 Eden 2 个 Survivor 空间 实际可用空间为 Eden 1 个 Survivor
  • 华为OD机试 - 经典屏保(Java)

    题目描述 DVD机在视频输出时 为了保护电视显像管 在待机状态会显示 屏保动画 如下图所示 DVD Logo在屏幕内来回运动 碰到边缘会反弹 请根据如下要求 实现屏保Logo坐标的计算算法 屏幕是一个800 600像素的矩形 规定屏幕的左上
  • PermissionError: [WinError 5] 拒绝访问。解决办法

    PermissionError WinError 5 拒绝访问 问题描述 解决办法 一 找到programdata下anaconda3的python 二 进入Users的权限 将除了特殊权限外 全部挂上 三 在返回到anaconda3的目录
  • Nginx-简介

    用Python语言开发的站点使用的Web服务器主要有Nginx Apache以及淘宝的Tengine Nginx是由Igor Sysoev在2004年发布的一个开源 高性能的HTTP服务器和反向代理 它还可以用来作为IMAP POP3的代理
  • K8s基础6——应用配置管理方案、调度策略、污点和污点容忍

    文章目录 一 应用配置管理方案 1 1 ConfigMap 1 1 1 注入变量 1 1 2 挂载数据卷 1 2 Secret 1 2 1 数据卷挂载 1 2 2 变量注入 二 调度策略 2 1 nodeSelector定向调度 2 1 1
  • Github上 10 个开源免费且优秀的后台控制面板

    Web 开发中几乎的平台都需要一个后台管理 但是从零开发一套后台控制面板并不容易 幸运的是有很多开源免费的后台控制面板可以给开发者使用 那么有哪些优秀的开源免费的控制面板呢 我在 Github 上收集了一些优秀的后台控制面板 并总结得出 T
  • HTTP协议(下)

    前面我介绍了什么是HTTP协议 以及HTTP的协议格式 接下来我介绍一下如何使用代码构造出HTTP请求 以及在介绍一下HTTPS 目录 一 通过代码构造HTTP请求 1 基于HTML JS 1 1 基于form表单 1 2 基于ajax 2
  • vue + threejs 给3D模型添加label标签(dom的方式)

    webGL js封装的代码 const THREE window THREE webGL对象配置 export const webglOBJ renderDom null Scene null 场景 camera null 摄像头 rend