Come si proietta correttamente un punto dietro la videocamera?


10

Sto realizzando un gioco 3D in cui metto un punto esclamativo sopra i punti di interesse.

Per scoprire dove dovrei posizionare il mio marker nella schermata 2D, sto proiettando manualmente il punto 3D dove dovrebbe essere il marker.

Sembra così:

I marcatori sembrano fantastici

Sembra abbastanza buono Quando il marker si trova fuori dallo schermo, ho semplicemente ritagliato le coordinate in modo che si adattino allo schermo. Sembra così:

I marcatori sembrano ancora più fantastici

Finora l'idea sta andando abbastanza bene. Tuttavia, quando i punti di interesse sono dietro la telecamera, le coordinate X, Y risultanti vengono invertite (come in positivo / negativo) e ottengo l'indicatore per apparire nell'angolo opposto dello schermo, in questo modo:

I marcatori non sono così fantastici

(Il punto proiettato, quindi bloccato, è la punta del marker. Non importa la rotazione del marker)

Ha senso, dal momento che le coordinate dietro il frustum sono invertite in X e Y. Quindi quello che sto facendo è invertire le coordinate quando sono dietro la telecamera. Tuttavia, non so ancora quale sia esattamente la condizione in cui le coordinate dovrebbero essere invertite.

Questo è attualmente l'aspetto del mio codice di proiezione (in C # con SharpDX):

    public override PointF ProjectPosition(float viewportWidth, float viewportHeight, float y)
    {
        var projectionMatrix = Matrix.PerspectiveFovRH(GetCalibratedFOV(Camera.FOV, viewportWidth, viewportHeight), viewportWidth / viewportHeight, Camera.Near, Camera.Far);
        var viewMatrix = Matrix.LookAtRH(new Vector3(Camera.PositionX, Camera.PositionY, Camera.PositionZ), new Vector3(Camera.LookAtX, Camera.LookAtY, Camera.LookAtZ), Vector3.UnitY);
        var worldMatrix = Matrix.RotationY(Rotation) * Matrix.Scaling(Scaling) * Matrix.Translation(PositionX, PositionY, PositionZ);
        var worldViewProjectionMatrix = worldMatrix * viewMatrix * projectionMatrix;

        Vector4 targetVector = new Vector4(0, y, 0, 1);
        Vector4 projectedVector = Vector4.Transform(targetVector, worldViewProjectionMatrix);

        float screenX = (((projectedVector.X / projectedVector.W) + 1.0f) / 2.0f) * viewportWidth;
        float screenY = ((1.0f - (projectedVector.Y / projectedVector.W)) / 2.0f) * viewportHeight;
        float screenZ = projectedVector.Z / projectedVector.W;

        // Invert X and Y when behind the camera
        if (projectedVector.Z < 0 ||
            projectedVector.W < 0)
        {
            screenX = -screenX;
            screenY = -screenY;
        }

        return new PointF(screenX, screenY);
    }

Come puoi vedere, la mia idea attuale è quella di invertire le coordinate quando le coordinate Z o W sono negative. Funziona la maggior parte delle volte, ma ci sono ancora alcune posizioni della telecamera molto specifiche in cui non funziona. In particolare questo punto mostra una coordinata funzionante e l'altra no (la posizione corretta dovrebbe essere in basso a destra):

Marker mezzo fantastico

Ho provato a invertire quando:

  • Z è negativo (questo è ciò che ha più senso per me)
  • W è negativo (non capisco il significato di un valore W negativo)
  • Sia Zo Wè negativo (quello che sta attualmente lavorando la maggior parte del tempo)
  • Ze Wsono di segno diverso, aka: Z / W < 0(ha senso per me. non funziona però)

Ma non ho ancora trovato un modo coerente con cui proiettare correttamente tutti i miei punti.

Qualche idea?

Risposte:


2

Che ne dici di farlo in modo leggermente diverso?

Inizia come al solito e visualizza tutti gli indicatori all'interno del riquadro di visualizzazione. Se un marker si trova all'esterno della finestra, procedere:

  1. Ottieni posizioni del marker A
  2. Ottieni la posizione della videocamera B(forse leggermente di fronte ad essa)
  3. Calcola il vettore 3D ABtra questi due
  4. Converti e normalizza quel vettore in limiti dello schermo 2D ( Asarà nel centro della vista e Bcolpirà un bordo delle viste)

