three.js+WebGL踩坑经验合集(3):THREE.Line的射线检测问题(不是阈值方面的,也不是难选中的问题)

笔者之所以要在标题里强调不是阈值方面,是因为网上的大多数文章提到线的射线检测问题,90%以上的文章都说是因为线太细所以难选中,然后让大家把线的阈值调大。

而本文所要探讨的问题则恰好相反,不是难选中,而是在某些角度下太容易被误选中了,下面放出测试用例:

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>threeLine_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>
</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.BufferGeometry();var points = [new THREE.Vector3(0, 0, 1000), new THREE.Vector3(0, 0, -1000)];geometry.setFromPoints(points);var material = new THREE.LineBasicMaterial({color: srcColor});var line = new THREE.Line(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, 600);  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;var intersects = raycaster.intersectObject(scene, true);      for(let intersect of intersects){intersect.object.material.color = overColor;}}window.addEventListener("mousemove", onMouseMove);</script>
</body>
</html>

按照笔者代码设置的相机参数,会发现鼠标在距离线还有一定距离的时候就已经出现移入效果了,然后旋转一下相机,鼠标又真的离线很近才会碰到,精度提升了不少。

为此笔者翻阅了Line.js的raycast代码,其实现的核心代码在这里

THREE.Ray.distanceSqToSegment计算的是射线到被检测线条(vStart和vEnd的连线)的距离,里面的代码看着让人犯困,各种不知名变量。笔者有想过按着代码演算一遍,但是很快就放弃了这一念头,因为除了可读性差,计算过程繁琐以外,笔者还发现了一个漏洞,就是距离的计算全是取的3D坐标,而透视相机因为具备近大远小的效果,所以越是靠近屏幕,两个3D点在投影到2D后的距离也会越大。

所以这里更像是跟一个具有实际厚度的圆柱体进行碰撞检测,下面我们在THREE.Line所在的位置添加一个半径为1的THREE.CylinderGeometry看看。此处我们不对圆柱体的射线检测做任何界面的反馈。

在创建line的后面追加创建圆柱体的代码:

var cyGeometry = new THREE.CylinderGeometry(1, 1, 1200, 100, 100)
var cyMaterial = new THREE.MeshBasicMaterial({color: srcColor, transparent: true, opacity: 0.25});
var cyLine = new THREE.Mesh(cyGeometry, cyMaterial);
cyLine.rotation.x = Math.PI * 0.5;
scene.add(cyLine);

然后射线检测代码做适当的调整,确保Raycaster只跟Line做射线检测:

//把scene换成line,确保新增的圆柱体不参与射线检测
var intersects = raycaster.intersectObject(line, true); 

我们来看看运行的效果

是吧,射线检测的结果更接近于具备透视效果的圆柱体。所以单纯调阈值,对于THREE.Line来说意义不大(除非用的是正交相机,或者线的z跨度不大,或者相机角度被限制在一个很小的范围内)。

数学上,线是一个一维几何体,它不具备厚度这一特性。所谓的厚度,它没有任何几何意义,仅仅是为界面显示而设置的一个属性。

three.js使用原生WebGL中WebGLRendingContext(以下简称gl)的画线api进行线条的渲染。

gl.drawArrays/drawElements(gl.LINES,...);

该api严格按照线的几何概念进行呈现,不会对输入的厚度参数进行近大远小的变换,但是three.js中的Line就没有考虑到这一点,所以射线检测的结果跟视觉没有正确匹配上。

此外,THREE.Line使用原生的gl.lineWidth设置厚度,然而这个原生的api存在兼容问题,火狐和部分桌面程序能识别,但Chrome不认,那除了粗细为1的线以外,THREE.Line是没有办法玩下去的。

盲猜three.js因为浏览器兼容问题而没有花太大精力去修复THREE.Line射线检测不准确的问题,而是另外弄了一个THREE.Line2对象。

Line2不是three.js主包里面的内容,而是被放到了示例文件夹examples里面,因此使用前需要引入(笔者现在还是拿es5版本的three.js,若已使用较新版本的three.js则无需手动引入)

<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>

然后再把THREE.Line换成THREE.Line2

var geometry = new THREE.LineGeometry();
geometry.setPositions([0, 0, 600, 0, 0, -600]);
var material = new THREE.LineMaterial({color: 0xFFFFFF, resolution: new THREE.Vector2(window.innerWidth, window.innerHeight)});    
var line = new THREE.Line2(geometry, material);
scene.add(line);

可以发现这里不是简单把Line改成Line2就完事,geometry和material的类型都换了,api也不一样。这也是让还没习惯three.js的开发小伙伴们嗤之以鼻的槽点之一。

