Viewport multipli con OpenGL moderno?


9

Uso SDL2 .

Attualmente il mio unico shader ha una matrice MVP e trasforma i punti con essa.

Calcolo la mia matrice di visualizzazione e proiezione per una fotocamera con GLM:

glm::lookAt(pos, pos + forward, up);
glm::perspective(fov, aspectRatio, zNear, zFar);

L'ho cercato, ma riesco a trovare solo l'implementazione legacy di più finestre.

Se creo ad esempio 4 telecamere e diciamo che sono tutte uguali , tranne per il fatto che ogni telecamera ha un quarto diverso dello schermo per il rendering. (Quindi voglio 4 viewport con lo stesso contenuto)

Come posso fare questo? Scusate se non ho dato abbastanza informazioni, non esitate a chiedere.


Stai specificatamente cercando di eseguire il rendering 4 volte, o semplicemente di renderlo una volta e visualizzare il risultato in 4 luoghi diversi?
trichoplax,

Eseguire il rendering 4 volte. Quello che ho scritto è solo un esempio semplificato, ad esempio voglio che la fotocamera principale esegua il rendering di un'auto a tutta la finestra, e un'altra fotocamera rende l'auto dal lato in una piccola piazza in uno degli angoli.
Tudvari,

Se ho capito bene, quindi è necessario dividere la finestra per esempio in 4 parti e in ciascuna parte renderizzare altre parti della scena in quel modo ?
nartece

Sì, ma non rigorosamente in 4 parti. Voglio che sia flessibile. Dimensioni e posizione flessibili del rettangolo della finestra.
Tudvari,

Risposte:


8

Il rendering su diversi viewport (parti) della stessa schermata può essere eseguito come segue:

Ad esempio, dividere lo schermo in quattro parti e renderizzare la stessa scena quattro volte in ciascun angolo con uniformi diverse e finestre diverse:

bindFramebuffer();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
scene->setConstPerFrameUniforms();

//left bottom
glViewport(0, 0, WindowWidth*0.5, WindowHeight*0.5);
scene->setVarPerFrameUniforms(1);
scene->draw();

//right bottom
glViewport(WindowWidth*0.5, 0, WindowWidth*0.5, WindowHeight*0.5);
scene->setVarPerFrameUniforms(2);
scene->draw();

//left top
glViewport(0, WindowHeight*0.5, WindowWidth*0.5, WindowHeight*0.5);
scene->setVarPerFrameUniforms(3);
scene->draw();

//right top
glViewport(WindowWidth*0.5, WindowHeight*0.5, WindowWidth*0.5, WindowHeight*0.5);
scene->setVarPerFrameUniforms(4);
scene->draw();

glViewport(0, 0, WindowWidth, WindowHeight); //restore default

Quindi con glViewPort () posso definire dove voglio disegnare successivamente, e con glBlendFunc posso definire come la GPU dovrebbe fondere le aree sovrapposte (framebuffer) l'una con l'altra. Ho ragione?
Tudvari,

1
Sì, sei libero di parametrarlo. Come parametri di viewportsono x, y dell'angolo inferiore sinistro del blocco e della larghezza, altezza del blocco. Con la miscelazione puoi sperimentare.
nartece

1
perché mescolare? come si collega alla domanda?
Raxvan,

3
Non hai bisogno di miscelazione o framebuffer multipli. Il rendering non scriverà su alcun pixel al di fuori dell'attuale rettangolo glViewport, quindi puoi semplicemente impostare e disegnare ogni finestra a turno. A proposito, puoi anche usare il test a forbice per limitare gli schiariti a un certo rettangolo, nel caso in cui desideri sovrapporre finestre.
Nathan Reed,

2
Il blending non ha nulla a che fare con questo e rende la risposta più confusa. L'impostazione dei viewport è tutto ciò che serve
impostazione predefinita

11

Se stai scrivendo il tuo Vertex / Fragment Shader c'è un'altra possibilità aggiuntiva per farlo. È molto più complicato ma potrebbe essere utile per te e / o gli altri. Inoltre, accelera l'intero processo di disegno poiché utilizza solo una chiamata a un comando di disegno. Il numero massimo di finestre è definito da GL_MAX_VIEWPORTS ed è generalmente di 16.

Da OpenGL 4.1 esiste il metodo glViewportArrayv . Può definire una matrice di finestre. Alle finestre create con questo metodo è assegnato un indice.