inserisci qui la descrizione dell'immagine

Le linee di colore sono ABvettori. Le sottili linee nere sono altezze dei personaggi. Sulla destra è lo schermo 2D

  1. Forse aggiungi una regola, che tutto dietro la telecamera passa sempre al bordo inferiore
  2. Disegna il marker nello spazio dello schermo per quanto riguarda le dimensioni e le sovrapposizioni dei marker

Potrebbe essere necessario provarlo per vedere se il marker che esce dalla finestra salterà tra le posizioni. In tal caso, puoi provare a modificare la posizione quando l'indicatore si avvicina al bordo della finestra.


Come funzionerebbe esattamente quel passaggio 4?
Panda Pajama,

@PandaPajama: ho aggiunto un po 'alla risposta. Adesso è più chiaro?
Kromster,

Non proprio. Come stai "convertendo e normalizzando" il vettore? Se questo è applicando una matrice di proiezione, allora stai facendo la stessa cosa che sto facendo
Panda Pajama

@PandaPajama: per quanto ho capito, lo stai facendo nello spazio della telecamera (da qui il problema ZW negativo). Suggerisco di farlo nello spazio del mondo e solo allora converti in spazio-schermo.
Kromster,

Ma, calcolando AB, non sto convertendo la posizione target da coordinate mondiali a coordinate della telecamera?
Panda Pajama,

1

Dati i tre vettori [camera position], [camera direction]e [object position]. Calcola il prodotto punto di [camera direction]e [object position]-[camera position]. Se il risultato è negativo, l'oggetto si trova dietro la telecamera.

Dato che ho capito bene il tuo codice sarebbe:

if((PositionX - Camera.PositionX) * Camera.LookAtX
    + (PositionY - Camera.PositionY) * Camera.LookAtY
    + (PositionZ - Camera.PositionZ) * Camera.LookAtZ
    < 0)

Ylo è PositionY + (y * Scaling). Ci proverò stasera
Panda Pajama,

Con questo posso davvero sapere quando il punto è dietro la telecamera. Tuttavia, ciò non impedisce alla proiezione di raggiungere una singolarità a Z = 0.
Panda Pajama,

@PandaPajama È un problema pratico? La probabilità di Zessere esattamente 0dovrebbe essere molto piccola, la maggior parte dei giocatori non la sperimenterà mai e l'impatto del gameplay nei pochi casi previsti è un trascurabile glitch visivo a frame singolo. Direi che il tuo tempo è meglio speso altrove.
aaaaaaaaaaaa,

0

Solo test (proiettato.W <0), non (Z <0 || W <0).

In alcune formulazioni di spazio clip ( tosse OpenGL tosse ), l'intervallo visibile include valori Z positivi e negativi, mentre W è sempre positivo davanti alla telecamera e negativo dietro.


Ho provato con solo W <0, ma ci sono ancora posti in cui è proiettato in modo errato.
Panda Pajama,

0
float screenX = (((projectedVector.X / abs(projectedVector.W)) + 1.0f) / 2.0f) * viewportWidth;
float screenY = ((1.0f - (projectedVector.Y / abs(projectedVector.W))) / 2.0f) * viewportHeight;

E rimuovi questa parte

// Invert X and Y when behind the camera
if (projectedVector.Z < 0 || projectedVector.W < 0)
{
    screenX = -screenX;
    screenY = -screenY;
}

-2

Prova un cartellone pubblicitario, che renderà automaticamente l'immagine rivolta verso la telecamera e disegnerà automaticamente l'immagine sullo schermo nella posizione corretta


3
Hai letto la domanda?
Kromster,

Mi dispiace, lasciatemi spiegare - È possibile prendere l'output di un tabellone per le affissioni, sbarazzarsi del ridimensionamento, (quindi è 2D ma segue la posizione 3d), e quindi tenerlo nei limiti dello schermo usando alcuni matematici a matrice.
Michael Langford,

La domanda riguarda in particolare il "e poi tienilo nei limiti dello schermo usando un po 'di matematica "
Kromster
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.