three.js Raycaster(鼠标点击选中模型)

效果:

代码:

<template>
  <div>
    <el-container>
      <el-main>
        <div class="box-card-left">
          <div id="threejs" style="border: 1px solid red"></div>
          <div class="box-right" style="text-align: left; padding: 10px">
            <div style="text-align: left">标准设备坐标系:</div>
            <div>
              three.js Canvas
              画布具有一个标准设备坐标系,该坐标系的坐标原点是在canvas画布的中间位置,x轴水平向右,y轴竖直向上,标准设备坐标系的坐标值不是绝对值,
              是相对值,范围是[-1,1],
              也就是说canvas画布上任何一个位置的坐标,如果用标准设备坐标取衡量,那么坐标的所有值都在-1
              和 1之间;
            </div>
            <div style="text-align: left; margin-top: 10px">
              屏幕坐标转为 标准设备坐标:
            </div>
            <div style="text-align: left">
              <pre>
                // 坐标转化公式
                addEventListener('click',function(event){
                    const px = event.offsetX;
                    const py = event.offsetY;
                    //屏幕坐标px、py转标准设备坐标x、y
                    //width、height表示canvas画布宽高度
                    const x = (px / width) * 2 - 1;
                    const y = -(py / height) * 2 + 1;
                })
              </pre>
            </div>
          </div>
        </div>
      </el-main>
    </el-container>
  </div>
</template>
<script>
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
// 效果制作器
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
// 渲染通道
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
// 发光描边OutlinePass
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";