Questo indice può essere utilizzato in Vertex Shader per impostare la finestra su cui viene eseguito il rendering della scena. (Devi includere l' estensione " ARB_shader_viewport_layer_array " nel codice shader)

Nel tuo caso, suggerirei di fare quanto segue:

  • Conserva le 4 matrici della videocamera in un buffer di archiviazione shader (array di mat4) per averle nel Vertex Shader
  • Usa il disegno indicizzato , ad esempio: glDrawElementsInstanced
  • usa la build in gl_InstanceID di Vertex Shader per accedere all'array matrice della telecamera
  • imposta la variabile di output variabile di generazione gl_ViewportIndex in Fragment Shader su gl_InstanceID. (per dettagli vedi ARB_fragment_layer_viewport )

" Questo indice può essere utilizzato in Vertex Shader per impostare il Viewport su cui viene eseguito il rendering della scena. " No, non può. Bene, non senza l'estensione ARB_shader_viewport_layer_array o gli equivalenti AMD. Nessuno di questi è standard in GL 4.5. Stai forse pensando a Geometry Shaders.
Nicol Bolas,

@Nicol, grazie per questo suggerimento! Ho dimenticato di dire che devi includere l'estensione. Modificherò la mia risposta.
Christian_B,

5

Questa è una copia della risposta di @ narthex, tranne che con solo i viewport poiché è tutto ciò di cui hai bisogno. Non sono sicuro del motivo per cui il buffer di frame / materiale di fusione è incluso nella sua risposta.

//left bottom
glViewport(0, 0, WindowWidth*0.5, WindowHeight*0.5);
scene->draw();

//right bottom
glViewport(WindowWidth*0.5, 0, WindowWidth*0.5, WindowHeight*0.5);
scene->draw();

//left top
glViewport(0, WindowHeight*0.5, WindowWidth*0.5, WindowHeight*0.5);
scene->draw();

//right top
glViewport(WindowWidth*0.5, WindowHeight*0.5, WindowWidth*0.5, WindowHeight*0.5);
scene->draw();

glViewport(0, 0, WindowWidth, WindowHeight); //restore default

1

Queste sono tutte buone risposte, eppure c'è un altro modo: (Non c'è sempre?)

A causa della crescente popolarità della realtà virtuale, la gente di OculusVR ha sviluppato un trio di estensioni "Multiview" chiamate:

  1. OVR_multiview ,
  2. OVR_multiview2 , &
  3. OVR_multiview_multisampled_render_to_texture

Queste estensioni consentono di visualizzare più viste della stessa scena in una singola chiamata di disegno, eliminando la ridondanza del rendering della stessa scena nello stesso stato dal punto di vista di ciascun occhio. Sebbene creato ai fini della realtà virtuale, lo sviluppatore non è necessariamente limitato a due sole visualizzazioni. Piuttosto, questa estensione consente tutte le visualizzazioni specificate da MAX_VIEWS_OVR .

Prima di utilizzare queste estensioni, lo sviluppatore dovrebbe verificare il supporto nel driver grafico dell'utente aggiungendo il seguente codice:

const GLubyte* extensions = GL_CHECK( glGetString( GL_EXTENSIONS ) );
char * found_extension = strstr( (const char*)extensions, "GL_OVR_multiview" );
if (NULL == found_extension)
{
     exit( EXIT_FAILURE );
}

Da lì, si tratta di configurare il tuo framebuffer per utilizzare questa funzione:

glFramebufferTextureMultisampledMultiviewOVR = PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEDMULTIVIEWOVR(eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR"));
glFramebufferTextureMultisampledMultiviewOVR (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureID, 0, 0, 2);

e allo stesso modo nello shader:

#version 300 es
#extension GL_OVR_multiview : enable

layout(num_views = 2) in;
in vec3 vertexPosition;
uniform mat4 MVP[2];

void main(){
    gl_Position = MVP[gl_ViewID_OVR] * vec4(vertexPosition, 1.0f);
}

In un'applicazione associata alla CPU, questa estensione può ridurre drasticamente i tempi di rendering, specialmente con scene più complesse :

Tempo di CPU relativo tra multiview e stereo normale.  Più piccolo è il migliore, con il numero di cubi sull'asse xe il tempo relativo sull'asse y.

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.