three.js+WebGL踩坑经验合集(4.1):THREE.Line2的射线检测问题(注意本篇说的是Line2,同样也不是阈值方面的问题)

上篇大家消化得如何了?

笔者说过,1级编号不同的两篇博文相对独立,所以这里笔者还是先给出完整代码,哪怕跟(3)没有太大区别。

这里我们把线的粗细调成5(排除难选中的因素),同时去掉参照物圆柱体。

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>threeLine2_raycaster</title><style>body {margin: 0;overflow: hidden;}</style><script src="three/build/three.js"></script><script src="three/examples/js/controls/OrbitControls.js"></script><script src="three/examples/js/lines/LineSegmentsGeometry.js"></script><script src="three/examples/js/lines/LineGeometry.js"></script><script src="three/examples/js/lines/LineMaterial.js"></script><script src="three/examples/js/lines/LineSegments2.js"></script><script src="three/examples/js/lines/Line2.js"></script>
</head><body><script>var scene = new THREE.Scene();var srcColor = new THREE.Color(1, 1, 1);var overColor = new THREE.Color(0.8, 0.3, 0);var geometry = new THREE.LineGeometry();geometry.setPositions([0, 0, 600, 0, 0, -600]);var material = new THREE.LineMaterial({linewidth: 5, color: 0xFFFFFF, resolution: new THREE.Vector2(window.innerWidth, window.innerHeight)});    var line = new THREE.Line2(geometry, material);scene.add(line);var width = window.innerWidth; var height = window.innerHeight; var camera = new THREE.PerspectiveCamera(60, 1, 0.1, 20000);camera.position.set(10, 10, 620);  camera.lookAt(0, 0, 0);var renderer = new THREE.WebGLRenderer();renderer.setSize(width, height);renderer.setClearColor(0x000000, 1); document.body.appendChild(renderer.domElement); function render() {renderer.render(scene, camera);requestAnimationFrame(render);}render();var controls = new THREE.OrbitControls(camera,renderer.domElement);controls.addEventListener('change', render);// var axisHelper = new THREE.AxesHelper(250);// scene.add(axisHelper);	var raycaster = new THREE.Raycaster(); function onMouseMove(e){	//这里是屏幕坐标到ndc的转换,不懂的可以自行上webgl中文网学习var x = ((e.clientX - width * 0.5) / width * 2);var y = (-(e.clientY - height * 0.5) / height * 2);raycaster.setFromCamera(new THREE.Vector2(x, y), camera);material.color = srcColor;//把scene换成line,确保新增的圆柱体不参与射线检测var intersects = raycaster.intersectObject(line, true);      for(let intersect of intersects){intersect.object.material.color = overColor;       }}window.addEventListener("mousemove", onMouseMove);</script>
</body>
</html>

运行效果如下,按照代码设定的线条位置和相机参数,它可以工作得很好,但是一旦用鼠标滚轮把镜头拉近,线就会莫名选不中了,只有当整根线都在屏幕范围内的时候,射线检测才能正常。

在排查问题之前,笔者先给大家简单介绍一下THREE.Line2的实现原理。核心逻辑在LineMaterial.js的shader里面,小伙伴们可以对照着代码进行理解。

1 以一段线为单位,取其start和end。

2 对start和end应用世界矩阵,相机矩阵和投影矩阵后,转换到WebGL使用的NDC坐标系上。此过程跟笔者前面的一篇博文中提到的THREE.Vector3.project方法有共通之处。three.js+WebGL踩坑经验合集(2):3D场景被相机裁切后,被裁切的部分依然可以被鼠标碰撞检测得到(射线检测)-CSDN博客

这一步的关键词为MVP矩阵,不懂的小伙伴可以自行搜索学习。

3 在步骤2的基础上,分别过S和E作垂直于SE(代码中为dir)的法线,共4条,如下图所示。

4 然后对法线向量进行缩放,使其长度等于linewidth的一半,这个法线向量在代码中对应offset属性。

此处为shader代码,需要把linewidth转换到ndc坐标系,但是shader在GPU层是无法直接获取到画布大小的,所以才有了LineMaterial的resolution属性,它是屏幕坐标转ndc坐标的条件之一。

5 取出上图中的ABCD点生成两个三角面。

6 此时若直接填充纯色,那么端点处就会不太光滑,所以后面还有一段代码,通过判断当前像素点到端点的距离来生成圆角。

笔者跳过了LineMaterial中的trimSegment代码,它虽然不是实现原理的主体部分,但是跟选不中的bug却有着密不可分的关系。

现在我们来看看Line2的射线检测代码,它的实现在父类LineSegments2上。

不难看出,射线检测的实现代码跟LineMaterial是配套编写的,过程极其相似,但是当时笔者并没有看懂LineMaterial上的trimSegment到底是干嘛用,就在那儿盲目断点了一把,这个过程分享出来没有任何意义,所以就直接给研究结果。

