11 Babylonjs基础入门 射线拾取
射线Ray就相当于一束光。它用于检查模型网格和细线之间在场景中的碰撞或交叉。
在上一个教程中,我们使用函数scene.pick(scene.pointerX,scene.pointerY):使用鼠标去拾取模型网格(射线从相机位置移动鼠标位置)
但本教程中,我们将实现我们从任何点和任何方法投射射线进行拾取。例如,在第三人称视角的射击游戏中:我们的子弹和障碍物之间的碰撞。
相关API:
https://doc.babylonjs.com/api/classes/babylon.ray 实现创建射线
https://doc.babylonjs.com/api/classes/babylon.scene 中的scene.pickWithRay()方法通过射线拾取模型网格。
https://doc.babylonjs.com/api/classes/babylon.pickinginfo 拾取对象的相关信息
射线拾取第一个照射到的模型网格
案例地址:点击这里
在案例当中,我们将红色盒子想象成射击的角色。它将持续想朝向的方向发出射线并检测哪个敌人(其它的盒子)被击中。因此,使用鼠标你无须点击,只需要在场景中移动,红色盒子就会改变朝向。被击中的盒子高度会增加。
射线在创建时需要:起点,方向,以及长度。
首先,我们将我们将红色盒子的box.isPickable
设置为false以避免红色盒子与射线触发拾取。因为我们将射线的起点设置在了红色盒子的中心。并通过红色盒子的朝向设置方向向量:
var forward = new BABYLON.Vector3(0,0,1);
forward = vecToLocal(forward, box);
var direction = forward.subtract(origin);
direction = BABYLON.Vector3.Normalize(direction);
我们设置射线的朝向和红色盒子的朝向相同。为了获得方向,我们要先获取到在盒子旋转后的Z轴坐标系在世界坐标系下的位置,然后通过此坐标减去立方体位置获得射线朝向。函数vecToLocal是将局部坐标位置(0,0,1)乘以立方体的世界变换矩阵获得世界坐标系位置。
获取到向量以后,我们设置射线起点为立方体位置,向量为计算出来的向量,射线长度为100:
var ray = new BABYLON.Ray(origin, direction, length);
最后,通过pickWithRay()函数去判断是否拾取的模型:
var hit = scene.pickWithRay(ray);
如果拾取到模型,我们就可以通过信息去做我们需要实现的功能,例如,案例中改变了立方体y轴的缩放。
如果你需要检测与红色立方体的交点,需要将box.isPickable设置为true。
案例地址:点击这里
过滤函数
它属于一个过滤器,用去设置可选择的网格。案例地址:点击这里
首先我们需要先设置一个函数:
function predicate(mesh){
if (mesh == box2 || mesh == box){
return false;
}
return true;
}
然后,作为第二个参数设置pickWithRay函数:
scene.pickWithRay(ray, predicate);
有了这个参数,我们就不用对每个网格设置isPickable为false。在函数中看到,我们不仅禁掉了红色立方体,还禁掉了离得近的蓝色立方体。
最后,pickWithRay方法还有第三个参数fastCheck(可选)。它是一个布尔值(默认值为false)。如果设置为true,射线将不会按照距离射线起点最短距离返回值,而是按照场景的网格数组,返回最先匹配的那个网格。
三角面过滤函数
从Babylon.js v4.0版本开始,你可以定义一个自定义回调,以过滤选择要进行拾取的三角形面。需要的值为面的三个顶点和拾取射线:
scene.pick(scene.pointerX, scene.pointerY, null, false, null, (p0, p1, p2, ray) => {
var p0p1 = p0.subtract(p1);
var p2p1 = p2.subtract(p1);
var normal = BABYLON.Vector3.Cross(p0p1, p2p1); //获取两个向量并叉乘计算面的朝向
return (BABYLON.Vector3.Dot(ray.direction, normal) < 0);
});
在这个案例中,我们过滤掉了所有面的向量不朝向相机的三角形。案例地址:点击这里
拾取多个
如果我们希望将射线穿过的模型都查找出来,可以使用scene.multiPickWithRay方法进行拾取, 案例地址:点击这里
案例中我们会发现,两个蓝色立方体会一起变高。这种返回的值将会是以pickResult组成数组,所以我们需要遍历一下进行设置。
另一种方法是直接使用Ray类。
将射线修改为局部空间坐标系:
Ray.Transform(ray, matrix) → Ray
然后使用射线的intersectsMesh方法检测:
Ray.intersectsMesh(mesh, fastCheck) //返回 PickingInfo
//射线辅助器
要了解射线的开始位置和方向是否正确,可能很难知道。为了方便调试,Babylon.js增加RayHelper。
你可以使用静态函数来创建和显示一个:
BABYLON.RayHelper.CreateAndShow(ray, scene, new BABYLON.Color3(1, 1, 0.1));
或者你可以使用另一种:
var rayHelper = new BABYLON.RayHelper(ray);
rayHelper.show(scene);
RayHelper也可以绑定到模型网格来跟踪它的方向:
var localMeshDirection = new BABYLON.Vector3(0, 0, -1);
var localMeshOrigin = new BABYLON.Vector3(0, 0, -.4);
var length = 3;
rayHelper.attachToMesh(box, localMeshDirection, localMeshOrigin, length);
查看案例:点击这里