सबसे कुशल AABB बनाम रे टक्कर एल्गोरिदम


53

AABB बनाम रे टक्कर का पता लगाने के लिए एक ज्ञात 'सबसे कुशल' एल्गोरिथ्म है?

मैंने हाल ही में Arvo के AABB बनाम स्फियर टकराव एल्गोरिथ्म के बीच ठोकर खाई है, और मैं सोच रहा हूं कि क्या इसके लिए समान रूप से उल्लेखनीय एल्गोरिथ्म है।

इस एल्गोरिथ्म के लिए एक शर्त यह होनी चाहिए कि मुझे किरण की उत्पत्ति से टक्कर के बिंदु तक की दूरी के लिए परिणाम को क्वेरी करने का विकल्प होना चाहिए। यह कहा जा रहा है, अगर वहाँ एक और तेजी से एल्गोरिथ्म है जो दूरी वापस नहीं करता है, तो एक पोस्ट करने के अलावा जो करता है, उस एल्गोरिथ्म को पोस्ट करना भी वास्तव में बहुत उपयोगी होगा।

कृपया यह भी बताएं कि फ़ंक्शन का रिटर्न तर्क क्या है, और आप इसका उपयोग दूरी या 'कोई-टकराव' के मामले में वापसी के लिए कैसे करते हैं। उदाहरण के लिए, क्या यह दूरी के लिए एक आउट पैरामीटर के साथ-साथ बूल रिटर्न मूल्य भी है? या यह केवल दूरी के साथ एक फ्लोट लौटाता है, बनाम कोई टक्कर के लिए -1 का मूल्य?

(उन लोगों के लिए जो नहीं जानते हैं: AABB = एक्सिस संरेखित बाउंडिंग बॉक्स)


मैं गलत हो सकता हूं लेकिन मुझे लगता है कि इस एल्गोरिथ्म के साथ आपको अभी भी झूठी सकारात्मकता मिलेगी। आप सही कह रहे हैं कि यदि 3 अक्षों की जांच करते समय सभी कोने एक ही तरफ हों, तो कोई टक्कर नहीं है। लेकिन ऐसा लगता है कि आपके पास अभी भी ऐसी स्थिति हो सकती है जहां सभी 3 अक्षों के दोनों तरफ बिंदु हों और फिर भी कोई टक्कर न हो। मैं आम तौर पर यह देखने के लिए जाँच करता हूं कि प्रवेश / निकास दूरी सुनिश्चित करने के लिए सभी तीन स्लैबों पर ओवरलैप है या नहीं। यह जियोमेट्रिक टूल साइट से है।
स्टीव एच।

दूरी क्वेरी के लिए शर्त क्यों होनी चाहिए? यदि आपको दूरी की आवश्यकता नहीं होने पर मामले के लिए और भी तेज़ एल्गोरिथम है, तो क्या आप इसके बारे में जानना नहीं चाहते हैं?
सैम होसेवर

ठीक है, नहीं, वास्तव में नहीं। मुझे यह जानना चाहिए कि टक्कर कितनी दूरी पर होती है।
सिरयाकोट

वास्तव में मुझे लगता है कि आप सही हैं, मैं प्रश्न संपादित करूँगा।
सिरयाकोट 12

4
जैसा कि मैंने आपके दूसरे सूत्र में पोस्ट किया है, इन प्रकार के एल्गोरिदम के लिए यहाँ एक अच्छा संसाधन है: realtimerendering.com/intersections.html
Tetrad

जवाबों:


22

