opengl
framebuffer
Ricerca…
Nozioni di base sui framebuffer
Framebuffer è un tipo di buffer che memorizza i valori dei colori , la profondità e le informazioni sullo stencil dei pixel in memoria. Quando si disegna qualcosa in OpenGL, l'output viene memorizzato nel framebuffer predefinito e quindi si vedono effettivamente i valori dei colori di questo buffer sullo schermo. Puoi anche creare il tuo framebuffer che può essere utilizzato per molti effetti di post-elaborazione come la scala di grigi, la sfocatura, la profondità di campo, le distorsioni, i riflessi ...
Per iniziare devi creare un oggetto framebuffer ( FBO ) e collegarlo come qualsiasi altro oggetto in OpenGL:
unsigned int FBO;
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
Ora devi aggiungere almeno un allegato (colore, profondità o stencil) al framebuffer. Un allegato è un percorso di memoria che funge da buffer per il framebuffer. Può essere una texture o un oggetto renderbuffer . Il vantaggio di usare una texture è che puoi facilmente utilizzare questa texture in shader post-elaborazione. La creazione della trama è simile a una trama normale:
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
La width
e l' height
dovrebbero essere uguali alle dimensioni della finestra di rendering. Il puntatore di dati di trama è NULL
perché si desidera solo allocare la memoria e non riempire la trama con alcun dato. La texture è pronta per poterla allegare al framebuffer:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
Il framebuffer dovrebbe essere pronto per l'uso ora, ma potresti anche voler aggiungere un allegato di profondità o entrambi gli allegati di profondità e stencil. Se vuoi aggiungerli come allegati di texture (e usarli per qualche elaborazione) puoi creare un'altra texture come sopra. L'unica differenza sarebbe in queste righe:
glTexImage2D(
GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0,
GL_DEPTH_COMPONENT, GL_FLOAT, NULL
);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
O questi se si desidera utilizzare l'allegato di profondità e stencil in una singola trama:
glTexImage2D(
GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0,
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL
);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
È inoltre possibile utilizzare un renderbuffer anziché una texture come allegato per i buffer di profondità e stencil se non si desidera elaborare i valori in un secondo momento. (Sarà spiegato in un altro esempio ...)
Puoi verificare se il framebuffer è stato creato e completato correttamente senza errori:
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
// do something...
Infine, non dimenticare di separare il framebuffer in modo da non renderlo accidentalmente visibile:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
limiti
Il numero massimo di buffer colore che possono essere collegati a un singolo frame buffer può essere determinato dalla funzione OGL glGetIntegerv , utilizzando il parametro GL_MAX_COLOR_ATTACHMENTS
:
GLint maxColAttchments = 0;
glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, &maxColAttchments );
Usando il framebuffer
L'utilizzo è abbastanza semplice. Innanzitutto leghi il tuo framebuffer e ti mostri la scena. Ma in realtà non vedrai ancora nulla perché il tuo renderbuffer non è visibile. Quindi la seconda parte è di rendere il framebuffer come una trama di un quad a schermo intero sullo schermo. Puoi semplicemente renderlo così com'è o fare alcuni effetti di post-elaborazione.
Ecco i vertici per un quad a schermo intero:
float vertices[] = {
// positions texture coordinates
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
Dovrai memorizzarli in un VBO o renderizzare usando i puntatori degli attributi. Avrai anche bisogno di un programma shader di base per il rendering del quad a schermo intero con texture.
Vertex shader:
in vec2 position;
in vec2 texCoords;
out vec2 TexCoords;
void main()
{
gl_Position = vec4(position.x, position.y, 0.0, 1.0);
TexCoords = texCoords;
}
Fragment shader:
in vec2 TexCoords;
out vec4 color;
uniform sampler2D screenTexture;
void main()
{
color = texture(screenTexture, TexCoords);
}
Nota: potrebbe essere necessario regolare gli shader per la versione di GLSL .
Ora puoi fare il rendering attuale. Come descritto sopra, la prima cosa è rendere la scena nel tuo FBO. Per farlo basta legare il tuo FBO, cancellarlo e disegnare la scena:
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// draw your scene here...
Nota: nella funzione glClear
è necessario specificare tutti gli allegati framebuffer che si stanno utilizzando (in questo esempio, l'allegato per colore e profondità).
Ora puoi renderizzare il tuo FBO come quad a schermo intero sul framebuffer predefinito in modo che tu possa vederlo. Per fare ciò basta semplicemente smistare il tuo FBO e rendere il quad:
glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind your FBO to set the default framebuffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.Use(); // shader program for rendering the quad
glBindTexture(GL_TEXTURE_2D, texture); // color attachment texture
glBindBuffer(GL_ARRAY_BUFFER, VBO); // VBO of the quad
// You can also use VAO or attribute pointers instead of only VBO...
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
E questo è tutto! Se hai fatto tutto correttamente dovresti vedere la stessa scena di prima ma renderizzata su un quad a schermo intero. L'output visivo è lo stesso di prima, ma ora puoi facilmente aggiungere effetti di post-elaborazione semplicemente modificando lo shader del frammento. (Aggiungerò effetti in un altro esempio (s) e collegarlo qui)