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"
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:
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):
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:
mentre dovrebbe essere qualcosa di simile:
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):
Poi ho iniziare con il secondo portale:
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:
Non so se sono riuscito a spiegare bene ...