एंड्रयू वू, जिन्होंने जॉन अमानटाइड्स के साथ रेअमराइज़र एल्गोरिथम (डीडीए) का उपयोग किया था, ने रेअटराइटर में सर्वत्र उपयोग किया था, "फास्ट रे-बॉक्स इंटरसेक्शन" ( यहां वैकल्पिक स्रोत ) लिखा था जो ग्राफिक्स रत्न, 1990, पीपी। 395-396 में प्रकाशित हुआ था। ग्रिड के माध्यम से एकीकरण के लिए विशेष रूप से निर्मित होने के बजाय (उदाहरण के लिए एक voxel मात्रा) के रूप में डीडीए है (zacharmarz 'उत्तर देखें), यह एल्गोरिथ्म विशेष रूप से उन दुनिया के लिए अनुकूल है जो समान रूप से उप-विभाजित नहीं हैं, जैसे कि आपका सामान्य रूप से 3 डी में पाया जाने वाला दुनिया खेल।

दृष्टिकोण 3 डी के लिए समर्थन प्रदान करता है, और वैकल्पिक रूप से बैकलेस कुलिंगिंग करता है। एल्गोरिथ्म डीडीए में उपयोग किए गए एकीकरण के समान सिद्धांतों से लिया गया है, इसलिए यह बहुत जल्दी है। अधिक विवरण मूल ग्राफिक्स रत्न की मात्रा (1990) में पाए जा सकते हैं।

कई अन्य दृष्टिकोण विशेष रूप से Ray-AABB के लिए realtimerendering.com पर देखे जा सकते हैं ।

संपादित करें: एक वैकल्पिक, शाखा रहित दृष्टिकोण - जो GPU और CPU दोनों पर वांछनीय होगा - यहां पाया जा सकता है


आह! तुम मुझे यह करने के लिए हरा, मैं आज सुबह भर में आया था। शानदार खोज!
सिराकालकोट

प्रसन्नता, सर। मैं आपको इस प्रकार के आधार पर मिलने वाले किसी भी एल्गोरिदम की तुलना करने का सुझाव दूंगा । (इस तरह की आधिकारिक सूचियाँ कहीं और हैं, लेकिन अभी कोई भी नहीं मिल सकती हैं।)
इंजीनियर


1
वू के एल्गोरिदम का एक अच्छी तरह से टिप्पणी किया गया कार्यान्वयन यहां पाया जा सकता है
इंजीनियर

4
आपके द्वारा प्रदान किए गए दो लिंक क्रमशः "नहीं मिला" और "निषिद्ध" त्रुटियां ...
liggiorgio

46

मैं अपनी किरण-प्रक्रिया में पहले क्या उपयोग कर रहा हूं:

// r.dir is unit direction vector of ray
dirfrac.x = 1.0f / r.dir.x;
dirfrac.y = 1.0f / r.dir.y;
dirfrac.z = 1.0f / r.dir.z;
// lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
// r.org is origin of ray
float t1 = (lb.x - r.org.x)*dirfrac.x;
float t2 = (rt.x - r.org.x)*dirfrac.x;
float t3 = (lb.y - r.org.y)*dirfrac.y;
float t4 = (rt.y - r.org.y)*dirfrac.y;
float t5 = (lb.z - r.org.z)*dirfrac.z;
float t6 = (rt.z - r.org.z)*dirfrac.z;

float tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6));
float tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6));

// if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us
if (tmax < 0)
{
    t = tmax;
    return false;
}

// if tmin > tmax, ray doesn't intersect AABB
if (tmin > tmax)
{
    t = tmax;
    return false;
}

t = tmin;
return true;

यदि यह सही है, तो यह अंतर-विच्छेद है, यदि यह गलत है, तो यह प्रतिच्छेद नहीं है।

यदि आप एक ही किरण का उपयोग कई बार करते हैं, तो आप पूर्वसंचालित कर सकते हैं dirfrac(पूरे चौराहे परीक्षण में केवल विभाजन)। और फिर यह वास्तव में तेज़ है। और आपके पास चौराहे (संग्रहित t) तक किरण की लंबाई भी है ।


क्या आपके चर नामों के लिए एक कुंजी प्रदान करना संभव होगा?
सिर्यकलोट

1
मैंने टिप्पणियों में कुछ स्पष्टीकरण जोड़ने की कोशिश की। तो: "r" किरण है, "r.dir" इसकी इकाई दिशा वेक्टर है, "r.org" मूल है, जहाँ से आप किरण को शूट करते हैं, "dirfrac" सिर्फ अनुकूलन है, क्योंकि आप इसे हमेशा उसी किरण के लिए उपयोग कर सकते हैं (आपको विभाजन नहीं करना है) और इसका अर्थ है 1 / r.dir। फिर "lb" सभी 3 निर्देशांक न्यूनतम के साथ AABB के कोने है और "rb" अधिकतम समन्वय के साथ oposite - कोने है। आउटपुट पैरामर "टी" मूल से चौराहे तक वेक्टर की लंबाई है।
zacharmarz

फ़ंक्शन की परिभाषा क्या दिखती है? क्या यह पता लगाना संभव है कि किरण पर टक्कर हुई?
सिरयाकोट

1
इसलिए जब किसी चौराहे पर वापसी होती है तो आपके एल्गोरिथ्म का क्या मतलब होता है लेकिन उस चौराहे की संख्या नकारात्मक होती है? tmin को कभी-कभी ऋणात्मक संख्या के रूप में लौटाया जाता है।
सिरयाकोट