export default {
  data() {
    return {
      name: "",
      scene: null,
      camera: null,
      renderer: null,
      effectComposer: null,
      mesh: null,
      geometry: null,
      group: null,
      material: null,
      texture: null,
      position: null,
      outlinePass: null,
      canvasWidth: 1000,
      canvasHeight: 800,
      color: [],
      meshArr: [],
    };
  },
  created() {},
  mounted() {
    this.name = this.$route.query.name;
    const numbers = Array.from({ length: 255 }, (_, i) => i);
    // const uppercaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
    this.color = [...numbers];
    this.init();
  },
  methods: {
    goBack() {
      this.$router.go(-1);
    },
    addEventListenerFn() {
      // canvas画布添加监听事件
      document.getElementById("threejs").addEventListener("click", (event) => {
        // 1,坐标转换
        const point_x = event.offsetX; // 获取x方向当前点击点距离原点的距离;向右为正(原点是当前元素的左上角点)
        const point_y = event.offsetY; // 获取y方向当前点击点距离原点的距离;向下为正(原点是当前元素的左上角点)
        const x = (point_x / this.canvasWidth) * 2 - 1;
        const y = -(point_y / this.canvasHeight) * 2 + 1;
        // 2,创建射线投射器对象,
        const raycaster = new this.$three.Raycaster();
        // 3,射线计算,(参数是鼠标点击位置,相机参数)
        raycaster.setFromCamera(new this.$three.Vector2(x, y), this.camera);
        // 4,射线交叉计算
        // const mesh_arr = []
        // if(!this.scene)return;
        // this.scene.traverse(m => {
        //   console.log('m',m);
        //   if(m.isMesh != undefined && m.isMesh) {
        //     mesh_arr.push(m);
        //   }
        // })
        const intersects = raycaster.intersectObjects(this.meshArr);
        if (intersects.length > 0) {
          if (this.effectComposer) {
            // intersects[0].object.material.color.set(0xff0000);
            this.outlinePass.selectedObjects = [intersects[0].object];
            this.renderFun();
          }
        }
      });
    },
    // 随机颜色
    randomColor() {
      // 要生成min-max之间的随机数,公式为:Math.random()*(max-min+1)+min
      let i = Math.floor(Math.random() * (this.color.length - 0 + 1) + 0);
      let j = Math.floor(Math.random() * (this.color.length - 0 + 1) + 0);
      let k = Math.floor(Math.random() * (this.color.length - 0 + 1) + 0);
      this.position = new this.$three.Vector3(i, j, k);
      return new this.$three.Color(
        "rgb(" +
          this.color[i] +
          ", " +
          this.color[j] +
          ", " +
          this.color[k] +
          ")"
      );
    },
    init() {
      // 创建场景对象
      this.scene = new this.$three.Scene();
      // 创建立方缓存几何体对象
      for (let i = 0; i < 3; i++) {
        this.boxGeometry();
      }
      // 创建辅助坐标轴对象
      const axesHelper = new this.$three.AxesHelper(200);
      this.scene.add(axesHelper);

      // 创建正交投影相机对象
      // this.camera = new this.$three.PerspectiveCamera(60,1,0.01,1000);
      // this.camera.position.set(600,600,600);
      // this.camera.lookAt(0,0,0);

      // 创建透视投影相机对象
      this.camera = new this.$three.OrthographicCamera(
        -500,
        500,
        400,
        -400,
        0,
        1000
      );
      this.camera.position.set(200, 200, 200);
      this.camera.lookAt(0, 0, 0);
      // const helper = new this.$three.CameraHelper( this.camera );
      // this.scene.add( helper );
      // 创建渲染器对象
      this.renderer = new this.$three.WebGLRenderer();
      this.renderer.setSize(this.canvasWidth, this.canvasHeight);
      this.renderer.render(this.scene, this.camera);
      window.document
        .getElementById("threejs")
        .appendChild(this.renderer.domElement);
      // 创建相机空间轨道控制器
      const controls = new OrbitControls(this.camera, this.renderer.domElement);
      controls.addEventListener("change", () => {
        // this.renderer.render(this.scene, this.camera);
        this.effectComposer.render();
      });
      // 调用后处理方法
      this.effectComposerFn();
      // 调用 监听  点击事件的方法
      this.addEventListenerFn();
    },
    // 创建盒模型的方法
    boxGeometry() {
      // 创建网格基础材质对象
      const material = new this.$three.MeshBasicMaterial({
        color: this.randomColor(),
      });
      // 创建立方几何体对象
      const geometry = new this.$three.BoxGeometry(
        this.position.x,
        this.position.y,
        this.position.z
      );
      // 创建网格模型对象
      const mesh = new this.$three.Mesh(geometry, material);
      if (this.position) {
        // 网格模型设置位置
        mesh.position.set(this.position.x, this.position.y, this.position.z);
      }
      this.meshArr.push(mesh);
      this.scene.add(mesh);
    },
    // 后处理方法
    effectComposerFn() {
      // 创建后处理对象
      this.effectComposer = new EffectComposer(this.renderer);
      // 创建后处理渲染器通道对象
      const renderPass = new RenderPass(this.scene, this.camera);
      // 后处理对象 添加渲染器通道
      this.effectComposer.addPass(renderPass);
      // 创建发光描边对象
      this.outlinePass = new OutlinePass(
        new this.$three.Vector2(this.canvasWidth, this.canvasHeight),
        this.scene,
        this.camera
      );
      // 设置发光描边颜色
      this.outlinePass.visibleEdgeColor.set(0xE0F409);
      // 设置发光描边厚度
      this.outlinePass.edgeThickness = 5;
      // 描边亮度
      this.outlinePass.edgeStrength = 20;
      // 设置发光描边频率
      this.outlinePass.pulsePeriod = 2;
      
      this.effectComposer.addPass(this.outlinePass);
    },
    renderFun() {
      // 调用后处理对象的render方法进行渲染,
      this.effectComposer.render();
      window.requestAnimationFrame(this.renderFun);
    },
  },
};
</script>
//
<style lang="less" scoped>
.box-card-left {
  display: flex;
  align-items: flex-start;
  flex-direction: row;

  width: 100%;

  .box-right {
  }
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/291361.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

集合学习笔记(二)

文章目录 1.请介绍TreeMap的底层原理2. Map和Set有什么区别&#xff1f;3. List和Set有什么区别&#xff1f;4 .ArrayList和LinkedList有什么区别&#xff1f;5. 有哪些线程安全的List&#xff1f;6 .介绍一下ArrayList的数据结构&#xff1f;7. 谈谈CopyOnWriteArrayList的原理…

低代码:实现数据可视化的强大助手

随着数据在企业中的价值越来越受到重视&#xff0c;数据可视化成为了决策者和业务专家们必备的工具。然而&#xff0c;传统的数据可视化开发过程常常繁琐且耗时&#xff0c;限制了其在应用中的广泛应用。低代码平台的出现&#xff0c;为实现高效的数据可视化提供了新的解决方案…

使用Matplotlib绘制模拟上海城市气温变化图

模拟上海气温变化折线图 实现步骤 准备数据创建画布绘制图像显示图像 基本实现 示例代码&#xff1a; import matplotlib.pyplot as plt import random# 准备数据 x range(60) y_shanghai [random.uniform(15,18) for _ in x]# 创建画布 plt.figure(figure(20,8), dpi10…

位运算 (运算符)

文章目录 位运算位运算概述位运算概览& 按位与&#xff08;AND&#xff09;| 按位或&#xff08;bitwise OR&#xff09;^ 按位异或&#xff08;bitwise XOR&#xff09;~ 按位非&#xff08;bitwise NOT&#xff09;<< 左移&#xff08;bitwise shift left&#xff…

深入探究:使用大型AI模型的实战指南

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 在今天的技术领域&#xff0c;大型AI模型已成为…

windows+django+nginx部署静态资源文件

平台&#xff1a;windows python&#xff1a;3.10.0 django&#xff1a;4.0.8 nginx&#xff1a;1.24.0 背景 开发阶段采用前后端分离模式&#xff0c;现在要将项目部署到工控机上&#xff0c;把前端项目编译出来的静态文件放到后端项目中进行一体化部署&#xff0c;且不修改…

告别复杂排版:Markdown语法指南

导语&#xff1a;Markdown作为一种轻量级的标记语言&#xff0c;以其简洁、易学的语法和强大的兼容性赢得了广泛的应用。本文将为您详细介绍Markdown的起源、基本语法及其在写作、博客、项目管理等场景的应用&#xff0c;带您领略这一简洁高效的文本编写工具的无穷魅力。 Mark…

「HarmonyOS」验证码多TextInput输入框焦点自动跳转问题

需求背景&#xff1a;需要做一个多输入框的验证码模块&#xff0c;输入验证码时输入后光标会自动跳转至下一个输入框&#xff0c;删除验证码时会自动删除上一个输入框内容&#xff0c;并且光标跳转至上一个输入框内。6位验证码全部输完后进行登录请求 具体样式如下图&#xff1…

前端angular 实现验证码 输入+展示(大框+加粗内容 )

参考用原生方在手机上此效果 如何实现一个4位验证码输入框效果 输入使用的任旧是html的input元素&#xff0c;只是让它看不到了只是把输入到input元素里的内容取到的内容放在改过样式的div里不需要dom操作&#xff0c;直接用双向绑定就拿到数据&#xff1b;使用动态样式 设置了…

全球大气二氧化碳浓度2°x2.5°栅格模拟数据集(1992-2020)

全球大气二氧化碳浓度2x2.5栅格模拟数据集&#xff08;1992-2020&#xff09; 全球大气二氧化碳浓度2x2.5栅格模拟数据集&#xff08;1992-2020&#xff09;是以2002-2012年全球对流层CO2浓度卫星遥感产品&#xff08;AIRS&#xff0c;AIRx3C2M 005&#xff09;为基础&#xff…

中国FinOps现状调查报告(2023)》亮点解读

今年以来&#xff0c;我们举办了多期FinOps的专题分享&#xff0c;邀请了美图、腾讯、B站、趣丸、知乎等厂商和行业专家&#xff0c;分享他们在FinOps领域的经验。我们也发现越来越多的人对FinOps产生了浓厚的兴趣&#xff0c;而且FinOps的成熟度也在逐渐提升。 降本增效&…

前端如何捕获网络问题导致的接口失败 net::ERR_NETWORK_IO_SUSPENDED

上传大文件时电脑休眠、页面静置导致接口报网络错误&#xff0c;上传失败 最近遇到了上传文件遇到网络波动、超时、网络中断情况下需要重传的需求刚开始排查只能在控制台看到报错net::ERR_NETWORK_IO_SUSPENDED&#xff0c;一头雾水。后加上catch捕获异常进行判断 fetch(/upl…

uniapp中uview组件库丰富的Slider 滑动选择器的使用方法

目录 #平台差异说明 #基本使用 #设置最大和最小值 #设置步进值 #禁用状态 #自定义按钮的内容和样式 #自定义滑动选择器整体的样式 #此页面源代码地址 #API #Props #Slider Events 该组件一般用于表单中&#xff0c;手动选择一个区间范围的场景。 说明 该组件在H5&…

如何制作可预约的上门维修服务小程序?

上门维修服务已经成为人们日常生活中不可或缺的一部分。为了满足这一需求&#xff0c;我们学习如何无经验自己制作上门维修服务小程序。 首先&#xff0c;打开乔拓云-门店系统的后台&#xff0c;可以看到有很多各行各业的模版。这些模版涵盖了各种行业&#xff0c;包括家电维修…

竞赛保研 基于机器视觉的行人口罩佩戴检测

简介 2020新冠爆发以来&#xff0c;疫情牵动着全国人民的心&#xff0c;一线医护工作者在最前线抗击疫情的同时&#xff0c;我们也可以看到很多科技行业和人工智能领域的从业者&#xff0c;也在贡献着他们的力量。近些天来&#xff0c;旷视、商汤、海康、百度都多家科技公司研…

Js的$如同 sed的 java的$0 指代matcher匹配到的内容的符号

Js的$&如同 sed的& java的$0 $& 可用于Js,Vscode,RJTextEd,editplus,notepad, 在 Vscode,RJTextEd,editplus,notepad,等的替换中, 启用正则, 就能使 $&生效, 比如 ($&)表示给匹配到的内容加上括号 $& 可用于Js的String的replace(和replaceAll( 的第二…

《软件方法(下)》8.2.4-8.2.5 类和属性的命名,审查类和属性(202401更新)

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 8.2 建模步骤C-1 识别类和属性 8.2.4 类和属性的命名 8.2.4.2 关于DDD话语中的“通用语言” DDD&#xff08;领域驱动设计&#xff09;话语中有“通用语言&#xff08;Ubiquitous L…

DBeaver配置达梦数据库连接

随着信创逐渐推广&#xff0c;达梦数据库也成为流行。下面展示如何使用dbeaver配置达梦数据库连接 1 驱动新建 菜单&#xff0c;数据库->驱动管理器 2 驱动信息填写 选择新建之后&#xff0c;弹出一个填写页面 需要填写的几个关键信息&#xff1a; 驱动名称&#xff1a;…

迈向更高质量的深度估计

题目&#xff1a; Towards High Quality Depoth Estimation 摘要 目前的深度估计从业人员大多follow the settings of specific backbone without thinking about why is that。本文将详细探索从数据&#xff08;不同类型数据集加载、稳定性、预处理、数据生成&#xff09;&a…

从工程和科学问题到实际解决方案——《Python应用数值方法——解决工程和科学问题》

内容简介 《Python应用数值方法——解决工程和科学问题》是为想要学习和应用数值方法来解决工程和科学问题的学生撰写的。书中提供了足够丰富的理论知识。如果读过本书的姊妹篇《工程与科学数值方法的MATLAB实现(第4版)》&#xff0c;就会发现过渡到Python程序是无缝的&#x…