您现在的位置是:亿华云 > 人工智能
Webgl & Three.js中的物体拾取
亿华云2025-10-09 09:01:28【人工智能】8人已围观
简介1、引子:在传统的web开发中,由于存在DOM树以及事件捕获冒泡等机制,我们可以很方便的在某个DOM节点上注册事件,并且执行父元素事件代理等一系列操作。但是在webgl的三维世界里,用户使用鼠标或者t
1、物体引子:
在传统的物体web开发中,由于存在DOM树以及事件捕获冒泡等机制,物体我们可以很方便的物体在某个DOM节点上注册事件,并且执行父元素事件代理等一系列操作。物体但是物体在webgl的三维世界里,用户使用鼠标或者touch事件,物体事件接收方是物体canvas容器,如何将这种点击行为映射到三维世界,物体就需要借助三维世界的物体能力了,并且建立从canvas平面容器到三维世界的物体桥梁,进行所谓的物体物体拾取
2、基础知识:
DOM与NDC坐标转换,物体相机射线,物体观察者模式。物体熟悉这部分知识的同学,可以直接跳过到下面的代码实现。
a、DOM坐标和NDC坐标转换:在这里,我们知道DOM坐标的(0,0)点在容器左上角,亿华云计算NDC坐标的(0,0)点在容器中心,需要进行坐标转换。具体定义可以参考下面两个链接三维坐标变换 屏幕坐标定义
b、相机射线: 根据Three.js的官方定义,射线就是用来做物体拾取的机制。相比较于传统的颜色拾取,射线拾取可以识别多个物体,得到先后顺序,在使用上更加方便并且符合人的直觉。? 下面看一个官方的code example?javascript
const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); function onMouseMove( event ) { // 这里实现了DOM坐标系到NDC坐标系的转换 mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; } function render() { // 从相机位置,发出一条射线 raycaster.setFromCamera( mouse, camera ); // 检测相机发出射线相交的obj列表 const intersects = raycaster.intersectObjects( scene.children ); for ( let i = 0; i < intersects.length; i ++ ) { // 将相交物体的材质颜色设置为红色 intersects[ i ].object.material.color.set( 0xff0000 ); } renderer.render( scene, camera ); } window.addEventListener( mousemove, onMouseMove, false ); window.requestAnimationFrame(render);c、观察者模式:与dom事件机制类似,观察者模式非常是适合做这种注册-触发机制的。
3、具体实现:
考虑到每次遍历intersects数组非常的不方便,特别是当场景中有实际上百个Object3D的服务器托管时候。所以这里我们定义一个全局的对象存储注册事件,然后修改Object3D的原型链,增加on和on和on和off方法,来实现类似于DOM元素的事件注册和销毁。
const globalEvent = { click: { }} Object.assign(Object3D.prototype, { $on(eventType, cb) { if(globalEvent.hasOwnProperty(eventType)) { globalEvent[eventType][this.id] = { object3d: this, callback: cb }; } else { // error warn} } $off(eventType) { if (!eventType) throw new Error() if(globalEvent.hasOwnProperty(eventType)) { delete globalEvent[eventType][this.id] } else { throw new Error() } } }) init(camera) function init(camera, container) { let intersectPoint, obj, mouseX, mouseY, clicked; const targetObj = globalEvent.click const rayCaster = new Raycaster(); function down(e) { obj = null; e.preventDefault(); mouseX = event.clientX; mouseY = event.clientY; if (!globalEvent.click) return; rayCaster.setFromCamera( new Vector2( (mouseX / window.innerWidth) * 2 - 1, -(mouseY / window.innerHeight) * 2 + 1 ), camera ); let intersects = rayCaster.intersectsObjects(getVisibleList(targetObj)); if (intersects.length > 0) { if (clicked) { obj = null; return; } clicked = true; obj = intersects[0].object; intersectPoint = intersects[0].point; } else { clicked = false; } } function move(e) { event.preventDefault(); // 这里针对移动端做一些优化 } function up(e) { event.preventDefault(); if (clicked && !!obj && obj.callback) { obj.callback(obj.object3d, intersectPoint); } clicked = false } const eventOption = { passive: false }; container.addEventListener(mousedown, down, { passive: false}); container.addEventListener(mousemove, move, { passive: false}); container.addEventListener(mouseup, up, { passive: false}); container.addEventListener(touchstart, down, { passive: false}); container.addEventListener(touchmove, move, { passive: false}); container.addEventListener(touchend, up, { passive: false});} function getVisibleList(targetObj) { const list = [] for (const key in targetObj) { const target = targetObj[key].object3d; if (target.visible) list.push(target); } return list } /* 使用方式:直接在mesh上注册事件 target: 命中物体 point: 命中点的三维坐标 */ mesh.$on(click, (target, point)) { }4、总结
本文通过修改three.js中Object3D的原型链,基于观察者模式,增加了点击事件绑定,支持用户点击canvas容器,拾取场景中的物体。在具体的生产环境使用中,还需要考虑如何实现事件冒泡,渲染层级,鼠标拖拽和长按等操作,这些不在主流程中,不再赘述,大家可以在debug的过程中,逐渐完善上面的代码。
作者:huoyunxieshen0007
链接:https://juejin.cn/post/6951226769529634830
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。企商汇
很赞哦!(9311)
上一篇: 公司和个人选域名方法一样吗?有什么不同?
下一篇: 四、长串数字域名
相关文章
- 国际域名转移的费用和处理步骤是什么?
- 手把手教你用ECharts画饼图和环形图
- 如何在 C# 8 中使用默认接口方法
- 芯片上的大脑:英国科学家将类人脑干细胞编织在芯片上
- 新手可以注册cc域名吗?cc域名有什么特点?
- 不要用arxiv链接了!引用更规范,华人博士创建了一个小工具
- React Core Team 成员开发的「火焰图组件」技术揭秘
- 鸿蒙通信开发Wi-Fi IoT套件连PCF8563实现电子钟功能
- 互联网其实拼的也是人脉,域名投资也是一个时效性很强的东西,一个不起眼的消息就会引起整个域名投资市场的动荡,因此拓宽自己的人脉圈,完善自己的信息获取渠道,让自己能够掌握更为多样化的信息,这样才更有助于自己的域名投资。
- 效率倍增!5款非常超级实用的Python工具