1
आह, यह तब होता है जब मूल बॉक्स के अंदर होता है
SirYakalot

14

किसी ने भी यहां एल्गोरिदम का वर्णन नहीं किया है, लेकिन ग्राफिक्स रत्न एल्गोरिदम बस है:

  1. अपनी किरण की दिशा सदिश का उपयोग करते हुए, निर्धारित करें कि 6 में से 3 उम्मीदवार विमानों को पहले हिट किया जाएगा । यदि आपकी (अप्राकृतिकृत) किरण दिशा वेक्टर (-1, 1, -1) है, तो जिन 3 विमानों को हिट करना संभव है, वे हैं + x, -y, और + z।

  2. 3 उम्मीदवार विमानों में से, प्रत्येक के लिए प्रतिच्छेदन के लिए टी-मान प्राप्त करें। उस विमान को स्वीकार करें जो हिट होने वाले विमान के रूप में सबसे बड़ा टी मूल्य प्राप्त करता है, और जांचें कि हिट बॉक्स के भीतर है । पाठ में आरेख यह स्पष्ट करता है:

यहाँ छवि विवरण दर्ज करें

मेरा कार्यान्वयन:

bool AABB::intersects( const Ray& ray )
{
  // EZ cases: if the ray starts inside the box, or ends inside
  // the box, then it definitely hits the box.
  // I'm using this code for ray tracing with an octree,
  // so I needed rays that start and end within an
  // octree node to COUNT as hits.
  // You could modify this test to (ray starts inside and ends outside)
  // to qualify as a hit if you wanted to NOT count totally internal rays
  if( containsIn( ray.startPos ) || containsIn( ray.getEndPoint() ) )
    return true ; 

  // the algorithm says, find 3 t's,
  Vector t ;

  // LARGEST t is the only one we need to test if it's on the face.
  for( int i = 0 ; i < 3 ; i++ )
  {
    if( ray.direction.e[i] > 0 ) // CULL BACK FACE
      t.e[i] = ( min.e[i] - ray.startPos.e[i] ) / ray.direction.e[i] ;
    else
      t.e[i] = ( max.e[i] - ray.startPos.e[i] ) / ray.direction.e[i] ;
  }

  int mi = t.maxIndex() ;
  if( BetweenIn( t.e[mi], 0, ray.length ) )
  {
    Vector pt = ray.at( t.e[mi] ) ;

    // check it's in the box in other 2 dimensions
    int o1 = ( mi + 1 ) % 3 ; // i=0: o1=1, o2=2, i=1: o1=2,o2=0 etc.
    int o2 = ( mi + 2 ) % 3 ;

    return BetweenIn( pt.e[o1], min.e[o1], max.e[o1] ) &&
           BetweenIn( pt.e[o2], min.e[o2], max.e[o2] ) ;
  }

  return false ; // the ray did not hit the box.
}

+1 वास्तव में इसे समझाने के लिए (वह भी एक तस्वीर के साथ :)
किंवदंतियों 2

4

यह मेरा 3D रे / AABox चौराहा है जिसका मैं उपयोग कर रहा हूं:

bool intersectRayAABox2(const Ray &ray, const Box &box, int& tnear, int& tfar)
{
    Vector3d T_1, T_2; // vectors to hold the T-values for every direction
    double t_near = -DBL_MAX; // maximums defined in float.h
    double t_far = DBL_MAX;

    for (int i = 0; i < 3; i++){ //we test slabs in every direction
        if (ray.direction[i] == 0){ // ray parallel to planes in this direction
            if ((ray.origin[i] < box.min[i]) || (ray.origin[i] > box.max[i])) {
                return false; // parallel AND outside box : no intersection possible
            }
        } else { // ray not parallel to planes in this direction
            T_1[i] = (box.min[i] - ray.origin[i]) / ray.direction[i];
            T_2[i] = (box.max[i] - ray.origin[i]) / ray.direction[i];

            if(T_1[i] > T_2[i]){ // we want T_1 to hold values for intersection with near plane
                swap(T_1,T_2);
            }
            if (T_1[i] > t_near){
                t_near = T_1[i];
            }
            if (T_2[i] < t_far){
                t_far = T_2[i];
            }
            if( (t_near > t_far) || (t_far < 0) ){
                return false;
            }
        }
    }
    tnear = t_near; tfar = t_far; // put return values in place
    return true; // if we made it here, there was an intersection - YAY
}

क्या हैं tnearऔर tfar?
टेकनकोलागी

