การตรวจจับการชนกันแบบ 3 มิติที่เร็วที่สุดระหว่างกล่องขอบเขตสองตัว (OBBs)


10

ฉันอยู่ในจุดที่เกมของฉันที่ฉันต้องการเพิ่มระบบการปะทะกัน ฉันลอง jBullet และในขณะที่ใช้งานได้มันก็ไม่ใช่สิ่งที่ฉันกำลังมองหา ฉันแค่ต้องการวิธีง่ายๆในการทดสอบว่าต้นไม้สองเส้นที่มุ่งเน้นกล่อง (OBB) ชนกันหรือไม่

ฉันจะไปชนกันโดยใช้ต้นไม้ สร้าง AABB สำหรับบรอดเฟสจากนั้นถ้าผ่านการทดสอบว่า OBB แต่ละต้นในต้นไม้ชนกับต้นไม้อื่นหรือไม่

ฉันพบบางสิ่งบนอินเทอร์เน็ต แต่ฉันไม่สามารถเข้าใจได้อย่างสมบูรณ์ สิ่งที่ฉันขอคือเว็บไซต์หรือแหล่งข้อมูลที่อธิบายการชนแบบ 3D OBB ได้ดี?

ฉันได้เรียนรู้ว่า GJK นั้นเร็วกว่า SAT และดูเหมือนจะสามารถบอกฉันได้ว่ากล่องนั้นทะลุกันได้ไกลแค่ไหน ฉันพบบางสิ่งที่ GJK แต่พวกเขาไม่ใช่กล่อง สิ่งที่ซับซ้อนและสับสนแทน

ฉันแค่ต้องการสร้าง OBB จาก 3 เวกเตอร์: ศูนย์กลางขนาดและการหมุนของแต่ละแกน จากนั้นสามารถทดสอบการชนกับสิ่งเหล่านั้นได้ ขอบคุณล่วงหน้าสำหรับทุกสิ่งที่คุณโพสต์


คุณแน่ใจหรือไม่ว่าคุณต้องการขอบเขตกล่อง? หากคุณมีต้นไม้แห่งกระดูกและคุณมีศูนย์กลางและความคิดคร่าวๆเกี่ยวกับปริมาตรของกระดูกแต่ละชิ้นทรงกลมที่ล้อมรอบนั้นง่ายกว่าและเร็วกว่ามากในการนำไปใช้
johnwbyrd

คำตอบ:


0

ฉันเห็นลิงก์ในความคิดเห็นของคุณที่แสดงให้เห็นตัวละครที่มี OBB ที่ถูกกล่าวหาว่าเป็นตาข่าย บางครั้งสามารถทำได้กับขอบเขตทรงกลมแล้วไม่มีปัญหาการปฐมนิเทศและการทดสอบทรงกลมมักจะเร็วกว่า

ตัวละครของคุณถ้าแสดงโครงสร้างขอบเขตเช่นในการเชื่อมโยงของคุณจะมีลักษณะเหมือนมนุษย์มิชลิน


0

นี่คือตัวอย่างการทำงานจริงของ AABB ซึ่งมาจากเอ็นจิ้นเกมของฉันโดยตรง:

using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;

namespace GrimoireEngine.Framework.Maths
{
    public struct BoundingBox : IEquatable<BoundingBox>
    {
        public Vector3 Min;
        public Vector3 Max;

        public const int CornerCount = 8;

        public static Vector3 MaxVector3
        {
            get
            {
                return new Vector3(float.MaxValue);
            }
        }

        public static Vector3 MinVector3
        {
            get
            {
                return new Vector3(float.MinValue);
            }
        }

        public static BoundingBox Identity
        {
            get
            {
                return new BoundingBox(Vector3.Zero, Vector3.One);
            }
        }

        public static BoundingBox Zero
        {
            get
            {
                return new BoundingBox();
            }
        }

        public BoundingBox(Vector3 min, Vector3 max)
        {
            Min = min;
            Max = max;
        }

        public BoundingBox(
            float minX, float minY, float minZ,
            float maxX, float maxY, float maxZ)
            : this(
                new Vector3(minX, minY, minZ),
                new Vector3(maxX, maxY, maxZ))
        { }

        public bool Collides(BoundingBox box)
        {
            if (box.Max.X < Min.X
                || box.Min.X > Max.X
                || box.Max.Y < Min.Y
                || box.Min.Y > Max.Y
                || box.Max.Z < Min.Z
                || box.Min.Z > Max.Z)
            {
                return false;
            }
            if (box.Min.X >= Min.X
                && box.Max.X <= Max.X
                && box.Min.Y >= Min.Y
                && box.Max.Y <= Max.Y
                && box.Min.Z >= Min.Z
                && box.Max.Z <= Max.Z)
            {
                return true;
            }
            return true;
        }

