线段和AABB包围盒求交

线段和AABB包围盒求交

本文介绍线段和AABB包围盒相交计算的算法。

概念解释

AABB包围盒:AABB(Axis-Aligned Bounding Box,轴对齐包围盒)是一种常用于计算机图形学和物理模拟中的简单包围体。其边缘与坐标轴对齐,即它的面是平行于坐标轴的。这使得计算变得简单,因为你只需要考虑最小和最大坐标。通常一个AABB包围盒会用其最小坐标点和最大坐标点表示。
   OBB包围盒:OBB(Oriented Bounding Box,定向包围盒)是一种更灵活的长方体包围盒,用于包围三维物体。与 AABB(轴对齐包围盒)不同,OBB 允许包围盒的面与坐标轴成任意角度。

平板法

算法思想

平板法来源自Cyrus-Beck的直线裁剪法。其中,《Real-Time Render》一书中对其进行了扩展,使其能够处理更加一般的OBB包围盒。同样地,这种方法也适用于AABB包围盒,因为AABB包围盒是一种OBB包围盒的一种特殊形式。一个OBB包围盒可以看作是有三个slab(由一组平行平面划分出的控件区域)组成的区域。
  计算时,用线段起点O和线段方向D表示线段所在的射线。对于包围盒的每一个slab来说,利用线段所在的射线与slab的两个面进行求交计算。对于每一个slab来说,如果射线与slab的两个面相交,则有以下两个交点(如果平行,则认为t分别为正负无穷大):

对于组成包围盒的三个slab来说,取所有tmin中的最大值为Tmin和tmax中的最小值为Tmax。如果Tmin<=Tmax则认为射线所在的直线与包围盒相交,反之则不相交。其中,Tmin表示的点为射线方向上的最近交点,Tmax表示的点为射线方向上的最远交点。进而,我们判断交点是否在线段上确定线段和包围盒的位置关系。算法思想在二维上的示意图如下所示:

  **说明:**左侧展示了由两个slab构成的OBB包围盒,右侧表示了两条射线。其中,左侧射线满足Tmin<Tmax;右侧的射线不满足相交条件。

算法实现

对AABB包围盒和线段的定义如下:

struct Box{
    vec3 max;
    vec3 min;
};

struct Ray{
    vec3 start;
    vec3 dir;
    vec3 inv_d; // 方向的倒数,注意方向分量为0时候的运算
    float length;
};

对平板法求交计算的实现如下:

enum class IntersectionType {
    NONE,
    INTERSECT,
    INCLUDE,
};

// 注意平行时的交点t计算
IntersectionType intersect(Box& box, Ray& ray){
    auto boxMax = box.max;
    auto boxMin = box.min;

    float tmin = (boxMin.x - ray.start.x) * ray.inv_d.x;
    float tmax = (boxMax.x - ray.start.x) * ray.inv_d.x;
    if(tmin > tmax){
        std::swap(tmin, tmax);
    }

    float tymin = (boxMin.y - ray.start.y) * ray.inv_d.y;
    float tymax = (boxMax.y - ray.start.y) * ray.inv_d.y;
    if(tymin > tymax){
        std::swap(tymin, tymax);
    }

    if((tmin > tymax) || (tymin > tmax)){
        return IntersectionType::NONE;
    }

    tmin = std::fmax(tymin, tmin);
    tmax = std::fmin(tymax, tmax);

    float tzmin = (boxMin.z - ray.start().z) * ray.inv_d.z;
    float tzmax = (boxMax.z - ray.start().z) * ray.inv_d.z;
    if(tzmin > tzmax){
        std::swap(tzmin, tzmax);
    }

    if((tmin > tzmax) || (tzmin > tmax)){
        return IntersectionType::NONE;
    }

    // 记录交点,t表示交点到起始点的距离
    tmin = std::fmax(tzmin, tmin);
    tmax = std::fmin(tzmax, tmax);
    auto inRange = [](float value, float left, float right){
        return (value >= left) && (value <= right);
    };

    // 只要有一个点在线上就认为交中
    if (inRange(tmin, 0.0f, ray.length) || inRange(tmax, 0.0f, ray.length)){
        return IntersectionType::INTERSECT;
    } else if(tmin < 0.0f && tmax > ray.length){
        return IntersectionType::INCLUDE;
    }

    return IntersectionType::NONE;
}

效果展示

使用OpenGL绘制AABB包围盒和线段进行测试,效果如下所示:
  1.不相交:box显示为绿色;

  2.相交:box显示为蓝色;

  3.Box包含线段:box显示为红色;

  可执行程序戳这里下载体验。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值