चौराहा [tnear, tfar] के बीच में है।
जीरो बर्थ

3

यहाँ ऊपर का एक अनुकूलित संस्करण है जिसका मैं GPU के लिए उपयोग करता हूं:

__device__ float rayBoxIntersect ( float3 rpos, float3 rdir, float3 vmin, float3 vmax )
{
   float t[10];
   t[1] = (vmin.x - rpos.x)/rdir.x;
   t[2] = (vmax.x - rpos.x)/rdir.x;
   t[3] = (vmin.y - rpos.y)/rdir.y;
   t[4] = (vmax.y - rpos.y)/rdir.y;
   t[5] = (vmin.z - rpos.z)/rdir.z;
   t[6] = (vmax.z - rpos.z)/rdir.z;
   t[7] = fmax(fmax(fmin(t[1], t[2]), fmin(t[3], t[4])), fmin(t[5], t[6]));
   t[8] = fmin(fmin(fmax(t[1], t[2]), fmax(t[3], t[4])), fmax(t[5], t[6]));
   t[9] = (t[8] < 0 || t[7] > t[8]) ? NOHIT : t[7];
   return t[9];
}

इसे एकता उपयोग के लिए परिवर्तित किया, और यह बिलिन सीमा
mgear

मैं लौटे मूल्य की व्याख्या कैसे कर सकता हूं? क्या यह उत्पत्ति और चौराहे बिंदु के बीच यूक्लिडियन दूरी की तरह कुछ है?
फर्डिनेंड मुट्स

बॉक्स की दूरी क्या है?
jjxtra

1

