babylon101

光线投射


光线投射

光线就像阳光。它用于检查网格和细线之间的场景中的碰撞或交叉。

在上一个教程中,我们使用函数scene.pick(scene.pointerX,scene.pointerY):使用鼠标选择网格(光线从3D中的相机移动到鼠标位置) //doc.babylonjs.com/How_To/picking_collisions

但在这里我们将看到我们可以从任何点和任何方向投射光线。例如,在第三人称视角的射击游戏中:我们的子弹和障碍物之间的碰撞。

课程文件:

//doc.babylonjs.com/classes/3.0/ray 您首先要创建一条光线。

//doc.babylonjs.com/classes/3.0/scene 方法scene.pickWithRay()在场景中抛出一条光线来拾取网格

//doc.babylonjs.com/classes/3.0/pickinginfo 获取拣货信息。


检测光线所触及的第一个网格

https://www.babylonjs-playground.com/#KNE0O#84 -


Raycast simple

在我们所有的游乐场,我们都会想象我们的角色是中心的主要盒子。它将持续向前射击激光束并检测哪个敌人(其他盒子)被击中。因此,使用鼠标,您不需要单击,而是使用此三角函数mousemovef(l34)移动以打开框。光线在创建时需要:原点,方向和长度。

首先,我们将box.isPickable设置为false以避免光线从内部触及盒子(l16)。因为我们将光线的起点(原点)设置在框的中心。

最重要的部分是获得良好的方向向量(l57):

var forward = new BABYLON.Vector3(0,0,1);        
forward = vecToLocal(forward, box);

var direction = forward.subtract(origin);
direction = BABYLON.Vector3.Normalize(direction);

我们希望前向矢量相对于框空间和方向。然后,为了获得方向,我们从原点,框位置中减去它。函数vecToLocal旨在通过将矢量乘以网格矩阵来从网格视点转换位置。

然后,我们使用给定的所有元素创建光线,例如长度为100(l65):

var ray = new BABYLON.Ray(origin, direction, length);

最后,如果光线接触到网格,我们得到光线的生命值(l68):

var hit = scene.pickWithRay(ray);

如果一个网格被击中,我们就可以通过拾取信息来做我们想要的,例如获取网格名称,点的位置等等......这里我们改变它的大小,因为它更有趣!


如果您稍后需要检查此框上的光线交叉点,则不必强制将box.isPickable设置为false。您可以在框前面设置矢量的原点,方向稍微进一步设置所需的长度(l55):

https://www.babylonjs-playground.com/#KNE0O#17 -



谓词函数

它是一个过滤器,用于选择可选择的网格:

https://www.babylonjs-playground.com/#KNE0O#18 -


Raycast predicate

我添加了一个新的函数谓词(l54):

  function predicate(mesh){
        if (mesh == box2 || mesh == box){
            return false;
        }
        return true;
    }

在这里的参数:

scene.pickWithRay(ray, predicate);

isPickable false参数变得无关紧要,因此我们必须避免使用box。我们也避免使用box2进行测试,并允许其余部分(默认情况下为box3和box4)。

结果是,只有box3,第二个蓝色背后,box4将增长。所以它工作得很好,就像box2对光线是透明的!


方法pickWithRay还有另一个可选参数。它是布尔值fastCheck(默认为false)。True将返回与光线相交的第一个网格(按网格数组的顺序),而不是与光线起点最接近的网格。


三角谓词

从Babylon.js v4.0开始,您可以定义一个自定义谓词,以过滤选择要针对传入光线进行测试的三角形。将使用每个面的3个顶点和即将到来的光线调用谓词:

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);
  });

在这个例子中,我们过滤掉所有不面向相机的三角形。

实例: https://www.babylonjs-playground.com/#EES9W5 -



多选

如果我们不希望光线在第一个障碍处停止,我们可以使用scene.multiPickWithRay:

https://www.babylonjs-playground.com/#KNE0O#19 -


Raycast multipick

拾取结果将是一个数组(l68)。所以我们做一个循环来改变所有网格命中,你可以看到两个蓝框大小的变化。这就像一颗强大的子弹!


另一种方法是直接使用Ray类。

要将光线更改为本地空间:

Ray.Transform(ray, matrix) → Ray

检查交叉点:

Ray.intersectsMesh(mesh, fastCheck) → PickingInfo


调试

要了解光线的开始位置和方向,可能很难理解。为了帮助您调试,您可以使用RayHelper。

您可以使用静态函数来创建和显示一个:

BABYLON.RayHelper.CreateAndShow(ray, scene, new BABYLON.Color3(1, 1, 0.1));

或者您可以使用更详细的版本:

var rayHelper = new BABYLON.RayHelper(ray);
rayHelper.show(scene);

帮助器也可以连接到网格以跟踪其方向:

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);

https://www.babylonjs-playground.com/#ZHDBJ#48 -


下一步

通常,在场景中有一些2D形状就足够了,接下来是精灵。

进一步阅读

基本 - L1

网格概述

External

外部
论坛挑选Ray
论坛世界本地雷