        public ContainmentType Contains(BoundingBox box)
        {
            if (box.Max.X < Min.X
                || box.Min.X > Max.X
                || box.Max.Y < Min.Y
                || box.Min.Y > Max.Y
                || box.Max.Z < Min.Z
                || box.Min.Z > Max.Z)
            {
                return ContainmentType.Disjoint;
            }
            if (box.Min.X >= Min.X
                && box.Max.X <= Max.X
                && box.Min.Y >= Min.Y
                && box.Max.Y <= Max.Y
                && box.Min.Z >= Min.Z
                && box.Max.Z <= Max.Z)
            {
                return ContainmentType.Contains;
            }
            return ContainmentType.Intersects;
        }

        public bool Collides(BoundingFrustum frustum)
        {
            int i;
            bool contained;
            Vector3[] corners = frustum.GetCorners();
            for (i = 0; i < corners.Length; i++)
            {
                if (corners[i].X < Min.X
                    || corners[i].X > Max.X
                    || corners[i].Y < Min.Y
                    || corners[i].Y > Max.Y
                    || corners[i].Z < Min.Z
                    || corners[i].Z > Max.Z)
                {
                    contained = false;
                }
                else if (corners[i].X == Min.X
                         || corners[i].X == Max.X
                         || corners[i].Y == Min.Y
                         || corners[i].Y == Max.Y
                         || corners[i].Z == Min.Z
                         || corners[i].Z == Max.Z)
                {
                    contained = true;
                }
                else
                {
                    contained = true;
                }
                if (contained == false)
                {
                    break;
                }
            }
            if (i == corners.Length)
            {
                return true;
            }
            if (i != 0)
            {
                return true;
            }
            i++;
            for (; i < corners.Length; i++)
            {
                if (corners[i].X < Min.X
                    || corners[i].X > Max.X
                    || corners[i].Y < Min.Y
                    || corners[i].Y > Max.Y
                    || corners[i].Z < Min.Z
                    || corners[i].Z > Max.Z)
                {
                    contained = false;
                }
                else if (corners[i].X == Min.X
                         || corners[i].X == Max.X
                         || corners[i].Y == Min.Y
                         || corners[i].Y == Max.Y
                         || corners[i].Z == Min.Z
                         || corners[i].Z == Max.Z)
                {
                    contained = true;
                }
                else
                {
                    contained = true;
                }
                if (contained != true)
                {
                    return true;
                }
            }
            return true;
        }

        public ContainmentType Contains(BoundingFrustum frustum)
        {
            int i;
            ContainmentType contained;
            Vector3[] corners = frustum.GetCorners();
            for (i = 0; i < corners.Length; i++)
            {
                if (corners[i].X < Min.X
                    || corners[i].X > Max.X
                    || corners[i].Y < Min.Y
                    || corners[i].Y > Max.Y
                    || corners[i].Z < Min.Z
                    || corners[i].Z > Max.Z)
                {
                    contained = ContainmentType.Disjoint;
                }
                else if (corners[i].X == Min.X
                         || corners[i].X == Max.X
                         || corners[i].Y == Min.Y
                         || corners[i].Y == Max.Y
                         || corners[i].Z == Min.Z
                         || corners[i].Z == Max.Z)
                {
                    contained = ContainmentType.Intersects;
                }
                else
                {
                    contained = ContainmentType.Contains;
                }
                if (contained == ContainmentType.Disjoint)
                {
                    break;
                }
            }
            if (i == corners.Length)
            {
                return ContainmentType.Contains;
            }
            if (i != 0)
            {
                return ContainmentType.Intersects;
            }
            i++;
            for (; i < corners.Length; i++)
            {
                if (corners[i].X < Min.X
                    || corners[i].X > Max.X
                    || corners[i].Y < Min.Y
                    || corners[i].Y > Max.Y
                    || corners[i].Z < Min.Z
                    || corners[i].Z > Max.Z)
                {
                    contained = ContainmentType.Disjoint;
                }
                else if (corners[i].X == Min.X
                         || corners[i].X == Max.X
                         || corners[i].Y == Min.Y
                         || corners[i].Y == Max.Y
                         || corners[i].Z == Min.Z
                         || corners[i].Z == Max.Z)
                {
                    contained = ContainmentType.Intersects;
                }
                else
                {
                    contained = ContainmentType.Contains;
                }
                if (contained != ContainmentType.Contains)
                {
                    return ContainmentType.Intersects;
                }
            }
            return ContainmentType.Contains;
        }

