Advanced Image synthesis - Deferred shading
TD5 : Deferred shading
1. Création et remplissage du g-buffer
Afin d’initialiser le FBO (ou g-buffer) on utilise le code source suivant. Dans notre cas on stocke les couleurs, les normales, les coéficients spéculaires et la profondeur dans deux textures. La premiere pour les couleurs et les coefficients spéculaires (rouge, vert, bleu, spéculaire) et la seconde pour les normales et les profondeurs (\(n _x\), \(n _y\), \(n _z\), profondeur). Il faut néanmoins definir un buffer pour la profondeur afin que le DEPTH TEST s’execute comme il faut.
//1. generate a framebuffer object and bind it
glGenFramebuffers(1, &_fboId);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fboId);
//2. init texture
glGenTextures(2, textures);
glGenRenderbuffers(1, &renderbuffer);
for (int i = 0; i < 2; ++i) {
// Bind the newly created texture
glBindTexture(GL_TEXTURE_2D, textures[i]);
// Give an empty image to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLint)GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLint)GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLint)GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLint)GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i], 0);
}
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32F, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
//6. Set the list of draw buffers.
GLenum DrawBuffers[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2, DrawBuffers); // "1" is the size of DrawBuffers
//7. check FBO status
checkFBOAttachment();
//8. switch back to original framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
Grace à ce FBO, si on dessine la scene (toute la geometrie) dans ces buffers on obtient les resultats suivants:
![]() |
![]() |
2. Calcul de l’éclairage
Ainsi, dans les textures du FBO on a donc calculé toutes les informations nécessaire pour “rendre” la scene avec des lumieres. On vas donc utiliser toutes ces informations pour calculer l’éclairage sans devoir tout recalculer pour toute la geometrie (toute la partie du pipeline OpenGL dédié aux sommets et aux primitives). Ainsi dans un second temps on viens juste dessiner, pour chaque lumière, un quad de la même taille que l’affichage afin de generer un fragment par pixel. Grace a ceci on peut facilement évaluer le rendu final de la scene en piochant toutes les informations nécessaires dans le FBO et en calculant l’éclairage pour chaque lampes les une apres les autres. Afin de pouvoir afficher l’eclairage de pludieurs lampes, il faut activer le BLEND et ainsi additioner toute les contributions lumineuses en les ajoutant les unes aux autres.
_fbo.bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
_gbufferPrg.activate();
// draw meshes into the fbo
for(int i=0; i<_shapes.size(); ++i)
{
...
}
_gbufferPrg.deactivate();
_fbo.unbind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable (GL_BLEND);
glBlendEquation (GL_FUNC_ADD);
glBlendFunc (GL_ONE, GL_ONE);
glDisable(GL_DEPTH_TEST);
for (int i = 0; i < 4; ++i) {
// Update light number i
...
_deferredPrg.activate();
// Render the quad with the deferred shaders
// these shaders pick the information into the fbo textures
// and compute the light for all visible fragments
_deferredPrg.deactivate();
}
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
// draw lights in the scene
for (int i = 0; i < 4; ++i) {
...
}
Pour 4 lampes on obtien le résultat suivant (pas très convaincant).
De plus il me semble qu’il y a quelque chose qui cloche avec mon implementation sans pouvoir mettre la main dessus.