基础知识:
AABB包围盒,也叫轴对称包围盒,意思就是它的六个面总是分别平行XYZ三个轴的
相交计算原理:
计算射线与包围盒每个面的平面的交点,计算这个点是否在包围盒面的范围,在就是相交,不在就是没有相交
图解:
用个2D图形简单讲解一下
首先从图中可以看到射线Ray和红色的包围盒相交了,但是怎么计算出来(毕竟在3D世界中,不可能全靠眼睛去看…)
这个包围盒有4个面,2个平行于Y轴,2个平行于X轴,有两个关键点,最小点min和最大点max(即xy值最小和最大,有正负)
下面是伪代码
//首先是看射线Ray,这条射线的原点是origin,方向是direction,
Ray ray;
if(ray.origin.x < min.x)//原点的X小于min的X,那么测试射线与垂直于X轴的面的相交点。
{
//假设射线的X方向到达min平面的时间为t
float t = (min.x - _origin.x) / _direction.x;
//那么可以计算出射线与平面的交点为hitPoint1
Point hitPoint1 = ray.origin + ray.direction * t;
if(hitPoint1.y> min.y && hitPoint.y < max.y)//比较,是不是在包围盒的面内
{
return true;
}
}
if(ray.origin.y < min.y)//原点的y小于min的y,那么测试射线与垂直于Y轴的面的相交点。
{
//假设射线的Y方向到达min平面的时间为t
float t = (min.y - _origin.y) / _direction.y;
//那么可以计算出射线与平面的交点为hitPoint2
Point hitPoint1 = ray.origin + ray.direction * t;
if(hitPoint1.x> min.x && hitPoint.x < max.x)//比较,是不是在包围盒的面内
{
return true;
}
}
三维空间里也是同样的道理。
下面是OpenGLES三维空间射线与AABB盒相交的计算源码
/**
* 测试射线box相交
* 如果相交,返回值中的first == true.否则false
* second为射线到点的距离
* 调用getPoint方法,则返回交点
*/
std::pair<bool, T> intersects(const AxisAlignedBox<T>& box) const
{
T lowt = 0.0f;
T t;
bool hit = false;
tvec3<T> hitpoint;
tvec3<T> min = box.getMinimum();
tvec3<T> max = box.getMaximum();
/**
* 点在包围盒里面
*/
if ( _origin > min && _origin < max )
{
return std::pair<bool, T>(true, 0);
}
// Check each face in turn, only check closest 3
// Min x
if (_origin.x <= min.x && _direction.x > 0)
{
t = (min.x - _origin.x) / _direction.x;
if (t >= 0)
{
// Substitute t back into ray and check bounds and dist
hitpoint = _origin + _direction * t;
if (hitpoint.y >= min.y &&
hitpoint.y <= max.y &&
hitpoint.z >= min.z &&
hitpoint.z <= max.z &&
(!hit || t < lowt))
{
hit = true;
lowt = t;
}
}
}
// Max x
if (_origin.x >= max.x && _direction.x < 0)
{
t = (max.x - _origin.x) / _direction.x;
if (t >= 0)
{
// Substitute t back into ray and check bounds and dist
hitpoint = _origin + _direction * t;
if (hitpoint.y >= min.y &&
hitpoint.y <= max.y &&
hitpoint.z >= min.z &&
hitpoint.z <= max.z &&
(!hit || t < lowt))
{
hit = true;
lowt = t;
}
}
}
// Min y
if (_origin.y <= min.y && _direction.y > 0)
{
t = (min.y - _origin.y) / _direction.y;
if (t >= 0)
{
// Substitute t back into ray and check bounds and dist
hitpoint = _origin + _direction * t;
if (hitpoint.x >= min.x &&
hitpoint.x <= max.x &&
hitpoint.z >= min.z &&
hitpoint.z <= max.z &&
(!hit || t < lowt))
{
hit = true;
lowt = t;
}
}
}
// Max y
if (_origin.y >= max.y && _direction.y < 0)
{
t = (max.y - _origin.y) / _direction.y;
if (t >= 0)
{
// Substitute t back into ray and check bounds and dist
hitpoint = _origin + _direction * t;
if (hitpoint.x >= min.x &&
hitpoint.x <= max.x &&
hitpoint.z >= min.z &&
hitpoint.z <= max.z &&
(!hit || t < lowt))
{
hit = true;
lowt = t;
}
}
}
// Min z
if (_origin.z <= min.z && _direction.z > 0)
{
t = (min.z - _origin.z) / _direction.z;
if (t >= 0)
{
// Substitute t back into ray and check bounds and dist
hitpoint = _origin + _direction * t;
if (hitpoint.x >= min.x &&
hitpoint.x <= max.x &&
hitpoint.y >= min.y &&
hitpoint.y <= max.y &&
(!hit || t < lowt))
{
hit = true;
lowt = t;
}
}
}
// Max z
if (_origin.z >= max.z && _direction.z < 0)
{
t = (max.z - _origin.z) / _direction.z;
if (t >= 0)
{
// Substitute t back into ray and check bounds and dist
hitpoint = _origin + _direction * t;
if (hitpoint.x >= min.x &&
hitpoint.x <= max.x &&
hitpoint.y >= min.y &&
hitpoint.y <= max.y &&
(!hit || t < lowt))
{
hit = true;
lowt = t;
}
}
}
return std::pair<bool, T>(hit, lowt);
}
};