Determinazione del contatto di rest tra sfera e piano quando si usano forze esterne

Questa domanda ha una domanda importante e una domanda minore. Credo di avere ragione in entrambe le domande dalla mia ricerca, ma non entrambe.

Per il mio ciclo di fisica, la prima cosa che faccio è applicare una forza gravitazionale al mio TotalForce per un object di corpo rigido. Quindi controllo le collisioni usando TotalForce e Velocity . My TotalForce viene reimpostato su (0, 0, 0) dopo ogni ciclo fisico, sebbene manterrò la mia velocity .

Ho familiarità con il controllo di collisione tra una sfera in movimento e un piano statico quando si utilizza solo la velocità. Tuttavia, cosa succede se ho altre forze oltre alla velocity , come la gravità? Ho messo le altre forze in TotalForces (in questo momento ho solo gravità). Per compensare ciò, quando determino che la sfera non si sta attualmente sovrapponendo al piano, lo faccio

  Vector3 forces = (sphereTotalForces + sphereVelocity); Vector3 forcesDT = forces * fElapsedTime; float denom = Vec3Dot(&plane->GetNormal(), &forces); 

Tuttavia, questo può essere problematico per come pensavo dovesse essere il contatto di rest. Pensavo che il contatto a rest fosse calcolato da

 denom * dist == 0.0f 

