Now before you go through the FBO settings, first check if your computer capable of doing so. I use the code from FBOBunnies example program, that can be downloaded from Apple's site.
// checking the FBO support
int supported = 1;
sscanf((char *)glGetString(GL_VERSION), "%f", &glCoreVersion);
printf("%s %s\n", (char *)glGetString(GL_RENDERER), (char*)glGetString(GL_VERSION));
printf("----------------------------------\n");
int j = sizeof(extension)/sizeof(glExtension);
for (int i = 0; i < j; i++) {
extension[i].supported = glutExtensionSupported(extension[i].name) |
(extension[i].promoted && (glCoreVersion >= extension[i].promoted));
printf("%-32s %d\n", extension[i].name, extension[i].supported);
supported &= extension[i].supported;
}
printf("----------------------------------\n");
if (!supported) {
printf("Required functionality not available on this renderer.\n");
// A robust app could fall back to other methods here, like glCopyTexImage.
// This is just a demo, so quit.
exit(0);
}
// end of checking FBO support
If your machine is compatible, it should print '1' at the end of the line. Now here is the result of my image:
So here is how I did the FBO settings. First I created four GLuints as buffer id, and generate the ID using this syntax:
glGenFramebuffersEXT(1, &fb1);
glGenRenderbuffersEXT(1, &rb1);
glGenTextures(1, &tx1);
glGenTextures(1, &tx2);
Then I set the properties for the two texture as usual:
// first texture for the diffuse albedo, type 8 bit RGBA
glBindTexture(GL_TEXTURE_2D, tx1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// second texture for the normal-rgba, type 8 bit RGB
glBindTexture(GL_TEXTURE_2D, tx2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// render buffer, kept as texture to keep the depth information,
// later attached as texture
// later attached as texture
glGenTextures(1, &rb1);
glBindTexture(GL_TEXTURE_2D, rb1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, rb1, 0);
Now we have to attach those 'textures' to the FBO, notice that the first texture attached as GL_COLOR_ATTACHMENT0_EXT and the second one is GL_COLOR_ATTACHMENT1_EXT. This is important since FBO need to differ the first and the second texture attached to it.
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb1);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D,tx1,0);
GL_TEXTURE_2D,tx1,0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT1_EXT,
GL_TEXTURE_2D,tx2,0);
GL_TEXTURE_2D,tx2,0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_DEPTH_ATTACHMENT_EXT,
GL_TEXTURE_2D,rb1,0);
GL_TEXTURE_2D,rb1,0);
status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
cout << "Error in setting up FBO" << endl;
// draw real scene inside fbo
glEnable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
glClearColor(0.2, 0.2, 0.2, 1.0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb1);
// first we draw the diffuse albedo (tx1) and depth (rb1)
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
drawTriangles();
glPopMatrix();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tx1);
glGenerateMipmapEXT(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, rb1);
glGenerateMipmapEXT(GL_TEXTURE_2D);
To render the second texture (normal), we have to override the rendering pipeline using shaders. Here are the vertex and the fragment shader used to render the normal texture:
//vert
varying vec3 normal;
void main()
{
normal = normalize(gl_NormalMatrix * gl_Normal);;
gl_Position = ftransform();
}
//frag
varying vec3 normal;
void main() {
gl_FragColor = vec4(normal.x, normal.y, normal.z, 1.0);
}
// then we draw the normal-rgb
glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.9, 0.9, 0.9, 1.0);
glPushMatrix();
glUseProgram(shaderId[1]);
drawTriangles();
glUseProgram(0);
glPopMatrix();
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, tx2);
glGenerateMipmapEXT(GL_TEXTURE_2D);
Now we are ready to render the scene. Since we have textures, we need to generate a polygon to be attached with those textures. We use a simple polygon from -1 to 1 (top bottom left right) and modify the glLookAt so we can properly view the whole polygon. Since we use shaders, we need to define the value of each texture ID, in this case is 1, 2, and 3 for diffuse, depth, and normal respectively.
// draw the scene on a polygon
glPushMatrix();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0.0, 0.0, 2.4,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
glUseProgram(shaderId[0]);
glUniform1i(glGetUniformLocation(shaderId[0], "texture0"), 1);
glBegin(GL_QUADS);
{
glTexCoord2f(0, 0); glVertex3f(-1.0, -1.0, 0.0);
glTexCoord2f(1.0, 0); glVertex3f(0.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(0.0, 0.0, 0.0);
glTexCoord2f(0, 1.0); glVertex3f(-1.0, 0.0, 0.0);
}
glEnd();
glUseProgram(shaderId[2]);
glUniform1i(glGetUniformLocation(shaderId[2], "texture0"), 0);
glBegin(GL_QUADS);
{
glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(0.0, 1.0, 0.0);
glTexCoord2f(0.0, 0.0); glVertex3f(0.0, 0.0, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 0.0, 0.0);
}
glEnd();
glUniform1i(glGetUniformLocation(shaderId[2], "texture0"), 2);
glBegin(GL_QUADS);
{
glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 0.0, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(0.0, 0.0, 0.0);
glTexCoord2f(0.0, 0.0); glVertex3f(0.0, -1.0, 0.0);
}
glEnd();
So there you go. Multitexture FBO. Here is the whole code at the main class:
http://pastie.org/private/1hsoqzcrb3keg5imgs4jg
If you need the whole project, feel free to contact me!
1 comment:
Amazing job! Thanks a lot for this incredibly useful tutorial! I've struggled for weeks to find a good and simple tutorial to start with multipass-rendering using FBO and then I've found it! Thanks a lot!
Post a Comment