Trovare il punto di contatto con SAT


12

Il Teorema dell'asse di separazione (SAT) semplifica la determinazione del vettore di traduzione minimo, ovvero il vettore più corto in grado di separare due oggetti in collisione. Tuttavia, ciò di cui ho bisogno è il vettore che separa gli oggetti lungo il vettore su cui si muove l'oggetto penetrante (ovvero il punto di contatto).

Ho disegnato una foto per aiutare a chiarire. C'è una scatola, che si sposta dalla posizione precedente a quella successiva. Nella sua posizione successiva, interseca il poligono grigio. SAT può restituire facilmente MTV, che è il vettore rosso. Sto cercando di calcolare il vettore blu.

Diagramma SAT

La mia attuale soluzione esegue una ricerca binaria tra le posizioni prima e dopo fino a quando la lunghezza del vettore blu è nota fino a una certa soglia. Funziona ma è un calcolo molto costoso poiché la collisione tra le forme deve essere ricalcolata ad ogni ciclo.

Esiste un modo più semplice e / o più efficiente per trovare il vettore del punto di contatto?


1
Sei pronto per usare SAT? Algoritmi come MPR (Minkowski Portal Refinement) possono trovare direttamente il collettore di contatto. Con SAT e GJK, è necessario un algoritmo separato per calcolare i punti di contatto.
Sean Middleditch,

Risposte:


6

Quello di cui stai parlando è abbastanza difficile se lo strutturi come prima cosa spostando un oggetto, quindi testando la collisione, quindi indietreggiando fino a quando non sei fuori dall'oggetto. Probabilmente è meglio pensarlo come un test di intersezione dinamica : un oggetto in movimento contro un oggetto fermo.

Fortunatamente, separare i test degli assi può aiutarti qui! Ecco una descrizione dell'algoritmo, per gentile concessione di Ron Levine :

L'algoritmo va così. Lavori con il vettore di velocità relativa dei due corpi convessi. Proiettando ciascuno dei due corpi e il vettore di velocità relativa su un particolare asse di separazione in t ₀ si ottengono due intervalli 1-D e una velocità 1-D, in modo che sia facile dire se i due intervalli si intersecano e, in caso contrario, se si stanno allontanando o si stanno muovendo insieme. Se sono separati e si allontanano su uno qualsiasi degli assi di separazione (o, di fatto, su qualsiasi asse qualunque), allora sai che non ci saranno collisioni future. Se su qualsiasi asse di separazione i due intervalli proiettati si intersecano in t₀ o sono separati e si muovono insieme, quindi è facile calcolare (con due semplici espressioni lineari 1D) il primo tempo futuro in cui i due intervalli si intersecheranno per la prima volta e (assumendo un movimento rettilineo continuo) l'ultimo momento futuro in cui i due gli intervalli dureranno per intersecarsi e inizieranno a separarsi. (Se si intersecano in t ₀ il tempo di intersezione futuro più recente è t ₀). Fallo al massimo per tutti gli assi di separazione. Se il massimo su tutti gli assi del primo tempo di intersezione futuro è inferiore al minimo su tutti gli assi dell'ultimo tempo di intersezione futuro più recente, allora quel massimo tempo di intersezione futuro prima è il tempo esatto della prima collisione dei due poliedri 3D, altrimenti lì non è una collisione in futuro.

In altre parole, attraversi tutti gli assi che normalmente eseguiresti in un test statico di separazione degli assi. Invece di uscire anticipatamente se non trovi sovrapposizioni, vai avanti e controlli la velocità proiettata dell'oggetto in movimento. Se si sta allontanando dall'oggetto statico, allora esci presto. Altrimenti, puoi risolvere abbastanza facilmente per il primo e l'ultimo momento di contatto (è un intervallo 1D che si sposta verso un altro intervallo 1D). Se lo fai per tutti gli assi e mantieni il massimo del primo tempo di intersezione e il minimo dell'ultimo tempo di intersezione, allora sai se l'oggetto in movimento colpirà l'oggetto statico e anche quando. Quindi puoi far avanzare l'oggetto in movimento esattamente fino al punto in cui colpirà l'oggetto statico.

Ecco alcuni pseudocodici approssimativi e non verificati per l'algoritmo:

t_min := +∞
t_max := -∞
foreach axis in potential_separating_axes
    a_min := +∞
    a_max := -∞
    foreach vertex in a.vertices
        a_min = min(a_min, vertex · axis)
        a_max = max(a_max, vertex · axis)
    b_min := +∞
    b_max := -∞
    foreach vertex in b.vertices
        b_min = min(b_min, vertex · axis)
        b_max = max(b_max, vertex · axis)
    v := b.velocity · axis
    if v > 0 then
        if a_max < b_min then
            return no_intersection
        else if (a_min < b_min < a_max) or (b_min < a_min < b_max) then
            t_min = min(t_min, (a_max - b_min) / v)
            t_max = max(t_max, 0)
        else
            t_min = min(t_min, (a_max - b_min) / v)
            t_max = max(t_max, (a_min - b_max) / v)
    else if v < 0 then
        // repeat the above case with a and b swapped
    else if v = 0 then
        if a_min < b_max and b_min < a_max then
            t_min = min(t_min, 0)
            t_max = max(t_max, 0)
        else
            return no_intersection
if t_max < t_min then
    // advance b by b.velocity * t_max
    return intersection
else
    return no_intersection

Ecco un articolo su Gamasutra che parla di questo implementato per alcuni diversi test primitivi. Si noti che proprio come SAT, questo richiede oggetti convessi.

Inoltre, questo è un po 'più complicato di un semplice test dell'asse di separazione. Assicurati assolutamente di averne bisogno prima di provarlo. Un numero molto elevato di giochi semplicemente sposta gli oggetti l'uno dall'altro lungo il vettore di traduzione minimo, perché semplicemente non penetrano molto l'uno nell'altro su un determinato frame ed è praticamente impercettibile visivamente.


2
Questo è tutto molto bello, ma non ha risposto direttamente alla domanda sul calcolo del collettore di contatto. Inoltre, se lo capisco correttamente, questa risposta funziona solo con velocità lineare e quindi non può supportare oggetti rotanti; non sono sicuro se la domanda chi lo desidera lo voglia o no.
Sean Middleditch,

1
@seanmiddleditch È vero, trascura le rotazioni sul telaio. Devi ruotare istantaneamente all'inizio. Ma nessun metodo che conosco a parte l'avanzamento conservativo in realtà si occupa esattamente delle rotazioni. Non avendo alcuna rotazione, tuttavia, produce una stima migliore del punto di contatto.
John Calsbeek,

2

Si desidera utilizzare il ritaglio poligonale. Questo è meglio spiegato con le foto, che non ho, ma questo ragazzo l'ha fatto, quindi glielo spiego.

http://www.codezealot.org/archives/394

Il collettore di contatto restituirà un punto su uno degli oggetti "più responsabili" della collisione, non il punto diretto della collisione. Tuttavia, non hai davvero bisogno di quel punto di collisione diretto. Puoi semplicemente allontanare gli oggetti usando la profondità di penetrazione e la normale che hai già, e usare il collettore di contatto per applicare altri effetti fisici (fai cadere la scatola / rotola giù per la pendenza, per esempio).

Nota che la tua foto mostra un piccolo problema: il punto sul vettore blu che stai chiedendo non verrà trovato in nessuna simulazione fisica, perché in realtà non è dove la scatola potrebbe colpire. La scatola colpirebbe con l'angolo in basso a sinistra da qualche parte più in alto del pendio mentre penetra solo un piccolo angolo.

La profondità di penetrazione sarà relativamente piccola, e semplicemente spingendo la scatola fuori dal pendio lungo la normale penetrazione la metterà abbastanza vicino alla posizione "corretta" per essere quasi impercettibile nella pratica, specialmente se la scatola rimbalzerà, cadrà o scorrere successivamente.


Sai se esiste un modo per calcolare quel "vettore blu" (quello necessario per spingere l'oggetto fuori dalla forma lungo il vettore di velocità) usando SAT?
Tara,

@Dudeson: non usando SAT, no. Non è quello che fa SAT. SAT offre il vantaggio di una profondità di penetrazione minima, non il primo bordo di contatto. Dovresti usare il rilevamento delle collisioni di forma spazzata per fare quello che stai chiedendo, credo.
Sean Middleditch il

So cosa fa SAT. L'ho implementato prima. Ma c'è un problema che sto affrontando che sarebbe risolto se potessi semplicemente usare l'output del SAT per calcolare il primo fronte di contatto. Vedi anche la risposta di "someguy". Suggerisce che è possibile ma non lo spiega molto bene.
Tara,

@Dudeson: Il bordo / asse della minima penetrazione non è necessariamente il bordo del primo contatto, quindi non vedo ancora come SAT aiuti qui. Non sono affatto un esperto in questo argomento, quindi ammetto che potrei sbagliarmi. :)
Sean Middleditch il