resolution这个参数也是怪怪的,但笔者打算下一篇再跟大家探讨,大家就先死记一下吧。

我们来看看换THREE.Line2后的效果。

这下真的跟线对应上了,而且某些位置还细得不太好选,这种情况下再调阈值就特别管用。

下面来小结本文的内容:

1 THREE.Line在显示上没有透视效果,但是射线检测的代码依然按着有透视效果的方式进行实现,所以在透视相机下,射线检测的结果跟显示不匹配。

2 THREE.Line在主流的Chrome浏览器下无法设置厚度(只能固定为1),需要用THREE.Line2代替。

3 调阈值的方法不适用于所有场景,遇到透视相机,线的z跨度较大的场合,阈值怎么调都是调不好的,这时也建议改用THREE.Line2。

THREE.Line2抛弃了原生的画线api,通过自己绘制三角面来模拟线的效果,跟THREE.Line相比,灵活性和可控性都高出不少,但与此同时也变得更加难用。

尽管如此,THREE.Line2在射线检测方面也是有bug的,笔者将会在下一篇跟大家继续探讨。大家先好好消化本文,我们待会见!

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

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

相关文章

省市区三级联动

引言 在网页中&#xff0c;经常会遇到需要用户选择地区的场景&#xff0c;如注册表单、地址填写等。为了提供更好的用户体验&#xff0c;我们可以实现一个三级联动的地区选择器&#xff0c;让用户依次选择省份、城市和地区。 效果展示&#xff1a; 只有先选择省份后才可以选择…

快速搭建深度学习环境(Linux:miniconda+pytorch+jupyter notebook)

本文基于服务器端环境展开&#xff0c;使用的虚拟终端为Xshell。 miniconda miniconda是Anaconda的轻量版&#xff0c;仅包含Conda和Python&#xff0c;如果只做深度学习&#xff0c;可使用miniconda。 [注]&#xff1a;Anaconda、Conda与Miniconda Conda&#xff1a;创建和管…

BGP分解实验·11——路由聚合与条件性通告(3)

续接上&#xff08;2&#xff09;的实验。其拓扑如下&#xff1a; 路由聚合的负向也就是拆分&#xff0c;在有双出口的情况下&#xff0c;在多出口做流量分担是优选方法之一。 BGP可以根据指定来源而聚合路由&#xff0c;在产生该聚合路由的范围内的条目注入到本地BGP表后再向…

攻防世界easyRSA

解密脚本&#xff1a; p473398607161 q4511491 e17def extended_euclidean(a, b):if b 0:return a, 1, 0gcd, x1, y1 extended_euclidean(b, a % b)x y1y x1 - (a // b) * y1return gcd, x, ydef calculate_private_key(p, q, e):phi (p - 1) * (q - 1)gcd, x, y extend…

常见的多媒体框架(FFmpeg GStreamer DirectShow AVFoundation OpenMax)

1.FFmpeg FFmpeg是一个非常强大的开源多媒体处理框架&#xff0c;它提供了一系列用于处理音频、视频和多媒体流的工具和库。它也是最流行且应用最广泛的框架&#xff01; 官方网址&#xff1a;https://ffmpeg.org/ FFmpeg 的主要特点和功能&#xff1a; 编解码器支持: FFmpe…

.NET MAUI进行UDP通信(二)

上篇文章有写过一个简单的demo&#xff0c;本次对项目进行进一步的扩展&#xff0c;添加tabbar功能。 1.修改AppShell.xaml文件&#xff0c;如下所示&#xff1a; <?xml version"1.0" encoding"UTF-8" ?> <Shellx:Class"mauiDemo.AppShel…

计算机网络之链路层

本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 02 数据链路层 在网上看到其他人做了详细的笔记&#xff0c;就不再多余写了&#xff0c;直接参考着学习吧。 1 详解数据链路层-数据链路层的功能【王道计算机网络笔记】_wx63088f6683f8f的技术博客_51C…

YOLOv11改进,YOLOv11检测头融合DSConv(动态蛇形卷积),并添加小目标检测层(四头检测),适合目标检测、分割等任务

前言 精确分割拓扑管状结构例如血管和道路,对各个领域至关重要,可确保下游任务的准确性和效率。然而,许多因素使任务变得复杂,包括细小脆弱的局部结构和复杂多变的全局形态。在这项工作中,注意到管状结构的特殊特征,并利用这一知识来引导 DSCNet 在三个阶段同时增强感知…

Flutter android debug 编译报错问题。插件编译报错

下面相关内容 都以 Mac 电脑为例子。 一、问题 起因&#xff1a;&#xff08;更新 Android studio 2024.2.2.13、 Flutter SDK 3.27.2&#xff09; 最近 2025年 1 月 左右&#xff0c;我更新了 Android studio 和 Flutter SDK 再运行就会出现下面的问题。当然 下面的提示只是其…

