现在建网站赚钱吗,wordpress 查看访客,站长工具无内鬼放心开车禁止收费,装修公司加盟店排行榜笔者之所以要在标题里强调不是阈值方面#xff0c;是因为网上的大多数文章提到线的射线检测问题#xff0c;90%以上的文章都说是因为线太细所以难选中#xff0c;然后让大家把线的阈值调大。
而本文所要探讨的问题则恰好相反#xff0c;不是难选中#xff0c;而是在某些角…笔者之所以要在标题里强调不是阈值方面是因为网上的大多数文章提到线的射线检测问题90%以上的文章都说是因为线太细所以难选中然后让大家把线的阈值调大。
而本文所要探讨的问题则恰好相反不是难选中而是在某些角度下太容易被误选中了下面放出测试用例
!DOCTYPE html
html
headmeta charsetUTF-8titlethreeLine_raycaster/titlestylebody {margin: 0;overflow: hidden;}/stylescript srcthree/build/three.js/scriptscript srcthree/examples/js/controls/OrbitControls.js/script
/headbodyscriptvar 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 srcthree/examples/js/lines/LineSegmentsGeometry.js/script
script srcthree/examples/js/lines/LineGeometry.js/script
script srcthree/examples/js/lines/LineMaterial.js/script
script srcthree/examples/js/lines/LineSegments2.js/script
script srcthree/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的笔者将会在下一篇跟大家继续探讨。大家先好好消化本文我们待会见