碰撞检测之Ray-Box检测

Posted 拳四郎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了碰撞检测之Ray-Box检测相关的知识,希望对你有一定的参考价值。

Separating Axis Theorem(分离轴理论)

在学习Ray-Box检测之前,首先来学习一些这个分离轴理论!

先说二维情况,一句话

Two convex polygons do not intersect if and only if there exists a line such that the projections of the two polygons onto the line do not intersect.

咳咳,翻译一下

两个凸包多边形,当且仅当存在一条线,这两个多边形在这条线上的投影不相交,则这两个多边形也不相交, 如下图所示

技术分享



将多边形换成多面体,线变成面,就变成了三维空间中的分离轴了。


对于矩形,假设出现碰撞的情况,则存在分离轴平行矩形的一条边。(这个后面会证明)


Ray - Rect检测

在到三维之前,还是来看二维的情况,也就是Ray-Rect检测。

假定Rect的中心为原点,所以就是下面这样

技术分享


首先要面对的一个问题就是射线的原点是否在矩形的内部,这里就用到了分离轴的定理。

将矩形投影到对应的轴上,如果没有和射线原点的投影重合,那么就不在矩形里面。


接下来判断是否相交,这里提到了一个简单 slab method,简单说来,首先将矩形的四条边无限延伸,那么整个平面就被矩形分割成了几个部分,用这个”井字“去切割射线,如果得得到的线段在矩形内,那么就相交了。如下图所示,绿色的射线是相交的,红色的没有相交。

技术分享


简单的代码

bool intersection(box b, ray r) {
    double tmin = -INFINITY, tmax = INFINITY;
 
    if (ray.n.x != 0.0) {
        double tx1 = (b.min.x - r.x0.x)/r.n.x;
        double tx2 = (b.max.x - r.x0.x)/r.n.x;
 
        tmin = max(tmin, min(tx1, tx2));
        tmax = min(tmax, max(tx1, tx2));
    }
 
    if (ray.n.y != 0.0) {
        double ty1 = (b.min.y - r.x0.y)/r.n.y;
        double ty2 = (b.max.y - r.x0.y)/r.n.y;
 
        tmin = max(tmin, min(ty1, ty2));
        tmax = min(tmax, max(ty1, ty2));
    }
 
    return tmax >= tmin;
}


三维空间

直接贴代码了。

加了坐标系的转换,代码是参考PhysX里优化过的代码,但原理基本不变。

  public static bool Raycast(Ray ray, float distance, Box box, out RaycastHitInfo hitInfo)
        {
            Quaternion inverRot = Quaternion.Inverse(box.rotation);
            Vector3 origin = ray.origin - box.center;

            Vector3 localOrigin = inverRot * origin;
            Vector3 localDir = inverRot * ray.direction;
            Ray localRay = new Ray(localOrigin, localDir);

            if (!IntersectRayAABB(localRay, distance, 0.5f * box.extents, out hitInfo))
            {
                return false;
            }
            hitInfo.normal = box.rotation * hitInfo.normal;
            hitInfo.point = box.rotation * hitInfo.point + box.center;
            return true;
        }

        public static bool IntersectRayAABB(Ray ray, float distance, Vector3 dimension, out RaycastHitInfo hitInfo)
        {
            float RAYAABB_EPSILON = 0.00001f;
            hitInfo = new RaycastHitInfo();
            Vector3 minPos = -dimension;
            Vector3 maxPos = dimension;
            Vector3 maxT = -Vector3.one;
            bool isInside = true;

            for (int i = 0; i < 3; i++)
            {
                if (ray.origin[i] < minPos[i])
                {
                    hitInfo.point[i] = minPos[i];
                    isInside = false;
                    if (ray.direction[i] != 0)
                        maxT[i] = (minPos[i] - ray.origin[i]) / ray.direction[i];
                }
                else if (ray.origin[i] > maxPos[i])
                {
                    hitInfo.point[i] = maxPos[i];
                    isInside = false;
                    if (ray.direction[i] != 0)
                        maxT[i] = (maxPos[i] - ray.origin[i]) / ray.direction[i];
                }
            }

            // Ray origin inside bounding box
            if (isInside)
            {
                hitInfo.point = ray.origin;
                hitInfo.distance = 0;
                hitInfo.normal = -ray.direction;
                return true;
            }

            // Get largest of the maxT‘s for final choice of intersection
            int whichPlane = 0;
            if (maxT[1] > maxT[whichPlane]) whichPlane = 1;
            if (maxT[2] > maxT[whichPlane]) whichPlane = 2;

            //Ray distance large than ray cast ditance
            if (maxT[whichPlane] > distance)
            { return false; }

            // Check final candidate actually inside box
            for (int i = 0; i < 3; i++)
            {
                if (i != whichPlane)
                {
                    hitInfo.point[i] = ray.origin[i] + maxT[whichPlane] * ray.direction[i];
                    if (hitInfo.point[i] < minPos[i] - RAYAABB_EPSILON || hitInfo.point[i] > maxPos[i] + RAYAABB_EPSILON)
                        return false;
                    //	if (hitInfo.point[i] < minPos[i] || hitInfo.point[i] > maxPos[i])
                    //	return false;
                }
            }

            hitInfo.distance = maxT[whichPlane];
            Vector3 normal = Vector3.zero;
            normal[whichPlane] = (hitInfo.point[whichPlane] > 0) ? 1 : -1;
            hitInfo.normal = normal;
            return true;
        }


测试代码

public class RayBoxTester : MonoBehaviour {

    public GameObject box;
    Box _box;
    // Use this for initialization
    void Start () {
        _box = new Box(Vector3.zero, Vector3.one, Quaternion.identity);
    }
	
	// Update is called once per frame
	void Update () {
        //Ray OBB test.
        Ray ray2 = new Ray(Vector3.zero, new Vector3(1, 1, 1));
        RaycastHitInfo hitinfo2;
        //ray2.origin = rayOrigin.transform.position;
        float castDistance = 10f;

        _box.center = box.transform.position;
        _box.extents = box.transform.localScale;
        _box.rotation = box.transform.rotation;
        if (NRaycastTests.Raycast(ray2, castDistance, _box, out hitinfo2))
        {
            Debug.DrawLine(ray2.origin, ray2.origin + ray2.direction * hitinfo2.distance, Color.red, 0, false);
            Debug.DrawLine(hitinfo2.point, hitinfo2.point + hitinfo2.normal, Color.green, 0, false);
        }
        else
        {
            Debug.DrawLine(ray2.origin, ray2.origin + ray2.direction * castDistance, Color.blue, 0, false);
        }
    }
}


结果

技术分享       技术分享


收工。


参考

FAST, BRANCHLESS RAY/BOUNDING BOX INTERSECTIONS  - https://tavianator.com/fast-branchless-raybounding-box-intersections/

Hyperplane_separation_theorem - https://en.wikipedia.org/wiki/Hyperplane_separation_theorem

Ray - Box Intersection  - http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm

PhysX 3.3  source code

以上是关于碰撞检测之Ray-Box检测的主要内容,如果未能解决你的问题,请参考以下文章

碰撞检测之Sphere-Box检测

碰撞检测之Ray-Sphere检测

碰撞检测之Box-Box检测

pygame之精灵碰撞检测

AEJoy —— 表达式之碰撞检测JS

2022-04-20 Unity入门7——物理系统之碰撞检测