        public bool Collides(BoundingSphere sphere)
        {
            if (sphere.Center.X - Min.X >= sphere.Radius
                && sphere.Center.Y - Min.Y >= sphere.Radius
                && sphere.Center.Z - Min.Z >= sphere.Radius
                && Max.X - sphere.Center.X >= sphere.Radius
                && Max.Y - sphere.Center.Y >= sphere.Radius
                && Max.Z - sphere.Center.Z >= sphere.Radius)
            {
                return true;
            }
            double dmin = 0;
            double e = sphere.Center.X - Min.X;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return false;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.X - Max.X;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return false;
                    }
                    dmin += e * e;
                }
            }
            e = sphere.Center.Y - Min.Y;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return false;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.Y - Max.Y;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return false;
                    }
                    dmin += e * e;
                }
            }
            e = sphere.Center.Z - Min.Z;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return false;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.Z - Max.Z;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return false;
                    }
                    dmin += e * e;
                }
            }
            return dmin <= sphere.Radius * sphere.Radius;
        }

        public ContainmentType Contains(BoundingSphere sphere)
        {
            if (sphere.Center.X - Min.X >= sphere.Radius
                && sphere.Center.Y - Min.Y >= sphere.Radius
                && sphere.Center.Z - Min.Z >= sphere.Radius
                && Max.X - sphere.Center.X >= sphere.Radius
                && Max.Y - sphere.Center.Y >= sphere.Radius
                && Max.Z - sphere.Center.Z >= sphere.Radius)
            {
                return ContainmentType.Contains;
            }
            double dmin = 0;
            double e = sphere.Center.X - Min.X;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return ContainmentType.Disjoint;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.X - Max.X;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return ContainmentType.Disjoint;
                    }
                    dmin += e * e;
                }
            }
            e = sphere.Center.Y - Min.Y;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return ContainmentType.Disjoint;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.Y - Max.Y;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return ContainmentType.Disjoint;
                    }
                    dmin += e * e;
                }
            }
            e = sphere.Center.Z - Min.Z;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return ContainmentType.Disjoint;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.Z - Max.Z;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return ContainmentType.Disjoint;
                    }
                    dmin += e * e;
                }
            }
            return dmin <= sphere.Radius * sphere.Radius ? ContainmentType.Intersects : ContainmentType.Disjoint;
        }

        public bool Collides(Vector3 point)
        {
            if (point.X < Min.X
                || point.X > Max.X
                || point.Y < Min.Y
                || point.Y > Max.Y
                || point.Z < Min.Z
                || point.Z > Max.Z)
            {
                return false;
            }
            if (point.X == Min.X
                || point.X == Max.X
                || point.Y == Min.Y
                || point.Y == Max.Y
                || point.Z == Min.Z
                || point.Z == Max.Z)
            {
                return true;
            }
            return true;
        }

        public ContainmentType Contains(Vector3 point)
        {
            ContainmentType result;
            if (point.X < Min.X
                || point.X > Max.X
                || point.Y < Min.Y
                || point.Y > Max.Y
                || point.Z < Min.Z
                || point.Z > Max.Z)
            {
                result = ContainmentType.Disjoint;
            }
            else if (point.X == Min.X
                     || point.X == Max.X
                     || point.Y == Min.Y
                     || point.Y == Max.Y
                     || point.Z == Min.Z
                     || point.Z == Max.Z)
            {
                result = ContainmentType.Intersects;
            }
            else
            {
                result = ContainmentType.Contains;
            }
            return result;
        }

        public bool Equals(BoundingBox other)
        {
            return (Min == other.Min) && (Max == other.Max);
        }

        public override bool Equals(object obj)
        {
            return (obj is BoundingBox) && Equals((BoundingBox)obj);
        }

        public Vector3[] GetCorners()
        {
            return new[] {
                new Vector3(Min.X, Max.Y, Max.Z),
                new Vector3(Max.X, Max.Y, Max.Z),
                new Vector3(Max.X, Min.Y, Max.Z),
                new Vector3(Min.X, Min.Y, Max.Z),
                new Vector3(Min.X, Max.Y, Min.Z),
                new Vector3(Max.X, Max.Y, Min.Z),
                new Vector3(Max.X, Min.Y, Min.Z),
                new Vector3(Min.X, Min.Y, Min.Z)
            };
        }

        public override int GetHashCode()
        {
            return Min.GetHashCode() + Max.GetHashCode();
        }

        public bool Intersects(BoundingBox box)
        {
            if ((Max.X >= box.Min.X) && (Min.X <= box.Max.X))
            {
                if ((Max.Y < box.Min.Y) || (Min.Y > box.Max.Y))
                {
                    return false;
                }
                return (Max.Z >= box.Min.Z) && (Min.Z <= box.Max.Z);
            }
            return false;
        }

        public bool Intersects(BoundingFrustum frustum)
        {
            return frustum.Intersects(this);
        }

        public bool Intersects(BoundingSphere sphere)
        {
            if (sphere.Center.X - Min.X > sphere.Radius
                && sphere.Center.Y - Min.Y > sphere.Radius
                && sphere.Center.Z - Min.Z > sphere.Radius
                && Max.X - sphere.Center.X > sphere.Radius
                && Max.Y - sphere.Center.Y > sphere.Radius
                && Max.Z - sphere.Center.Z > sphere.Radius)
            {
                return true;
            }
            double dmin = 0;
            if (sphere.Center.X - Min.X <= sphere.Radius)
            {
                dmin += (sphere.Center.X - Min.X) * (sphere.Center.X - Min.X);
            }
            else if (Max.X - sphere.Center.X <= sphere.Radius)
            {
                dmin += (sphere.Center.X - Max.X) * (sphere.Center.X - Max.X);
            }
            if (sphere.Center.Y - Min.Y <= sphere.Radius)
            {
                dmin += (sphere.Center.Y - Min.Y) * (sphere.Center.Y - Min.Y);
            }
            else if (Max.Y - sphere.Center.Y <= sphere.Radius)
            {
                dmin += (sphere.Center.Y - Max.Y) * (sphere.Center.Y - Max.Y);
            }
            if (sphere.Center.Z - Min.Z <= sphere.Radius)
            {
                dmin += (sphere.Center.Z - Min.Z) * (sphere.Center.Z - Min.Z);
            }
            else if (Max.Z - sphere.Center.Z <= sphere.Radius)
            {
                dmin += (sphere.Center.Z - Max.Z) * (sphere.Center.Z - Max.Z);
            }
            return dmin <= sphere.Radius * sphere.Radius;
        }

        public PlaneIntersectionType Intersects(Plane plane)
        {
            Vector3 positiveVertex;
            Vector3 negativeVertex;
            if (plane.Normal.X >= 0)
            {
                positiveVertex.X = Max.X;
                negativeVertex.X = Min.X;
            }
            else
            {
                positiveVertex.X = Min.X;
                negativeVertex.X = Max.X;
            }
            if (plane.Normal.Y >= 0)
            {
                positiveVertex.Y = Max.Y;
                negativeVertex.Y = Min.Y;
            }
            else
            {
                positiveVertex.Y = Min.Y;
                negativeVertex.Y = Max.Y;
            }
            if (plane.Normal.Z >= 0)
            {
                positiveVertex.Z = Max.Z;
                negativeVertex.Z = Min.Z;
            }
            else
            {
                positiveVertex.Z = Min.Z;
                negativeVertex.Z = Max.Z;
            }
            float distance = plane.Normal.X * negativeVertex.X + plane.Normal.Y * negativeVertex.Y + plane.Normal.Z * negativeVertex.Z + plane.D;
            if (distance > 0)
            {
                return PlaneIntersectionType.Front;
            }
            distance = plane.Normal.X * positiveVertex.X + plane.Normal.Y * positiveVertex.Y + plane.Normal.Z * positiveVertex.Z + plane.D;
            if (distance < 0)
            {
                return PlaneIntersectionType.Back;
            }
            return PlaneIntersectionType.Intersecting;
        }

        public float? Intersects(Ray ray)
        {
            return ray.Intersects(this);
        }

        public static bool operator ==(BoundingBox a, BoundingBox b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(BoundingBox a, BoundingBox b)
        {
            return !a.Equals(b);
        }

        public override string ToString()
        {
            return "{{Min:" + Min + " Max:" + Max + "}}";
        }

        public void DrawImmediate()
        {
            GL.Begin(PrimitiveType.LineLoop);
            GL.Vertex3(Max.X, Max.Y, Min.Z);
            GL.Vertex3(Min.X, Max.Y, Min.Z);
            GL.Vertex3(Min.X, Min.Y, Min.Z);
            GL.Vertex3(Max.X, Min.Y, Min.Z);
            GL.End();
            GL.Begin(PrimitiveType.LineLoop);
            GL.Vertex3(Max.X, Min.Y, Max.Z);
            GL.Vertex3(Max.X, Max.Y, Max.Z);
            GL.Vertex3(Min.X, Max.Y, Max.Z);
            GL.Vertex3(Min.X, Min.Y, Max.Z);
            GL.End();
            GL.Begin(PrimitiveType.LineLoop);
            GL.Vertex3(Max.X, Max.Y, Min.Z);
            GL.Vertex3(Max.X, Max.Y, Max.Z);
            GL.Vertex3(Min.X, Max.Y, Max.Z);
            GL.Vertex3(Min.X, Max.Y, Min.Z);
            GL.End();
            GL.Begin(PrimitiveType.LineLoop);
            GL.Vertex3(Max.X, Min.Y, Max.Z);
            GL.Vertex3(Min.X, Min.Y, Max.Z);
            GL.Vertex3(Min.X, Min.Y, Min.Z);
            GL.Vertex3(Max.X, Min.Y, Min.Z);
            GL.End();
        }
    }
}