扣子平台音频功能:让声音也能“智能”起来

在数字化时代&#xff0c;音频内容的重要性不言而喻。无论是在线课程、有声读物&#xff0c;还是各种多媒体应用&#xff0c;音频都是传递信息、增强体验的关键元素。扣子平台的音频功能&#xff0c;为开发者和内容创作者提供了一个强大而灵活的工具&#xff0c;让音频的使用和…

RubyFPV开源代码之系统简介

RubyFPV开源代码之系统简介 1. 源由2. 工程架构3. 特性介绍&#xff08;软件&#xff09;3.1 特性亮点3.2 数字优势3.3 使用功能 4. DEMO推荐&#xff08;硬件&#xff09;4.1 天空端4.2 地面端4.3 按键硬件Raspberry PiRadxa 3W/E/C 5. 软件设计6. 参考资料 1. 源由 RubyFPV以…

将 OneLake 数据索引到 Elasticsearch - 第二部分

作者&#xff1a;来自 Elastic Gustavo Llermaly 及 Jeffrey Rengifo 本文分为两部分&#xff0c;第二部分介绍如何使用自定义连接器将 OneLake 数据索引并搜索到 Elastic 中。 在本文中&#xff0c;我们将利用第 1 部分中学到的知识来创建 OneLake 自定义 Elasticsearch 连接器…

PMP–一、二、三模–分类–14.敏捷

文章目录 敏捷中的角色职责与3个工件--题干关键词角色职责3个工件 高频考点分析&#xff08;一、过程&#xff1b;二、人员&#xff09;一、过程&#xff1a;1.1 变更管理&#xff1a;1.1.1 瀑布型变更&#xff08;一次交付、尽量限制、确定性需求 &#xff1e;风险储备&#x…

Vue2下篇

插槽&#xff1a; 基本插槽&#xff1a; 普通插槽&#xff1a;父组件向子组件传递静态内容。基本插槽只能有一个slot标签&#xff0c;因为这个是默认的位置&#xff0c;所以只能有一个 <!-- ParentComponent.vue --> <template> <ChildComponent> <p>…

【科研建模】Pycaret自动机器学习框架使用流程及多分类项目实战案例详解

Pycaret自动机器学习框架使用流程及项目实战案例详解 1 Pycaret介绍2 安装及版本需求3 Pycaret自动机器学习框架使用流程3.1 Setup3.2 Compare Models3.3 Analyze Model3.4 Prediction3.5 Save Model4 多分类项目实战案例详解4.1 ✅ Setup4.2 ✅ Compare Models4.3 ✅ Experime…

Linux学习笔记——网络管理命令

一、网络基础知识 TCP/IP四层模型 以太网地址&#xff08;MAC地址&#xff09;&#xff1a; 段16进制数据 IP地址&#xff1a; 子网掩码&#xff1a; 二、接口管命令 ip命令&#xff1a;字符终端&#xff0c;立即生效&#xff0c;重启配置会丢失 nmcli命令&#xff1a;字符…

手撕Diffusion系列 - 第九期 - 改进为Stable Diffusion(原理介绍)

手撕Diffusion系列 - 第九期 - 改进为Stable Diffusion&#xff08;原理介绍&#xff09; 目录 手撕Diffusion系列 - 第九期 - 改进为Stable Diffusion&#xff08;原理介绍&#xff09;DDPM 原理图Stable Diffusion 原理Stable Diffusion的原理解释Stable Diffusion 和 Diffus…

JAVAweb学习日记(八) 请数据库模型MySQL

一、MySQL数据模型 二、SQL语言 三、DDL 详细见SQL学习日记内容 四、DQL-条件查询 五、DQL-分组查询 聚合函数&#xff1a; 分组查询&#xff1a; 六、DQL-分组查询 七、分页查询 八、多表设计-一对多&一对一&多对多 一对多-外键&#xff1a; 一对一&#xff1a; 多…

微信小程序1.1 微信小程序介绍

1.1 微信小程序介绍 内容提要 1.1 什么是微信小程序 1.2 微信小程序的功能 1.3 微信小程序使用场景 1.4 微信小程序能取代App吗 1.5 微信小程序的发展历程 1.6微信小程序带来的机会

音频入门(一):音频基础知识与分类的基本流程

音频信号和图像信号在做分类时的基本流程类似&#xff0c;区别就在于预处理部分存在不同&#xff1b;本文简单介绍了下音频处理的方法&#xff0c;以及利用深度学习模型分类的基本流程。 目录 一、音频信号简介 1. 什么是音频信号 2. 音频信号长什么样 二、音频的深度学习分…