2016-07-09 16 views
5

Ho cercato di realizzare qualcosa di simile al gioco Antichamber (per essere più precisly questa mostrato sotto trick) per la settimana passata:opengl - come implementare "rendering portale"

trick

Ecco un video di ciò che spero di ottenere (anche se è stato fatto con l'Unreal Engine 4: non lo uso): https://www.youtube.com/watch?v=Of3JcoWrMZs

Ho cercato il modo migliore per farlo e ho scoperto lo stencil buffer. Tra l'articolo this e il codice this (la funzione "drawPortals()") che ho trovato online sono riuscito quasi a implementarlo.

Funziona bene con un portale in un'altra stanza (non un portale incrociabile, il che significa che non è possibile attraversarlo ed essere teleportato nell'altra stanza). Nel mio esempio sto disegnando un portale in una semplice stanza quadrata con dentro una sfera; dietro il portale c'è un'altra sfera, che ho usato per controllare se il buffer di profondità funzionava correttamente e tirandolo dietro il portale:

front

side

I problemi sorgono quando aggiungo altro portale che è vicino a questo In tal caso, riesco a visualizzare l'altro portale correttamente (l'illuminazione è fuori, ma la sfera sulla destra è di un colore diverso per mostrare che è un altro ambito):

enter image description here

Ma se mi rivolgo il telecamera in modo che il primo portale deve essere disegnato sopra il secondo, allora la profondità del primo diventa sbagliata, e il secondo portale viene disegnata sopra il primo, in questo modo:

enter image description here

mentre dovrebbe essere qualcosa di simile:

enter image description here

Quindi, questo è il problema. Probabilmente sto facendo qualcosa di sbagliato con il buffer di profondità, ma non riesco a trovare cosa.

Il mio codice per la parte di rendering è più o meno questo:

glClear(GL_DEPTH_BUFFER_BIT); 
glEnable(GL_STENCIL_TEST); 

// First portal 
glPushMatrix(); 

// Disable writing to the color and depht buffer; disable depth testing 
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 
glDepthMask(GL_FALSE); 
glDisable(GL_DEPTH_TEST); 

// Make sure that the stencil always fails 
glStencilFunc(GL_NEVER, 1, 0xFF); 

// On fail, put 1 on the buffer 
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); 

// Enable writing to the stencil buffer 
glStencilMask(0xFF); 

// Clean the buffer 
glClear(GL_STENCIL_BUFFER_BIT); 

// Finally draw the portal's frame, so that it will have only 1s in the stencil buffer; the frame is basically the square you can see in the pictures 
portalFrameObj1.Draw(); 

/* Now I compute the position of the camera so that it will be positioned at the portal's room; the computation is correct, so I'm skipping it */ 

// I'm going to render the portal's room from the new perspective, so I'm going to need the depth and color buffers again 
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 
glDepthMask(GL_TRUE); 
glEnable(GL_DEPTH_TEST); 
glClear(GL_DEPTH_BUFFER_BIT); 

// Disable writing to the stencil buffer and enable drawing only where the stencil values are 1s (so only on the portal frame previously rendered) 
glStencilMask(0x00); 
glStencilFunc(GL_EQUAL, 1, 0xFF); 

// Draw the room from this perspective 
portalRoomObj1.Draw(); 

glPopMatrix(); 


// Now the second portal; the procedure is the same, so I'm skipping the comments 
glPushMatrix(); 
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); 
glDepthMask(GL_FALSE); 
glDisable(GL_DEPTH_TEST); 

glStencilFunc(GL_NEVER, 1, 0xFF); 
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); 
glStencilMask(0xFF); 
glClear(GL_STENCIL_BUFFER_BIT); 

portalFrameObj2.Draw(); 

/* New camera perspective computation */ 

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 
glDepthMask(GL_TRUE); 
glEnable(GL_DEPTH_TEST); 
glClear(GL_DEPTH_BUFFER_BIT); 

glStencilMask(0x00); 
glStencilFunc(GL_EQUAL, 1, 0xFF); 

portalRoomObj2.Draw(); 

glPopMatrix(); 


// Finally, I have to draw the portals' frames once again but this time on the depth buffer, so that they won't get drawn over; first off, disable the stencil buffer 
glDisable(GL_STENCIL_TEST); 

// Disable the color buffer 
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 

glClear(GL_DEPTH_BUFFER_BIT); 