เพียงลบเมธอด Bounding อื่น ๆ


3
นี่ไม่เหมือนกับ OOB สำหรับฉัน แต่ AABB ซึ่งดูเหมือนจะไม่ใช่สิ่งที่ OP กำลังมองหา?
Tyyppi_77

@ Tyyppi_77 ชื่อคำถามอ่าน "อัลกอริทึมกล่องกระโดดเร็วที่สุด" นี่เป็นปัญหาของคำถามที่คลุมเครือ
Krythic

3
ดูเหมือนว่าคำถามจะบ่งบอกอย่างชัดเจนว่า OP กำลังถามเกี่ยวกับ OOBs: "ฉันแค่ต้องการสร้าง OBB"
Tyyppi_77

-2

Molly Rocket เป็นเพื่อนของคุณตลอดไป

http://mollyrocket.com/849

แต่ดูเหมือนว่าคุณเข้าใจผิดเกี่ยวกับการใช้งานกล่องแบบ จำกัด คุณไม่ได้ใช้มันสำหรับระบบการชนของฟิสิกส์ โดยเฉพาะอย่างยิ่งเมื่อมันไม่มีประสิทธิภาพอย่างมากสำหรับการใช้งานประเภทนั้น

บางทีคุณอาจคิดถึงคิวรีการชนกันของฉากกราฟใช่ไหม ที่ที่คุณตรวจสอบว่าวัตถุเข้าสู่ QuadTree หรือ Octree หรือไม่และคุณสร้างกราฟขึ้นใหม่อย่างรวดเร็ว


