表达方法
AABB内的点满足不等式:\(\begin{align}x_{min}\leq x\leq x_{max}\\y_{min}\leq y\leq y_{max}\\z_{min}\leq z\leq z_{max}\end{align}\)
顶点:\(\begin{align} p_{min} = \begin{bmatrix} x_{min} & y_{min} & z_{min} \end{bmatrix}\\ p_{max} = \begin{bmatrix} x_{max} & y_{max} & z_{max} \end{bmatrix} \end{align}\)
中心点:\(c=(p_{min}+p_{max})/2\)
尺寸向量:\(s=p_{max}-p_{min}\),尺寸向量为\(p_{min}\)指向\(p_{max}\)的向量,包含矩形边界框的长、宽、高
半径向量:\(r=p_{max}-c=s/2\),半径向量为中心指向\(p_{max}\)的向量
明确定义一个AABB只需要\(p_{min}\),\(p_{max}\),\(c\),\(s\),\(r\)这5个向量中的两个(\(s\)和\(r\)不能配对,其它任意两个都可以配对)
算法实现
计算AABB
struct Vec3 {
float x, y, z;
};
struct AABB {
Vec3 min;
Vec3 max;
};
AABB ComputeAABB(const std::vector<Vec3>& vertices) {
Vec3 min = vertices[0];
Vec3 max = vertices[0];
for (const auto& v : vertices) {
if (v.x < min.x) min.x = v.x;
if (v.y < min.y) min.y = v.y;
if (v.z < min.z) min.z = v.z;
if (v.x > max.x) max.x = v.x;
if (v.y > max.y) max.y = v.y;
if (v.z > max.z) max.z = v.z;
}
return { min, max };
}
判断点与AABB是否相交
bool PointInsideAABB(const Vec3& point, const AABB& box) {
return (point.x >= box.min.x && point.x <= box.max.x) &&
(point.y >= box.min.y && point.y <= box.max.y) &&
(point.z >= box.min.z && point.z <= box.max.z);
}
判断两个AABB是否相交
bool AABBOverlap(const AABB& a, const AABB& b) {
return (a.min.x <= b.max.x && a.max.x >= b.min.x) &&
(a.min.y <= b.max.y && a.max.y >= b.min.y) &&
(a.min.z <= b.max.z && a.max.z >= b.min.z);
}
判断射线与AABB是否相交
射线:\(Ray(t)=origin+t\times dir(t\geq0)\)
由射线表达式可以得到\(t=\frac{origin-Ray(t)}{dir}\),其中\(Ray(t)\)可由\(p_{min}\)和\(p_{max}\)获得,使用\(Slab Method\)算法求出每个轴所对应的相交区间\([t_{min},t_{max}]\), 射线与AABB相交的条件是:所有三个轴的相交区间有公共部分。
struct Vec3 {
float x, y, z;
float& operator[](int i) { return *((&x) + i); }
const float& operator[](int i) const { return *((&x) + i); }
Vec3 operator+(const Vec3& rhs) const {
return { x + rhs.x, y + rhs.y, z + rhs.z };
}
Vec3 operator*(float s) const {
return { x * s, y * s, z * s };
}
};
bool RayIntersectsAABB(
const Vec3& origin,
const Vec3& dir,
const Vec3& boxMin,
const Vec3& boxMax,
Vec3& enterPoint,
Vec3& exitPoint)
{
float tmin = -std::numeric_limits<float>::infinity();
float tmax = std::numeric_limits<float>::infinity();
for (int i = 0; i < 3; ++i) {
if (dir[i] == 0.0f) {
if (origin[i] < boxMin[i] || origin[i] > boxMax[i])
return false; // 平行且在外面
} else {
float t1 = (boxMin[i] - origin[i]) / dir[i];
float t2 = (boxMax[i] - origin[i]) / dir[i];
if (t1 > t2) std::swap(t1, t2);
tmin = std::max(tmin, t1);
tmax = std::min(tmax, t2);
if (tmin > tmax) return false;
if (tmax < 0) return false;
}
}
enterPoint = origin + dir * tmin;
exitPoint = origin + dir * tmax;
return true;
}
浙公网安备 33010602011771号