因为射线检测出错是发生在线条超出屏幕的那一刹那,所以我们看看端点在出界前后转换到NDC坐标系的结果。

这个状态下,在控制台输入

new THREE.Vector3(0, 0, 600).project(camera),得到的结果为

这个点是左侧端点,看百分比没有啥毛病

我们用滚轮拉近一下镜头再输出一下

嗯-0.82,基本对的上。

再滚轮一下,这个端点会穿出屏幕,结果如下

嗯,这下有毛病了,端点在左下角,应该是负一点多才是对的,然而变成了正数,飞到了另一侧去。

至此,我们发现project方法的一个大坑,对于超出可视范围的点,其计算结果并不可靠。

对于显示来说,这个bug太明显了,所以LineMaterial类加了trimSegment在边界处进行了修正。但是射线检测那个地方,大概没有人仔细地测试过,因此就留下了这样的bug。

笔者用跟trimSegment类似的方法进行修正

结果是对了,但是笔者在写这段代码的时候,实际上是还没看懂trimSegment的原理,所以用的变量是w而非z。但不管用的哪个变量,这背后隐藏着的,是齐次坐标以及透视相机矩阵公式等内容,比较复杂,所以笔者打算放到下一篇再写。因为即使笔者把trimSegment抄过来,也是没有解释为什么出界了之后,project的计算会有问题,还会再解释为什么trimSegment只需要修复z而不需要修复xy。

进入下一篇之前,还是先小结一下

1 THREE.Vector3的project方法在出界之后,结果可能会不正确

2 为确保界内部分的结果正确,我们应该把出界的部分裁剪掉,让线段的计算由始至终都在可靠的范围内进行

3 THREE.Line2的显示部分做了裁剪,但是射线检测没有,从而导致射线检测结果出错

好了,时间不早了,今天先到这里,希望明天还有时间来给大家继续分享,晚安喽~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

doris:JSON导入数据

本文介绍如何在 Doris 中导入 JSON 格式的数据文件。Doris 支持导入标准 JSON 格式数据&#xff0c;通过配置相关参数&#xff0c;可以灵活地处理不同的 JSON 数据结构&#xff0c;并支持从 JSON 数据中抽取字段、处理嵌套结构等场景。 导入方式​ 以下导入方式支持 JSON 格式…

信息收集 CTF 1 挑战通关指南

大家好&#xff01;今天我想和大家分享 Information Gathering CTF 1 挑战的完整攻略。我将解释我是如何逐步攻克每一个 flag&#xff0c;并使用了哪些工具。放心&#xff0c;我不会直接给出 flag&#xff0c;因为学习的目的不是直接提交答案&#xff0c;而是掌握解决问题的方法…

PHP防伪溯源一体化管理系统小程序

&#x1f50d; 防伪溯源一体化管理系统&#xff0c;品质之光&#xff0c;根源之锁 &#x1f680; 引领防伪技术革命&#xff0c;重塑品牌信任基石 我们自豪地站在防伪技术的前沿&#xff0c;为您呈现基于ThinkPHP和Uniapp精心锻造的多平台&#xff08;微信小程序、H5网页&…

使用MQTT.fx向阿里云物理网平台上报物理模型数据

MQTT向阿里云物理网平台上报物理模型数据 一、前言二、测试三、结束语 一、前言 上一篇文章介绍了阿里云物联网平台的基本使用&#xff08;文章入口&#xff09;&#xff0c;本篇博客接着来讲&#xff0c;主要介绍如何使用MQTT连接测试工具向阿里云物联网平台上报物理模型数据。…

【阅读笔记】基于图像灰度梯度最大值累加的清晰度评价算子

本文介绍的是一种新的清晰度评价算子&#xff0c;基于图像灰度梯度最大值累加 一、概述 目前在数字图像清晰度评价函数中常用的评价函数包括三类&#xff1a;灰度梯度评价函数、频域函数和统计学函数&#xff0c;其中灰度梯度评价函数具有计算简单&#xff0c;评价效果好等优…

数据库底层存储的基本逻辑

1. 数据库、表空间、磁盘是多对多的关系 一个表空间可以分散到多个磁盘上&#xff0c;一个磁盘上也可以有多个表空间&#xff1b; 一个数据库可以分散存储在多个磁盘上&#xff0c;一个磁盘也可以包含多个数据库。 2. 一个表只能属于一个库&#xff0c;也只能对应一个表空间…

80,【4】BUUCTF WEB [SUCTF 2018]MultiSQL

53&#xff0c;【3】BUUCTF WEB october 2019 Twice SQLinjection-CSDN博客 上面这个链接是我第一次接触二次注入 这道题也涉及了 对二次注入不熟悉的可以看看 BUUCTF出了点问题&#xff0c;打不开&#xff0c;以下面这两篇wp作为学习对象 [SUCTF 2018]MultiSQL-CSDN博客 …

docker-registry

