建立第一条射线,与前面的线段不同,这里的射线是数学意义上的射线,它不是一个物体也无法被渲染出来。因为它不是任何物体的子物体,所以raycaster的端点位置取世界坐标而非相对坐标,注意和前面同样位置的线段端点的不同。
因为我们使用了子物体,所以intersectObjects的第二个参数必须设为true以强制检查每一个子物体,否则raycaster只会检查第一个参数这一层的物体而忽略掉globeMesh。
1 if ( intersects.length > 0 ) { (var i=0;i<intersects.length;i++) 5 { (intersects[i].object.visible && intersects[i].face&&(this.object3D.inter_group!=intersects[i].object.inter_group)&&intersects[i].distance<this.object3D.inter_length) { intersected = intersects[i];
前面提到raycaster无法检测到物体的内表面,但在涉及到子物体检测时,这一命题变得不确定了,所以要加上更多的判断条件(这是不是自己坑自己。。。)
raycaster2.far=this.object3D.inter_length; 3 var intersects2 = raycaster2.intersectObjects(scene.children,true); ( intersects2.length > 0 ) 6 { 7 flag_safe=1; 8 for(var j=0;j<intersects2.length;j++) 9 { 10 if (intersects2[j].object.visible && intersects2[j].face&&(intersected.object.inter_group!=intersects2[j].object.inter_group)&&intersects2[j].distance<this.object3D.inter_length) 11 { { } 16 break; 17 } 18 } 19 if(flag_safe==1) 20 { 21 this.flag_coll = 1; 22 } 23 }
这里的逻辑还不够优雅,下次再调整吧
四、优化方向:
1、现在的“3D碰撞检测”实现了最简单情况下的碰撞检测,但算法仍具有很大的局限性,比如这种情况下,碰撞检测射线永远无法穿过其他物体:
对此我想到的解决方法是:
将raycaster扩充为检测方向上的多条平行射线来检测物体边缘碰撞的情况,而这需要用到线性代数的相关知识,需再复习一下。
2、在没有设置半透明的情况下运行,可能会发生物体重叠但没有判断碰撞的情况,怀疑是因为我采用的是“先碰撞后检测”的方法,Three.js认为重叠部分的图元不需要绘制将其自动舍弃,导致射线检测不到重叠部分。
五、扩展:
昨天发现美国前辈Lee Stemkoski的Three.js示例展示了另一种碰撞检测方法,和我的方法相比各有优缺点
演示地址:
核心代码:
1 for (var vertexIndex = 0; vertexIndex < MovingCube.geometry.vertices.length; vertexIndex++) 2 { 3 var localVertex = MovingCube.geometry.vertices[vertexIndex].clone(); 4 var globalVertex = localVertex.applyMatrix4( MovingCube.matrix ); 5 var directionVector = globalVertex.sub( MovingCube.position ); ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() ); 8 var collisionResults = ray.intersectObjects( collidableMeshList ); 9 if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ) 10 appendText(" Hit "); 11 }
该方法从物体的中心向物体的每一个顶点做一条碰撞检测射线,如果碰到第一个物体的碰撞点到物体中心的距离小于物体中心到该顶点的距离,则认为发生碰撞。