八叉树的范围和射线检测

typescript八叉树的简单实现
说一下typescript八叉树的范围和射线检测实现。

学习八叉树,并用自己的方式实现一下,理论联系实际

范围检测

应用场景

在3d项目中,针对场景节点较多的情况,需要找到距离目标节点,比如玩家最近的一些其他玩家、怪物、NPC、风景等,把他们显示出来。
或者需要进行碰撞检测的时候,只对附近的物体进行检测。
使用八叉树都可以很大地减少运算量,提升性能。

大体逻辑

如果父节点和给的范围相交,则遍历递归查看子节点哪些相交,直至找到叶子节点,哪些相交。
因为八叉树将场景划分成了八份,给的范围很可能只和其中1份或者2份相交,比如场景有8个堆叠的建筑,玩家很可能只在某个建筑里或者在两个建筑之间的通道里。
确定了和哪个大的空间相交,则必定和其中的一个或者几个小空间相交。
我们的目的是找到具体节点,不是这些虚拟的空间。所以要逐层查找,直至找到最底层的叶子空间,里面包含了1个或者几个具体节点。再对这些具体节点进行是否包含在范围内的判断。
如果父节点的空间直接被包含在了给的范围,则不需要再对子空间进行判断了,直接拿到所有具体节点即可。
然后将所有这些包含在范围内的具体节点打包返回。

核心代码
  //递归函数,每次对子节点进行遍历判断是否相交
  private getNearCubeData(sourcePosition: Vec3, parentOcTree: OcTreeNode, out: CubeData[]) {
      for (let i = 0; i < parentOcTree.children.length; i++) {
        const ocTreeNode = parentOcTree.children[i];
        const { x, y, z } = sourcePosition;
        const { x: ocX, y: ocY, z: ocZ } = ocTreeNode.centerPosition;
        const halfLen = ocTreeNode.sideLength / 2;
        geometry.Sphere.set(this.toolSphere, x, y, z, APPConfig.nearSphereRadius);
        geometry.AABB.set(this.toolAABB, ocX, ocY, ocZ, halfLen, halfLen, halfLen);
        //这里因为是用的cocos引擎,所以直接用了他的几何库。判断球和AABB是否相交
        if (geometry.intersect.sphereAABB(this.toolSphere, this.toolAABB)) {
          if (ocTreeNode.items.length > 0) {
          //将叶子节点的具体元素放入包中
      		out.push(...ocTreeNode.items);
    	  } else if (ocTreeNode.sideLength > APPConfig.ocTreeMinSideLength) {
      		//如果不是叶子节点,且相交的情况,就进行递归查找
      		this.getNearCubeData(sourcePosition, ocTreeNode, out);
    	  }
        }
      }
  }

射线检测

应用场景

当场景里的元素很多,需要用鼠标选中一个的时候,如果对所有元素进行遍历,那运算量是很大的。八叉树就是把问题分成了8份,尽早筛除无用运算。就像机械加工,先进行粗加工,把大块的无用材料,尽快去掉。
也会对元素之间进行距离检测,比如找到最近的敌人自动锁定之类的。

大体逻辑

射线检测和范围检测几乎一致,只是相交判断由范围变成了一条射线。
射线检测也会获得多个结果。看自己需要是不是获取最近的。
如果是鼠标选中,一般会从摄像机位置发送一条经过近裁剪面点击位置的射线。然后就逐节点检测是否相交,直至找到哪些和射线相交的元素。
如果是距离判断,则360度发送射线,比如每1度发送一条射线,依次找到最近的元素,始终保留最近的那个。则走完后就获得了最近的元素。这个肯定要比对所有元素进行一次排序快非常多。

核心代码
//核心递归代码,检测八叉树节点是否与射线相交
  private getRayHitOcTreeNode(ray: geometry.Ray, ocTreeNode: OcTreeNode) {
    if (this.isRayHitOcTreeNode(ray, ocTreeNode)) {
      if (ocTreeNode.items.length) {
      //如果是叶子节点,则判断是否最近元素
        for (let i = 0; i < ocTreeNode.items.length; i++) {
          const item = ocTreeNode.items[i];
          if (this.isRayHitCubeData(ray, item) && item.curCube) {
            if (!this.rayHitNearestItem) {
              this.rayHitNearestItem = item;
            } else {
              if (Vec3.squaredDistance(item.position, ray.o) < Vec3.squaredDistance(this.rayHitNearestItem.position, ray.o)) {
                this.rayHitNearestItem = item;
              }
            }
          }
        }
      } else {
      //如果不是叶子节点且相交,则递归查找子节点的相交情况
        for (let i = 0; i < ocTreeNode.children.length; i++) {
          this.getRayHitOcTreeNode(ray, ocTreeNode.children[i]);
        }
      }
    }
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值