Three.js 选择对象的本质是从点击位置发射光线
,但屏幕坐标系与webgl坐标系是不同的,而把屏幕的二维坐标转化为三维坐标
就是关键,做一步换算后交由Raycaster的setFromCamera方法即可。
思路
如下:1.获取屏幕坐标(x, y)2.换算至webgl坐标中的(x2,y2),此时长度仍为像素单位下的长度3.由于webgl坐标轴的范围为(-1,1),做个比值,称为标准化
4.Raycaster.setFromCamera(mouse, camera)发射射线5.Raycaster.intersectObjects获取射得的所有对象,再按需要操作 先对比下各坐标轴,再按代码分析步骤
代码中使用了我封装的方法,threeConf对象包含three.js的基本组件,但不影响拾取对象的逻辑,
一、坐标轴
1.屏幕坐标轴
即我们的屏幕的二维坐标,矩形为我们的屏幕,左上角就是(0, 0),长度即是以px为单位
2.世界坐标系
即webgl中的坐标系,屏幕中心为(0, 0, z),第三个坐标为从屏幕指向我们的z轴。三个坐标的大小范围
为(-1,1),如图
这里其实还有个相机坐标系,因为还没有学习webgl或者opengl怕胡说八道,我觉得一篇很棒的blog贴于文末
但是我们实现选择拾取功能时,但单纯从three.js角度来看可以不需要相关知识二、代码思路
全部代码请看上方地址
1.坐标标准化
//1.获取屏幕坐标(x, y)//2.换算至webgl坐标中的(x2,y2),此时长度单位仍为像素//3.由于webgl坐标轴的范围为(-1,1),做个比值,称为标准化//这三步后化简的式子如下,这里我们three.js的窗口为整个浏览器let rayRaster = new THREE.Raycaster();let mouse = new THREE.Vector2();function onMouseMove(event) { mouse.x = (event.clientX/window.innerWidth) * 2 - 1; mouse.y = 1 -(event.clientY/window.innerHeight) * 2 ;}window.addEventListener('mousemove', onMouseMove, false);
x1 = event.clientX,y1 = event.clientY,即点击位置
,换算过程如下(这里默认画布宽高为浏览器宽高)
2.发射射线,获取对象
这里截取射线代码
function render() { //射线射出 rayRaster.setFromCamera(mouse, threeConf.camera); //射线上的物体 let intersects = rayRaster.intersectObjects(threeConf.scene.children); //选中另一物体的情况,仍为同一物体则不更新,这里我们只拿第一个物体 if(selectedObj !== intersects[0]){ selectedObj && selectedObj.object.material.color.set(0x87CEEB); //复原 intersects.length && intersects[0].object.material.color.set(0xff0000); selectedObj = intersects[0]; document.body.style.cursor = "pointer"; } //未选中物体情况 if(intersects[0] === undefined){ document.body.style.cursor = "auto"; } threeConf.stats.update(); threeConf.renderer.render(threeConf.scene, threeConf.camera); requestAnimationFrame(render);}
即可
三、参考资料
OpenGL.坐标系统的介绍与坐标变换的实现(此篇blog包含相机坐标轴)
ThreeJS中的点击与交互——Raycaster的用法
Three.js Raycaster官方文档