Möller–Trumbore intersektionsalgoritm
Möller –Trumbore stråltriangelskärningsalgoritmen, uppkallad efter dess uppfinnare Tomas Möller och Ben Trumbore, är en snabb metod för att beräkna skärningspunkten mellan en stråle och en triangel i tre dimensioner utan att behöva förberäkning av planekvationen för planet som innehåller triangeln. . Bland andra användningsområden kan den användas i datorgrafik för att implementera strålspårningsberäkningar som involverar triangelnät .
Beräkning
Definitioner
Strålen definieras av en ursprungspunkt och en riktningsvektor . Varje punkt på strålen kan uttryckas med där parametern sträcker sig från noll till oändligt. Triangeln definieras av tre hörn, benämnda , , . Planet som triangeln befinner sig på, som behövs för att beräkna stråltriangelns skärning, definieras av en punkt på planet, såsom , och en vektor som är ortogonal mot varje punkt på det planet, såsom korsprodukten mellan vektorn från till och vektorn från till :
, där och och är alla punkter på planet.
Kontrollera om strålen är parallell med triangeln
Ta först reda på om strålen skär det plan som triangeln befinner sig på, och om den gör det, hitta koordinaterna för den skärningspunkten. Det enda sättet att strålen inte kommer att skära planet är om strålens riktningsvektor är parallell med planet. När detta händer prickprodukten mellan strålens riktningsvektor och planets normalvektor noll. Annars skär strålen planet någonstans , men inte nödvändigtvis inom triangeln.
Kontrollera om stråle-plansskärningen ligger utanför triangeln
Med hjälp av barycentriska koordinater kan vilken punkt som helst på triangeln uttryckas som en konvex kombination av triangelns hörn:
Koefficienterna måste vara icke-negativa och summera till 1, så w kan ersättas med :
, där är vilken punkt som helst på planet. Observera att och är vektorer på kanten av triangeln, och tillsammans spänner de över ett plan (som går genom origo). Varje punkt på det planet kan skrivas som och kan översättas med för att "flytta" den punkten på det plan som triangeln befinner sig på.
För att hitta och för en viss skärningspunkt, ställ in stråluttrycket lika med det plana uttrycket och placera variablerna på ena sidan och konstanterna på den andra.
Detta är ett system av linjära ekvationer med tre ekvationer (en vardera för , , ) och tre okända ( , och ), och kan representeras som en matris-vektormultiplikation.
Denna ekvation kommer alltid att ha en lösning när matrisen har tre linjärt oberoende kolumnvektorer i och därmed är inverterbar . Detta händer om och endast om triangelns höjdpunkter inte är kolinjära och strålen inte är parallell med planet.
Algoritmen kan använda Cramers regel för att hitta värdena , och för en skärningspunkt, och om den ligger inom triangeln kan skärningspunktens exakta koordinater hittas genom att koppla in till strålens ekvation.
C++ implementering
Följande är en implementering av algoritmen i C++ :
bool RayIntersectsTriangle ( Vector3D rayOrigin , Vector3D rayVector , Triangle * inTriangle , Vector3D & outIntersectionPoint ) { const float EPSILON = 0,0000001 ; Vector3D vertex0 = inTriangle -> vertex0 ; Vector3D vertex1 = inTriangle -> vertex1 ; Vector3D vertex2 = inTriangle -> vertex2 ; Vector3D edge1 , edge2 , h , s , q ; flyta a , f , u , v ; kant1 = vertex1 - vertex0 ; kant2 = vertex2 - vertex0 ; h = rayVector . crossProduct ( kant2 ); a = kant1 . dotProduct ( h ); if ( a > - EPSILON && a < EPSILON ) returnerar false ; // Denna stråle är parallell med denna triangel. f = 1,0 / a ; s = rayOrigin - vertex0 ; u = f * s . dotProduct ( h ); if ( u < 0,0 || u > 1,0 ) returnerar falskt ; q = s . crossProduct ( edge1 ); v = f * rayVector . dotProduct ( q ); if ( v < 0,0 || u + v > 1,0 ) returnerar falskt ; // I detta skede kan vi beräkna t för att ta reda på var skärningspunkten är på linjen. flyta t = f * kant2 . dotProduct ( q ); if ( t > EPSILON ) // ray intersection { outIntersectionPoint = rayOrigin + rayVector * t ; returnera sant ; } else // Detta betyder att det finns en linjeskärning men inte en strålkorsning. returnera falskt ; }
Java implementering
Följande är en implementering av algoritmen i Java med javax.vecmath
från Java 3D API:
public class MollerTrumbore { private static final double EPSILON = 0,0000001 ; public static boolean rayIntersectsTriangle ( Point3d rayOrigin , Vector3d rayVector , Triangle inTriangle , Point3d outIntersectionPoint ) { Point3d vertex0 = inTriangle . getVertex0 (); Point3d vertex1 = intriangel . getVertex1 (); Point3d vertex2 = intriangel . getVertex2 (); Vector3d edge1 = new Vector3d (); Vector3d edge2 = new Vector3d (); Vector3d h = ny Vector3d (); Vector3d s = ny Vector3d (); Vector3d q = ny Vector3d (); dubbla a , f , u , v ; kant1 . sub ( vertex1 , vertex0 ); kant2 . sub ( vertex2 , vertex0 ); h . kors ( rayVector , edge2 ); a = kant1 . punkt ( h ); if ( a > - EPSILON && a < EPSILON ) { return false ; // Denna stråle är parallell med denna triangel. } f = 1,0 / a ; s . sub ( rayOrigin , vertex0 ); u = f * ( s . punkt ( h )); if ( u < 0,0 || u > 1,0 ) { return false ; } q . kors ( s , kant1 ); v = f * rayVector . punkt ( q ); if ( v < 0,0 || u + v > 1,0 ) { return false ; } // I detta skede kan vi beräkna t för att ta reda på var skärningspunkten är på linjen. dubbel t = f * kant2 . punkt ( q ); if ( t > EPSILON ) // ray intersection { outIntersectionPoint . set ( 0,0 , 0,0 , 0,0 ); outIntersectionPoint . scaleAdd ( t , rayVector , rayOrigin ); returnera sant ; } else // Detta betyder att det finns en linjeskärning men inte en strålkorsning. { return false ; } } }
Se även
- Badouel skärningsalgoritm
- MATLAB version av denna algoritm (mycket vektoriserad)
- Baldwin-Weber strål-triangel skärningsalgoritm
- Schlick–Subrenat-algoritm för strål-fyrsidig skärning
Länkar
- Snabb Minimal Storage Ray-Triangle Intersection
- Optimering av den grundläggande algoritmen av Möller & Trumbore , kod från journal of graphics tools
- Ray-Tracing: Återge en triangel