安装依赖 apt install apache2-utils设置密码 htpasswd -Bbn 用户名 密码 >/data/registry_hub/passwd#docker私服部署 docker run -d -p 5000:5000 --name docker-registry -v /data/registry_hub/:/var/lib/registry -v /data/registry_hub/passwd:/auth/htpasswd \ -e …

领域驱动设计(DDD)Spring Boot 3 实现 二

使用 Spring Boot 3 实现领域驱动设计&#xff08;DDD&#xff09;是一种很自然的选择&#xff0c;因为 Spring 提供了良好的生态支持&#xff0c;特别是在分层架构、依赖管理、事件驱动等方面。以下是如何在 Spring Boot 3 中结合 DDD 进行开发的详细指南&#xff1a; 项目结构…

FFmpeg 头文件完美翻译之 libavcodec 模块

前言 众所周知&#xff0c;FFmpeg 的代码开发上手难度较高&#xff0c;源于官方提供的文档很少有包含代码教程相关的。要想熟练掌握 FFmpeg 的代码库开发&#xff0c;需要借助它的头文件&#xff0c;FFmpeg 把很多代码库教程都写在头文件里面。因此&#xff0c;熟读头文件的内…

redis实现lamp架构缓存

redis服务器环境下mysql实现lamp架构缓存 ip角色环境192.168.242.49缓存服务器Redis2.2.7192.168.242.50mysql服务器mysql192.168.242.51web端php ***默认已安装好redis&#xff0c;mysql 三台服务器时间同步&#xff08;非常重要&#xff09; # 下载ntpdate yum -y install…

flink写parquet解决timestamp时间格式字段问题

背景 Apache Parquet 是一种开源的列式数据文件格式,旨在实现高效的数据存储和检索。它提供高性能压缩和编码方案(encoding schemes)来批量处理复杂数据,并且受到许多编程语言和分析工具的支持。 在我们通过flink写入parquet文件的时候,会遇到timestamp时间格式写入的问题。…

PaddleSeg 从配置文件和模型 URL 自动化运行预测任务

git clone https://github.com/PaddlePaddle/PaddleSeg.git# 在ipynb里面运行 cd PaddleSegimport sys sys.path.append(/home/aistudio/work/PaddleSeg)import os# 配置文件夹路径 folder_path "/home/aistudio/work/PaddleSeg/configs"# 遍历文件夹&#xff0c;寻…

ESMC-600M蛋白质语言模型本地部署攻略

前言 之前介绍了ESMC-6B模型的网络接口调用方法&#xff0c;但申请token比较慢&#xff0c;有网友问能不能出一个本地部署ESMC小模型的攻略&#xff0c;遂有本文。 其实本地部署并不复杂&#xff0c;官方github上面也比较清楚了。 操作过程 环境配置&#xff1a;CUDA 12.1、…

Spring Boot中如何实现异步处理

在 Spring Boot 中实现异步处理可以通过使用 Async 注解和 EnableAsync 注解来实现。以下是如何配置和使用异步处理的步骤和示例代码。 步骤&#xff1a; 启用异步支持&#xff1a; 在 Spring Boot 配置类上使用 EnableAsync 注解启用异步处理。使用 Async 注解异步方法&…

HTML<hgroup>标签

例子&#xff1a; 使用hgroup元素标记标题和段落是相关的&#xff1a; <hgroup> <h2>Norway</h2> <p>The land with the midnight sun.</p> </hgroup> 定义和用法&#xff1a; 标签<hgroup>用于包围标题和一个或多个<p&g…

人工智能在医疗领域的应用与挑战

人工智能在医疗领域的应用与挑战 摘要&#xff1a;本文深入探讨了人工智能在医疗领域的应用现状&#xff0c;分析了其在疾病诊断、药物研发、医疗影像分析等方面的显著成果&#xff0c;同时也剖析了人工智能在医疗应用中面临的数据隐私、伦理道德、技术可靠性等挑战&#xff0…

JavaScript 验证 API:全面解析与实战指南

JavaScript 验证 API:全面解析与实战指南 引言 随着互联网技术的不断发展,前端开发领域的重要性日益凸显。JavaScript 作为前端开发的核心技术之一,其功能性和可扩展性得到了广泛关注。验证功能是JavaScript中不可或缺的一部分,它保证了用户输入数据的正确性和有效性。本…

web端ActiveMq测试工具

如何用vue3创建简单的web端ActiveMq测试工具&#xff1f; 1、复用vue3模板框架 创建main.js,引入APP文件&#xff0c;createApp创建文件&#xff0c;并加载element插件&#xff0c;然后挂载dom节点 2、配置vue.config.js脚本配置 mport { defineConfig } from "vite&qu…

3.DrawCall的概念

DrawCall是渲染管线中的一个重要概念&#xff0c;指的是CPU向GPU发送的一个绘制命令&#xff0c;告诉GPU&#xff1a;“请根据我提供的数据&#xff0c;画一个物体&#xff08;或一部分物体&#xff09;。” 通俗易懂讲解&#xff1a;DrawCall就像给画师下订单 想象你是一个老…