赞
踩
首先关于Three.js鼠标点击交互事件的基础部分的推导及写法不再详细赘述,可参考:
ThreeJS中的点击与交互——Raycaster的用法
初始化时定义:
//点击射线
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
点击物体触发方法:
clickGoujian(event) {
var width = document.getElementById("canvas-frame").clientWidth;
var height = document.getElementById("canvas-frame").clientHeight;
this.mouse.x = (event.clientX / width) * 2 - 1;
this.mouse.y = -(event.clientY / height) * 2 + 1;
this.raycaster.setFromCamera(this.mouse, this.camera);
var intersects = this.raycaster.intersectObjects(this.clickObjects);//this.clickObjects是我存放可点击模型的数组
}
这里的intersects就是我们点击射线穿过去得到的对象数组了,返回格式:
[ { distance, point, face, faceIndex, object }, … ]
distance - 射线的起点到相交点的距离
point - 在世界坐标中的交叉点
face -相交的面
faceIndex - 相交的面的索引
object - 相交的对象
instanceId - 实例化网格的实例index
数组是按照distance来排序的,所以一般情况下我们拿到第一个对象的object就可以进行所需要的操作了。
如果你的代码里面有让物体隐藏的部分,需要过滤一下隐藏的物体:
//过滤被隐藏的物体,这里是lodash的方法,你也可以自己实现
//可参考:https://www.lodashjs.com/docs/lodash.filter#_filtercollection-predicate_identity
var clickVisableInfoArr = _.filter(intersects, (item) => {
return item.object && item.object.visible;
});
if (clickVisableInfoArr.length) {
var clickVisableObj = clickVisableInfoArr[0];
var clickVisableMesh = clickVisableObj.object;//这里就是需要操作的网格模型了
}
当你的绘制场景不是全屏时,就不能够像上面那样了,比如我这种:
主要看视图的左上角点距离屏幕顶端和左侧有多少,分别记为marginLeft和marginTop,推导过程可以看文章最开始提到的链接,看懂后再推导非全屏的就不难了。代码改成:
var marginLeft = 150;
var marginTop = 60;
this.mouse.x = ((event.clientX - marginLeft) / width) * 2 - 1;
this.mouse.y = -((event.clientY - marginTop) / height) * 2 + 1;
因为我的场景中有大量相交重合的物体,会出现闪烁现象,所以使用了多边形偏移(关于闪烁问题的解决可移步至three.js 模型重合相交部分闪烁 Z-Fighting),这个导致了我点击获取的第一个物体不是我想要的。
解决办法:
而我们把基础用法里面的clickVisableInfoArr打印出来时可以看到,除去隐藏的模型,得到了六个对象,但我们实际有三个相交的物体,观察得到,我们这三个模型的距离近似相等(不是完全相等),我这里的情况是相差不会超过1,具体情况要根据你的实际情况来,如果有更好的办法欢迎指正。所以我们第一步是根据距离来判断它们是不是属于相交的这几个模型。
这个就需要根据多边形偏移量来进行判断了,我们知道,当 polygonOffsetFactor、polygonOffsetUnits这两个参数都为负时(深度减小),网格将被拉向摄影机(位于前面)。 当两个参数都为正(增加深度)时,网格会被推离摄影机(会落在后面)。所以我们可以通过这两个参数来判断。
综上,我们在上面的基础用法拿到clickVisableInfoArr后可以根据距离和多边形偏移量来进行手动对比。
//多个相交物体获取离镜头最近的 var clickVisableObj = clickVisableInfoArr[0], tempDis = clickVisableInfoArr[0].distance; for (var i = 0; i < clickVisableInfoArr.length; i++) { var item = clickVisableInfoArr[i]; //得到的数组按距离排序的 相交物体的距离可能不会完全一致,存在微小差异,一般不会大于1,具体根据项目情况来 if (Math.abs(item.distance - tempDis) >= 1) { //距离与第一个相差大于1则退出循环 break; } else { //因为我在对材质设置偏移量时,主要改动的是polygonOffsetUnits,所以这里仅对这个参数来进行对比,具体实现要参照你的偏移量怎么设置的 var tempMater = item.object.material; var tempOffset = tempMater.polygonOffsetUnits ? tempMater.polygonOffsetUnits : 0; var clickMater = clickVisableObj.object.material; var clickOffset = clickMater.polygonOffsetUnits ? clickMater.polygonOffsetUnits : 0; //偏移量越小的离相机越近,在上层,点击的是上层的构件 tempOffset < clickOffset ? clickVisableObj = item : null; } } var clickVisableMesh = clickVisableObj.object;
这里的clickVisableObj.object就是实际点击的最上层模型了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。