एक चीज जिसे आप जांचना चाहते हैं, वह दो अलग-अलग बफ़र्स में अपने बाउंडिंग बॉक्स के सामने और पीछे के हिस्से को तेज कर रही है। आरजीबी के रूप में x, y, z मानों को रेंडर करें (यह एक कोने में (0,0,0) के साथ बाउंडिंग बॉक्स के लिए सबसे अच्छा काम करता है और इसके विपरीत (1,1,1)।

जाहिर है, इसका सीमित उपयोग है लेकिन मैंने इसे सरल वॉल्यूम प्रदान करने के लिए बहुत अच्छा पाया।

अधिक विवरण और कोड के लिए:

http://www.daimi.au.dk/~trier/?page_id=98


1

यहां लाइन बनाम AABB कोड का उपयोग किया गया है:

namespace {
    //Helper function for Line/AABB test.  Tests collision on a single dimension
    //Param:    Start of line, Direction/length of line,
    //          Min value of AABB on plane, Max value of AABB on plane
    //          Enter and Exit "timestamps" of intersection (OUT)
    //Return:   True if there is overlap between Line and AABB, False otherwise
    //Note:     Enter and Exit are used for calculations and are only updated in case of intersection
    bool Line_AABB_1d(float start, float dir, float min, float max, float& enter, float& exit)
    {
        //If the line segment is more of a point, just check if it's within the segment
        if(fabs(dir) < 1.0E-8)
            return (start >= min && start <= max);

        //Find if the lines overlap
        float   ooDir = 1.0f / dir;
        float   t0 = (min - start) * ooDir;
        float   t1 = (max - start) * ooDir;

        //Make sure t0 is the "first" of the intersections
        if(t0 > t1)
            Math::Swap(t0, t1);

        //Check if intervals are disjoint
        if(t0 > exit || t1 < enter)
            return false;

        //Reduce interval based on intersection
        if(t0 > enter)
            enter = t0;
        if(t1 < exit)
            exit = t1;

        return true;
    }
}

//Check collision between a line segment and an AABB
//Param:    Start point of line segement, End point of line segment,
//          One corner of AABB, opposite corner of AABB,
//          Location where line hits the AABB (OUT)
//Return:   True if a collision occurs, False otherwise
//Note:     If no collision occurs, OUT param is not reassigned and is not considered useable
bool CollisionDetection::Line_AABB(const Vector3D& s, const Vector3D& e, const Vector3D& min, const Vector3D& max, Vector3D& hitPoint)
{
    float       enter = 0.0f;
    float       exit = 1.0f;
    Vector3D    dir = e - s;

    //Check each dimension of Line/AABB for intersection
    if(!Line_AABB_1d(s.x, dir.x, min.x, max.x, enter, exit))
        return false;
    if(!Line_AABB_1d(s.y, dir.y, min.y, max.y, enter, exit))
        return false;
    if(!Line_AABB_1d(s.z, dir.z, min.z, max.z, enter, exit))
        return false;

    //If there is intersection on all dimensions, report that point
    hitPoint = s + dir * enter;
    return true;
}

0

यह zacharmarz द्वारा पोस्ट किए गए कोड के समान लगता है।
मुझे यह कोड Christerony की पुस्तक 'Real-Time Collision Detection' से '5.3.3 Intersecting Ray or Segment Against Box' के तहत मिला।

// Where your AABB is defined by left, right, top, bottom

// The direction of the ray
var dx:Number = point2.x - point1.x;
var dy:Number = point2.y - point1.y;

var min:Number = 0;
var max:Number = 1;

var t0:Number;
var t1:Number;

// Left and right sides.
// - If the line is parallel to the y axis.
if(dx == 0){
    if(point1.x < left || point1.x > right) return false;
}
// - Make sure t0 holds the smaller value by checking the direction of the line.
else{
    if(dx > 0){
        t0 = (left - point1.x)/dx;
        t1 = (right - point1.x)/dx;
    }
    else{
        t1 = (left - point1.x)/dx;
        t0 = (right - point1.x)/dx;
    }

    if(t0 > min) min = t0;
    if(t1 < max) max = t1;
    if(min > max || max < 0) return false;
}

// The top and bottom side.
// - If the line is parallel to the x axis.
if(dy == 0){
    if(point1.y < top || point1.y > bottom) return false;
}
// - Make sure t0 holds the smaller value by checking the direction of the line.
else{
    if(dy > 0){
        t0 = (top - point1.y)/dy;
        t1 = (bottom - point1.y)/dy;
    }
    else{
        t1 = (top - point1.y)/dy;
        t0 = (bottom - point1.y)/dy;
    }

    if(t0 > min) min = t0;
    if(t1 < max) max = t1;
    if(min > max || max < 0) return false;
}

// The point of intersection
ix = point1.x + dx * min;
iy = point1.y + dy * min;
return true;

यह 2d है, हाँ?
सिरयाकोट

यह केवल 2 डी है, हाँ। इसके अलावा, कोड zacharmarz के रूप में अच्छी तरह से सोचा नहीं है, जो डिवीजनों और परीक्षणों की संख्या को कम करने का ख्याल रखता है।
sam hocevar 14

0

मुझे यह देखकर आश्चर्य हुआ कि किसी ने भी तवियन द्वारा शाखाविहीन स्लैब पद्धति का उल्लेख नहीं किया है

bool intersection(box b, ray r) {
    double tx1 = (b.min.x - r.x0.x)*r.n_inv.x;
    double tx2 = (b.max.x - r.x0.x)*r.n_inv.x;

    double tmin = min(tx1, tx2);
    double tmax = max(tx1, tx2);

    double ty1 = (b.min.y - r.x0.y)*r.n_inv.y;
    double ty2 = (b.max.y - r.x0.y)*r.n_inv.y;

    tmin = max(tmin, min(ty1, ty2));
    tmax = min(tmax, max(ty1, ty2));

    return tmax >= tmin;
}

पूर्ण विवरण: https://tavianator.com/fast-branchless-raybounding-box-intersections/


0

जब AABB के अंदर किरण की उत्पत्ति होती है, तो मुझे हैंडल करने के लिए @zacharmarz उत्तर मिलाया है। इस मामले में tmin ऋणात्मक होगा और किरण के पीछे होगा इसलिए tmax, किरण और AABB के बीच पहला चौराहा है।

// r.dir is unit direction vector of ray
dirfrac.x = 1.0f / r.dir.x;
dirfrac.y = 1.0f / r.dir.y;
dirfrac.z = 1.0f / r.dir.z;
// lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
// r.org is origin of ray
float t1 = (lb.x - r.org.x)*dirfrac.x;
float t2 = (rt.x - r.org.x)*dirfrac.x;
float t3 = (lb.y - r.org.y)*dirfrac.y;
float t4 = (rt.y - r.org.y)*dirfrac.y;
float t5 = (lb.z - r.org.z)*dirfrac.z;
float t6 = (rt.z - r.org.z)*dirfrac.z;

float tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6));
float tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6));

// if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us
if (tmax < 0)
{
    t = tmax;
    return false;
}

// if tmin > tmax, ray doesn't intersect AABB
if (tmin > tmax)
{
    t = tmax;
    return false;
}

// if tmin < 0 then the ray origin is inside of the AABB and tmin is behind the start of the ray so tmax is the first intersection
if(tmin < 0) {
  t = tmax;
} else {
  t = tmin;
}
return true;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.