// Draw portals' frames 
portalFrameObj1.Draw(); 
portalFrameObj2.Draw(); 

// Enable the color buffer again 
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 


/* Here I draw the rest of the scene */ 

UPDATE

sono riuscito a scoprire qual è il problema, ma non sono ancora in grado di risolverlo. Non è in realtà collegato al buffer di profondità, ma allo stencil.

Fondamentalmente, il modo in cui sto disegnando il primo portale è questo: 1) Riempire i bit del frame del portale nel buffer stencil con 1s; fuori dal portale ci sono solo 0s 2) Disegna la stanza del portale dove lo stencil ha 1s (in modo che venga disegnato sul portale del frame

E lo ripeto per il secondo portale.

Per il primo portale, ottengo al punto 1 qualcosa di simile allora (pardon disegni stupidi, sono pigro): enter image description here

Poi, dopo la fase 2:
enter image description here

Poi ho iniziare con il secondo portale:
enter image description here

Ma ora, tra la fase 1 e 2, dico lo stencil per disegnare solo quando i bit sono 1s; dal momento che il buffer è ora cancellato, ho perso la traccia degli 1 del primo portale, quindi se una parte del frame del secondo portale è dietro il frame precedente, gli 1s dei secondi frame verranno comunque disegnati con la stanza del secondo portale, indipendentemente da il buffer di profondità. Un po 'come in questa immagine: enter image description here

Non so se sono riuscito a spiegare bene ...

risposta

0

E' passato un po 'di tempo. Dal momento che non ci saranno anwsers (probabilmente) sto scrivendo sul trucco che sto usando ora.

Ho provato a risolvere il problema utilizzando diverse funzioni di stencil e non svuotando il buffer di stencil quando eseguo il rendering di un nuovo portale; l'idea era di sfruttare il fatto che conoscevo osservando il buffer degli stencil dove i precedenti portali venivano sottoposti a rendering.

Alla fine, non sono riuscito a farlo; negli esempi che ho postato nella mia domanda iniziale, uno dei 2 portali ha sempre oscurato parti dell'altro.

Così, ho deciso di fare qualcosa di più semplice: controllo se un portale è visibile, quindi lo disegno esattamente come nel codice che ho postato nella domanda (quindi svuoto il buffer stencil ogni volta).

In pseudo-codice, faccio questo:

for(var i = 0; i < PORTALS_NUMBER; i++) 
{ 
    // I get the normal to the portal's frame and its position 
    var normal = mPortalFramesNormals[i]; 
    var framePos = mPortalFrames[i].GetPosition(); 

    // I compute the scalar product between the normal and the direction vector between the camera's position and the frame's position 
    var dotProduct = normal * (currentCameraPosition - framePos); 

    // If the dot product is 0 or positive, the portal is visible 
    if(dotProduct >= 0) 
    { 
     // I render the portal 
     DrawPortal(mPortalFrames[i], mPortalRooms[i]); 
    } 
} 

glDisable(GL_STENCIL_TEST); 

// Now I draw the portals' frames in the depth buffer, so they don't get overwritten by other objects in the scene 
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 
glDepthMask(GL_TRUE); 
glEnable(GL_DEPTH_TEST); 
glClear(GL_DEPTH_BUFFER_BIT); 

for(var i = 0; i < PORTALS_NUMBER; i++) 
{ 
    mPortalFrames[i].Draw(); 
} 

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 

Nel mio caso, in cui ho bisogno di visualizzare 4 portali in totale posizionati come un cubo (quindi so che al massimo 2 portali sono direttamente visibili a tempo) e solo una faccia del frame visualizza il portale mentre l'altra non lo fa, funziona perfettamente.

So che probabilmente c'è un modo più elegante per farlo usando solo il buffer degli stencil, ma per ora questo funziona per me.

Se qualcuno conosce un modo migliore per farlo, sono comunque interessato a sapere!

EDIT: ho scoperto altre versioni delle funzioni stencil (vale a dire, glStencilFuncSeparate, glStencilOpSeparate, glStencilMaskSeparate) che permettono di fare cose diverse con schiena e lati frontali. Sembra che avrei bisogno di risolvere i problemi che ho descritto, ma non potrò provare a farlo presto. Lo sto solo indicando nel caso qualcuno che abbia il mio stesso problema vaga qui.

Problemi correlati