Dove dist è

     float dist = Vec3Dot(&plane->GetNormal(), &spherePosition) - plane->d; 

    (Per riferimento, il denom * dist > 0.0f ovvio che la sfera si sta allontanando dal piano)

    Tuttavia, questo non può mai essere vero. Anche quando sembra esserci “contatto a rest”. Ciò è dovuto al fatto che il calcolo delle mie forces sopra ha sempre almeno un .y di -9,8 (la mia gravità). Quando quando si muove verso un piano con una normale di (0, 1, 0) si produce ay of denom di -9,8.

    La mia domanda è

    1) Sto calcolando correttamente il contatto a rest con quello che ho menzionato con i miei primi due frammenti di codice?

    Se è così,

    2) Come dovrebbero essere usate le mie “altre forze” come la gravità? Il mio uso di TotalForces errato?

    Per riferimento, il mio timestep è

      mAcceleration = mTotalForces / mMass; mVelocity += mAcceleration * fElapsedTime; Vector3 translation = (mVelocity * fElapsedTime); 

    MODIFICARE

    Poiché sembra che alcune modifiche suggerite cambino il mio codice di collisione, ecco come rilevo i miei stati di collisione

     if(fabs(dist) GetNormal(), &forces); // Resting contact if(dist == 0) { } // Sphere is moving away from plane else if(denom * dist > 0.0f) { } // There will eventually be a collision else { float fIntersectionTime = (sphereRadius - dist) / denom; float r; if(dist > 0.0f) r = sphereRadius; else r = -sphereRadius; Vector3 collisionPosition = spherePosition + fIntersectionTime * sphereVelocity - r * planeNormal; } } 

    1. Dovresti usare if(fabs(dist) < 0.0001f) { /* collided */ } Si tratta di acocunt per precisioni in virgola mobile. Sicuramente non otterresti un 0.0f esatto alla maggior parte degli angoli o dei contatti.

    2. il valore di dist se negativo, è in effetti la quantità effettiva di cui hai bisogno per spostare il corpo indietro sulla superficie del piano nel caso in cui attraversi la superficie piana. sphere.position = sphere.position - plane.Normal * fabs(dist);

    3. Una volta che lo hai riportato in superficie, puoi opzionalmente farlo rimbalzare nella direzione opposta rispetto al piano normale; o semplicemente stai sull'aereo.

      parallel_vec = Vec3.dot(plane.normal, -sphere.velocity);

      perpendicular_vec = sphere.velocity - parallel_vec;

      bounce_velocity = parallel - perpendicular_vec;

    4. non puoi fare ciecamente totalforce = external_force + velocity meno che tutto non abbia una massa unitaria.

    MODIFICA :

    1. Per definire completamente un piano nello spazio 3D, la struttura del piano dovrebbe memorizzare un piano vettore normale e un punto sul piano. http://en.wikipedia.org/wiki/Plane_(geometry ).

    Vector3 planeToSphere = sphere.point - plane.point;

    float dist = Vector3.dot(plane.normal, planeToSphere) - plane.radius;

    if(dist < 0) { // collided. }

    Ti suggerisco di studiare più matematica prima se questa è la parte che non conosci.

    NB: Scusa, la formattazione è incasinata ... Non posso contrassegnarla come blocco di codice.

    EDIT 2 : Basandomi sulla mia comprensione del tuo codice, o stai nominando male le tue variabili o, come ho detto prima, hai bisogno di rivedere la tua teoria matematica e fisica. Questa linea non fa nulla di utile.

    float denom = Vec3Dot(&plane->GetNormal(), &forces);

    In qualsiasi istante di tempo, una forza sulla sfera può essere in qualsiasi direzione, indipendentemente dalla direzione del viaggio. quindi denom calcola essenzialmente la quantità di forza nella direzione della superficie del piano, ma non ti dice nulla se la palla colpirà l'aereo. ad esempio la gravità è verso il basso, ma una palla può avere velocità verso l'alto e colpire un aereo sopra. Con quello, hai bisogno di Vec3Dot(plane.normal, velocity) invece.

    In alternativa, Mark Phariss e Gerhard Powell ti avevano già fornito l'equazione fisica per la cinematica lineare, puoi usarli per calcolare direttamente le posizioni, la velocità e il tempo di impatto futuri.

    es. s = 0.5 * (u + v) * t; dà lo spostamento dopo l'ora futura t. confronta questo spostamento con la distanza dal piano e ottieni se la sfera colpirà l'aereo. Quindi, di nuovo, ti suggerisco di leggere su http://en.wikipedia.org/wiki/Linear_motion e le cose facili prima di http://en.wikipedia.org/wiki/Kinematics .

    Ancora un altro metodo, se si aspetta o non si assume che altre forze agiscano sulla sfera, allora si esegue un test di collisione raggio / piano per trovare il tempo t in cui colpirà l'aereo, in tal caso, leggere http: // it .wikipedia.org / wiki / Line-plane_intersection .

    Ci sarà sempre -9,8y di gravità che agisce sulla sfera. Nel caso di una sfera sospesa ciò comporterà un’accelerazione verso il basso (la forza netta è diversa da zero). Nel caso della sfera che poggia sul piano, questo si tradurrà nell’aereo che esercita una normale forza sulla sfera. Se l’aereo fosse perfettamente orizzontale con la sfera in rest, questa forza normale sarebbe esattamente di + 9,8 y che annullerebbe perfettamente la forza di gravità. Per una sfera ferma su un piano non orizzontale la forza normale è 9.8y * cos(angle) (l’angolo è compreso tra -90 e +90 gradi).

    Le cose diventano più complicate quando una sfera mobile colpisce un piano in quanto la forza normale dipenderà dalla velocità e dalle proprietà del materiale piano / sfera. A seconda di quali siano i requisiti dell’applicazione, puoi ignorarlo o provare alcune cose con le normali forze e vedere come funziona.

    Per le tue domande specifiche:

    1. Credo che il contatto sia più specificamente solo quando dist == 0.0f , cioè la sfera e il piano stanno prendendo contatto. Presumo che la tua collisione tenga conto del fatto che la sfera potrebbe muoversi oltre il piano in qualsiasi fase temporale della fisica.
    2. In questo momento non sembra che le forze normali siano state messe sulla sfera dall’aereo quando stanno entrando in contatto. Lo farei controllando il contatto ( dist == 0.0f ) e se è vero aggiungendo la forza normale alla sfera. Nel caso semplice di una sfera che cade su un piano quasi orizzontale (angolo compreso tra -90 e +90 gradi), sarebbe solo sphereTotalForces += Vector3D(0, 9.8 * cos(angle), 0) .

    Modificare:

    Da qui la tua equazione per dist calcolare la distanza dal bordo della sfera al piano potrebbe non essere corretta a seconda dei dettagli del tuo problema e del codice (che non è dato). Supponendo che il tuo aereo percorra l’origine, l’equazione corretta è:

     dist = Vec3Dot(&spherePosition, &plane->GetNormal()) - sphereRadius; 

    È uguale alla tua equazione se plane->d == sphereRadius . Si noti che se l’aereo non è all’origine, utilizzare:

     D3DXVECTOR3 vecTemp(spherePosition - pointOnPlane); dist = Vec3Dot(&vecTemp, &plane->GetNormal()) - sphereRadius; 

    La soluzione esatta a questo problema implica una matematica piuttosto seria. Se vuoi una soluzione approssimativa, ti consiglio vivamente di svilupparla gradualmente.

    1) Assicurati che il tuo sim funzioni senza gravità. La palla deve attraversare lo spazio e avere collisioni anelastiche (o parzialmente elastiche) con superfici angolate prive di attrito.

    2) Introdurre la gravità. Questo cambierà le traiettorie balistiche da linee rette a parabole e introdurrà lo scivolamento , ma non avrà molto effetto sulle collisioni.

    3) Introdurre l’attrito statico e cinetico (indipendentemente). Questi cambieranno la dynamic dello scivolamento. Non preoccuparti per l’attrito nelle collisioni per ora.

    4) Dare la velocità angular della palla e un momento di inerzia. Questo è un grande passo. Assicurati di poter applicare le coppie e ottenere accelerazioni angolari realistiche. Si noti che il comportamento realistico di una massa rotante può essere contro-intuitivo.

    5) Prova a far scorrere la palla lungo una superficie piana, sotto gravità. Se hai fatto tutto bene, la sua velocità angular aumenterà gradualmente e la sua velocità lineare diminuirà gradualmente, fino a quando non si rompe. Prova a dare alla palla un giro iniziale (“draw”, “follow” o “english”).

    6) Prova lo stesso, ma su una superficie inclinata. Questo è un passo relativamente piccolo.

    Se arrivi così lontano avrai una sim molto realistica. Non tentare di saltare uno dei passaggi, ti darai solo mal di testa.

    Risposte ai tuoi problemi di fisica:

     f = mg + other_f; // m = mass, g = gravity (9.8) a = f / m; // a = acceleration v = u + at; // v = new speed, u = old speed, t = delta time s = 0.5 * (u + v) *t; 

    Quando hai una collisione, cambi entrambe le velocità a 0 (o v eu = – (u * 0.7) se vuoi che rimbalzi).

    Poiché la velocità = 0, la palla è ferma.

    Se è 2D o 3D, allora basta cambiare la velocità nella direzione della normale della superficie a 0 e mantenere la velocità parallela la stessa. Ciò comporterà la rotazione della palla sulla superficie.

    È necessario spostare la palla in superficie se taglia la superficie. È ansible ridurre la distanza di collisione a una piccola quantità (ad esempio 0,001) per assicurarsi che rimanga ferma.

    http://www.physicsforidiots.com/dynamics.html#vuat

    Modificare:

    NeHe è una straordinaria fonte di design per i motori di gioco: ecco una pagina sul rilevamento delle collisioni con ottime descrizioni: http://nehe.gamedev.net/tutorial/collision_detection/17005/

    Modifica 2: (da NeHe)

     double DotProduct=direction.dot(plane._Normal); // Dot Product Between Plane Normal And Ray Direction Dsc=(plane._Normal.dot(plane._Position-position))/DotProduct; // Find Distance To Collision Point Tc= Dsc*T / Dst Collision point= Start + Velocity*Tc 

    Successivamente suggerisco di dare un’occhiata agli articoli di erin cato (l’autore di Box2D) e agli articoli di Glennfedler. La gravità è una forte accelerazione e si traduce in forze forti. È facile avere simulazioni errate a causa delle imprecisioni fluttuanti, dei timestep variabili e dell’integrazione di euler, molto rapidamente. Il riposizionamento della sfera sulla superficie del piano nel caso in cui cominci a sfilarsi ha superato il piano è obbligatorio, mi sono reso conto che è meglio farlo solo se la velocità della sfera è in opposizione al piano normale (questo può essere confrontato affrontare il culling nel rendering 3D: non prendere in considerazione gli aerei con retroilluminazione).

    inoltre, la maggior parte dei motori fisici interrompe la simulazione su corpi inattivi, e la maggior parte dei giochi non tiene conto della gravità mentre si muove, solo quando cade. Usano “mesh di navigazione” e sistemi personalizzati a patto che siano sicuri che l’object simulato si attacchi al “terreno”.

    Non conosco un simulatore di fisica impeccabile là fuori, ci sarà sempre un’esplosione di integrazione, una collisione mancata (cercare “collisione spazzata”) … ci vuole un sacco di raffinate modifiche empiriche.

    Inoltre ti suggerisco di cercare “impulsi” che è un metodo per evitare di modificare manualmente la velocità quando si incontra una collisione.

    Dai anche un’occhiata a “ciò che ogni scienziato informatico dovrebbe sapere sui punti mobili”

    in bocca al lupo, sei entrato in un campo minato, in modo casuale non comprensibile, area mordace di computer science numerica 🙂

    Per una maggiore fedeltà (non risolverei il tuo problema principale), cambierei il tuo timestep a

     mAcceleration = mTotalForces / mMass; Vector3 translation = (mVelocity * fElapsedTime) + 0.5 * mAcceleration * pow(fElapsedTime, 2); mVelocity += mAcceleration * fElapsedTime; 

    Hai detto che la sfera era un corpo rigido; stai anche modellando l’aereo come rigido? Se è così, avresti una forza puntiforms infinita al momento del contatto e una collisione perfettamente elastica senza una esplicita dissipazione di quantità di moto.

    Forza e velocità non possono essere sumte (unità incompatibili); se stai solo provando a modellare la cinematica, puoi ignorare la massa e lavorare solo con accelerazione e velocità.

    Supponendo che la sfera sia semplicemente caduta su un piano orizzontale con una collisione perfettamente inelastica (nessun rimbalzo), si potrebbe fare [NB, non conosco la syntax C, quindi questo sarà Pythonic]

     mAcceleration = if isContacting then (0, 0, 0) else (0, -9.8, 0) 

    Se aggiungete dell’elasticità (diciamo un mezzo impulso conservato) alla collisione, sarebbe più simile

     mAcceleration = (0, -9.8, 0) + if isContacting then (0, 4.9, 0)