Esattamente. Ecco perché non sono sicuro che sia possibile. Ciò implicherebbe, tuttavia, che la risposta di Someguy è semplicemente sbagliata. Ma grazie per l'aiuto comunque! : D
Tara,

0

Basta proiettare il MAT Vector sulla direzione Vector. Il vettore risultante può essere aggiunto al vettore di direzione per compensare la penetrazione. Proiettalo allo stesso modo, come fai sull'Asse quando fai il SAT. Questo imposta l'oggetto esattamente sulla posizione in cui tocca l'altro oggetto. Aggiungi un piccolo epsilon per combattere i problemi in virgola mobile.


1
"MAT Vector"? Intendi "MTV"?
Tara,

0

Ci sono un paio di avvertenze alla mia risposta, che mi toglierò di mezzo prima: si tratta solo di scatole di delimitazione non rotanti. Si presuppone che si stia tentando di affrontare i problemi di tunneling , ovvero i problemi causati da oggetti che si muovono ad alta velocità.

Una volta identificato l'MTV, conosci il bordo / superficie normale che devi testare. Conosci anche il vettore di velocità lineare dell'oggetto compenetrante.

Una volta stabilito che ad un certo punto durante il frame, si è verificata un'intersezione, è quindi possibile eseguire operazioni binarie di mezzo passo, in base ai seguenti punti di partenza: Identificare il vertice che è penetrato per primo durante il frame:

vec3 vertex;
float mindot = FLT_MAX;
for ( vert : vertices )
{
    if (dot(vert, MTV) < mindot)
    {
         mindot = dot(vert, MTV);
         vertex = vert;
    }
}

Una volta identificato il vertice, il mezzo passo binario diventa molto meno costoso:

//mindistance is the where the reference edge/plane intersects it's own normal. 
//The max dot product of all vertices in B along the MTV will get you this value.
halfstep = 1.0f;
vec3 cp = vertex;
vec3 v = A.velocity*framedurationSeconds;
float errorThreshold = 0.01f; //choose meaningful value here
//alternatively, set the while condition to be while halfstep > some minimum value
while (abs(dot(cp,normal)) > errorThreshold)
{            
    halfstep*=0.5f;
    if (dot(cp,normal) < mindistance) //cp is inside the object, move backward
    {
        cp += v*(-1*halfstep);
    }
    else if ( dot(cp,normal) > mindistance) //cp is outside, move it forward
    {
        cp += v*(halfstep);
    }
}

return cp;

Ciò è ragionevolmente accurato, ma fornirà un solo punto di collisione, in un singolo caso.

Il fatto è che di solito è possibile dire in anticipo se un oggetto si sposterà abbastanza velocemente per frame per poter tunnelare in questo modo, quindi il miglior consiglio è quello di identificare i vertici principali lungo la velocità ed eseguire un test del raggio lungo il vettore di velocità. Nel caso di oggetti rotanti, sarà necessario eseguire una sorta di slerp binario a mezzo passo per accertare il punto di contatto corretto.

Nella maggior parte dei casi, tuttavia, si può presumere che la maggior parte degli oggetti nella scena non si muoverà abbastanza velocemente da penetrare fino a quel punto in un singolo fotogramma, quindi non è necessario un mezzo passo e sarà sufficiente il rilevamento discreto delle collisioni. Oggetti ad alta velocità come proiettili, che si muovono troppo velocemente per essere visti, possono essere rintracciati per i punti di contatto.

È interessante notare che questo metodo halfstep può anche darti il ​​tempo (quasi) esatto in cui l'oggetto si è verificato durante il frame:

float collisionTime = frametimeSeconds * halfstep;

Se si sta eseguendo una sorta di risoluzione della collisione fisica, è possibile correggere la posizione di A:

v - (v*halfstep)

allora puoi fare normalmente la tua fisica da lì. Il rovescio della medaglia è che se l'oggetto si muove ragionevolmente velocemente, lo vedrai teletrasportarsi lungo il suo vettore di velocità.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.