ขออภัยถ้าฉันเข้าใจผิด วิธีที่ฉันคิดว่าจะทำการชนกันคือการให้กระดูกแต่ละชิ้นบนตาข่ายมีช่อง จำกัด ซึ่งแต่ละอันจะเคลื่อนไหวไปพร้อมกับเมทริกซ์ของกระดูก ตัวอย่างเช่นการเชื่อมโยง ดูเหมือนว่าจะเร็วกว่าการปะทะกันของการเล็มขนโดยเฉพาะอย่างยิ่งถ้าตาข่ายจะมีการเคลื่อนไหวของโครงกระดูก ฉันจะไม่ได้มีฟิสิกส์ที่ซับซ้อนเพียงโทรกลับชนง่าย ๆ เพื่อแจ้งการชนกันระหว่างตาข่าย
andrew3ds

1
คำตอบของคุณขึ้นอยู่กับลิงก์ภายนอกเป็นอย่างมาก ฉันขอแนะนำให้คุณอัปเดตคำตอบเพื่อรวมข้อมูลที่เกี่ยวข้องที่นี่ในกรณีที่ลิงก์หยุดทำงานในบางวัน
MichaelHouse

โอ้ กล่องตีเป็นสิ่งที่คุณต้องการ กล่องที่ได้รับผลกระทบไม่จำเป็นต้องมีต้นไม้และไม่ใช่ AAB จริงๆ โดยทั่วไปพวกมันจะมองไม่เห็นการชนกันของตาข่ายที่ติดอยู่กับกระดูก คลังฟิสิกส์ที่คุณใช้ก่อนหน้านี้สามารถช่วยคุณทำสิ่งนี้ได้อย่างง่ายดายเช่นกัน แต่ใช่ GTK ยังคงสามารถทำงานได้ดีในระบบประเภทนี้ โดยเฉพาะถ้าคุณอยากรู้ว่าทำอะไร
moonshineTheleocat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.