Aquí hay un "punto de referencia" FBO Me escribió una while ago. set_fbo_size()
tiene una secuencia de creación que funciona para mí (TM).
/////////////////////////////////////////////////////////////////////////////
// INCLUDES /////////////////////////////////////////////////////////////////
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
#include <iomanip>
#include <sstream>
using namespace std;
/////////////////////////////////////////////////////////////////////////////
// CLASSES //////////////////////////////////////////////////////////////////
// http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
class ExpAvg
{
public:
ExpAvg(float initial, unsigned int time_periods) : avg(initial), alpha(2.0f/(time_periods + 1)) {}
void Update(float nextval) { avg = alpha*nextval + (1.0f-alpha)*avg; }
float Get() { return avg; }
private:
float avg;
float alpha;
};
class gl2D
{
public:
gl2D() {
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();
gluOrtho2D(0, viewport[2], 0, viewport[3]);
glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
}
~gl2D() {
glMatrixMode(GL_PROJECTION); glPopMatrix();
glMatrixMode(GL_MODELVIEW); glPopMatrix();
}
};
/////////////////////////////////////////////////////////////////////////////
// GLOBALS //////////////////////////////////////////////////////////////////
int screen_width = 1024;
int screen_height = 768;
int mouse_x, mouse_y;
bool mouse_left, mouse_right;
float camera_angle_x = 45;
float camera_angle_y = 45;
float camera_distance = 0;
int texture_width, texture_height;
GLuint tex, fbo, rbo; // object IDs
/////////////////////////////////////////////////////////////////////////////
// UTILITIES ////////////////////////////////////////////////////////////////
bool get_fbo_status()
{
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
switch(status) {
case GL_FRAMEBUFFER_COMPLETE_EXT:
cout << "Framebuffer complete." << endl; return true;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
cerr << "[ERROR] Attachment is NOT complete." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
cerr << "[ERROR] No image is attached to FBO." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
cerr << "[ERROR] Attached images have different dimensions." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
cerr << "[ERROR] Color attached images have different internal formats." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
cerr << "[ERROR] Draw buffer." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
cerr << "[ERROR] Read buffer." << endl; return false;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
cerr << "[ERROR] Unsupported by FBO implementation." << endl; return false;
default:
cerr << "[ERROR] Unknow error." << endl; return false;
}
}
void gl_print(const char *str, int x, int y, void *font)
{
glPushAttrib(GL_ENABLE_BIT);
glDisable(GL_LIGHTING); // need to disable lighting for proper text color
glDisable(GL_TEXTURE_2D);
glRasterPos2i(x, y); // place text position
while(*str) glutBitmapCharacter(font, *str++);
glPopAttrib();
}
void textured_cube()
{
glBegin(GL_QUADS);
glColor4f(1, 1, 1, 1);
// face v0-v1-v2-v3
glNormal3f(0,0,1);
glTexCoord2f(1, 1); glVertex3f(1,1,1);
glTexCoord2f(0, 1); glVertex3f(-1,1,1);
glTexCoord2f(0, 0); glVertex3f(-1,-1,1);
glTexCoord2f(1, 0); glVertex3f(1,-1,1);
// face v0-v3-v4-v5
glNormal3f(1,0,0);
glTexCoord2f(0, 1); glVertex3f(1,1,1);
glTexCoord2f(0, 0); glVertex3f(1,-1,1);
glTexCoord2f(1, 0); glVertex3f(1,-1,-1);
glTexCoord2f(1, 1); glVertex3f(1,1,-1);
// face v0-v5-v6-v1
glNormal3f(0,1,0);
glTexCoord2f(1, 0); glVertex3f(1,1,1);
glTexCoord2f(1, 1); glVertex3f(1,1,-1);
glTexCoord2f(0, 1); glVertex3f(-1,1,-1);
glTexCoord2f(0, 0); glVertex3f(-1,1,1);
// face v1-v6-v7-v2
glNormal3f(-1,0,0);
glTexCoord2f(1, 1); glVertex3f(-1,1,1);
glTexCoord2f(0, 1); glVertex3f(-1,1,-1);
glTexCoord2f(0, 0); glVertex3f(-1,-1,-1);
glTexCoord2f(1, 0); glVertex3f(-1,-1,1);
// face v7-v4-v3-v2
glNormal3f(0,-1,0);
glTexCoord2f(0, 0); glVertex3f(-1,-1,-1);
glTexCoord2f(1, 0); glVertex3f(1,-1,-1);
glTexCoord2f(1, 1); glVertex3f(1,-1,1);
glTexCoord2f(0, 1); glVertex3f(-1,-1,1);
// face v4-v7-v6-v5
glNormal3f(0,0,-1);
glTexCoord2f(0, 0); glVertex3f(1,-1,-1);
glTexCoord2f(1, 0); glVertex3f(-1,-1,-1);
glTexCoord2f(1, 1); glVertex3f(-1,1,-1);
glTexCoord2f(0, 1); glVertex3f(1,1,-1);
glEnd();
}
bool set_fbo_size(int width, int height)
{
int max_size;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &max_size);
if(width > max_size) return false;
if(height > max_size) return false;
texture_width = width; texture_height = height;
// create FBO
if(fbo) glDeleteFramebuffersEXT(1, &fbo);
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
// create and attach a new texture as the FBO's color buffer
if(tex) glDeleteTextures(1, &tex);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);
// create and attach a new depth buffer to currently bound FBO
if(rbo) glDeleteRenderbuffersEXT(1, &rbo);
glGenRenderbuffersEXT(1, &rbo);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rbo);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // unbind fbo
if(!get_fbo_status()) exit(1);
return true;
}
/////////////////////////////////////////////////////////////////////////////
// GLUT CALLBACKS ///////////////////////////////////////////////////////////
void CB_Idle()
{
glutPostRedisplay();
}
void CB_Reshape(int width, int height)
{
screen_width = width;
screen_height = height;
glViewport(0, 0, (GLsizei)width, (GLsizei)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f, (float)(width)/height, 1.0f, 1000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void CB_Mouse(int button, int state, int x, int y)
{
mouse_x = x; mouse_y = y;
if(button == GLUT_LEFT_BUTTON)
mouse_left = (state == GLUT_DOWN);
else if(button == GLUT_RIGHT_BUTTON)
mouse_right = (state == GLUT_DOWN);
}
void CB_Motion(int x, int y)
{
if(mouse_left) {
camera_angle_y += (x - mouse_x);
camera_angle_x += (y - mouse_y);
mouse_x = x; mouse_y = y;
}
if(mouse_right) {
camera_distance += (y - mouse_y) * 0.2f;
mouse_y = y;
}
}
void CB_Keyboard(unsigned char key, int x, int y)
{
static int drawMode = 0;
static int tex_size = 0;
bool ret = false;
switch(key) {
case 27: // ESCAPE
exit(0);
break;
case ' ':
while(!ret) {
tex_size = (tex_size+1) % 7;
switch(tex_size) {
case 0: ret = set_fbo_size(128,128); break;
case 1: ret = set_fbo_size(256,256); break;
case 2: ret = set_fbo_size(512,512); break;
case 3: ret = set_fbo_size(1024,1024); break;
case 4: ret = set_fbo_size(2048,2048); break;
case 5: ret = set_fbo_size(4096,4096); break;
case 6: ret = set_fbo_size(8192,8192); break;
default: ; break;
}
}
break;
default:
break;
}
glutPostRedisplay();
}
void CB_Init()
{
GLenum err = glewInit();
if(GLEW_OK != err) {
cerr << "Error: " << glewGetErrorString(err) << endl;
exit(1);
}
if(!GLEW_EXT_framebuffer_object) {
cerr << "Requires EXT_framebuffer_object" << endl;
exit(1);
}
tex = fbo = rbo = 0;
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glEnable(GL_CULL_FACE);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glClearColor(0, 0, 0, 0);
GLfloat lightKa[] = {.2f, .2f, .2f, 1.0f}; // ambient light
GLfloat lightKd[] = {.7f, .7f, .7f, 1.0f}; // diffuse light
GLfloat lightKs[] = {1, 1, 1, 1}; // specular light
glLightfv(GL_LIGHT0, GL_AMBIENT, lightKa);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightKd);
glLightfv(GL_LIGHT0, GL_SPECULAR, lightKs);
float lightPos[4] = {0, 0, 20, 1}; // positional light
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
glEnable(GL_LIGHT0);
set_fbo_size(128, 128);
}
void CB_Exit()
{
glDeleteTextures(1, &tex);
glDeleteFramebuffersEXT(1, &fbo);
glDeleteRenderbuffersEXT(1, &rbo);
}
void CB_Display()
{
static ExpAvg ft_fbo(0, 19);
static ExpAvg ft_overall(0, 19);
int before = glutGet(GLUT_ELAPSED_TIME);
// compute rotation angle
const float ANGLE_SPEED = 90; // degree/s
float angle = ANGLE_SPEED * (glutGet(GLUT_ELAPSED_TIME)/1000.0f);
// render using fbo /////////////////////////////////////////////
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // bind fbo
// adjust viewport and projection matrix to texture dimension
glViewport(0, 0, texture_width, texture_height);
glMatrixMode(GL_PROJECTION); glLoadIdentity();
gluPerspective(60.0f, (float)(texture_width)/texture_height, 1.0f, 100.0f);
glMatrixMode(GL_MODELVIEW); glLoadIdentity();
// clear buffer
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(0,0,-3);
glPushMatrix();
glRotatef(angle*0.5f, 1, 0, 0);
glRotatef(angle, 0, 1, 0);
glRotatef(angle*0.7f, 0, 0, 1);
// set up teapot colors
float shininess = 15.0f;
float diffuseColor[3] = {0.929524f, 0.796542f, 0.178823f};
float specularColor[4] = {1.00000f, 0.980392f, 0.549020f, 1.0f};
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess); // range 0 ~ 128
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularColor);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glColor3fv(diffuseColor);
glBindTexture(GL_TEXTURE_2D, 0);
glFrontFace(GL_CW);
glutSolidTeapot(1.0);
glFrontFace(GL_CCW);
glPopMatrix();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // unbind fbo
glFinish();
ft_fbo.Update((float)(glutGet(GLUT_ELAPSED_TIME) - before));
// normal rendering ///////////////////////////////////
// back to normal viewport and projection matrix
glViewport(0, 0, screen_width, screen_height);
glMatrixMode(GL_PROJECTION); glLoadIdentity();
gluPerspective(60.0f, (float)(screen_width)/screen_height, 1.0f, 100.0f);
glMatrixMode(GL_MODELVIEW); glLoadIdentity();
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(0,0,-4);
glPushMatrix();
glTranslatef(0, 0, camera_distance);
glRotatef(camera_angle_x, 1, 0, 0);
glRotatef(camera_angle_y, 0, 1, 0);
// draw a cube with the dynamic texture
glBindTexture(GL_TEXTURE_2D, tex);
textured_cube();
glPopMatrix();
{
gl2D two_dee; // set 2D mode
glDisable(GL_DEPTH_TEST);
stringstream ss;
glColor3f(1,1,0);
ss << fixed << setprecision(3);
int pos = 1;
ss.str(""); ss << "Texture size: " << texture_width << "x" << texture_height;
gl_print(ss.str().c_str(), 10, screen_height - (pos++ * 20), GLUT_BITMAP_8_BY_13);
ss.str(""); ss << "Overall frame time: " << ft_overall.Get() << " ms";
gl_print(ss.str().c_str(), 10, screen_height - (pos++ * 20), GLUT_BITMAP_8_BY_13);
ss.str(""); ss << " FBO frame time: " << ft_fbo.Get() << " ms";
gl_print(ss.str().c_str(), 10, screen_height - (pos++ * 20), GLUT_BITMAP_8_BY_13);
ss.str(""); ss << "Press space to change texture size; mouse moves/zooms cube";
gl_print(ss.str().c_str(), 10, 10, GLUT_BITMAP_8_BY_13);
glEnable(GL_DEPTH_TEST);
}
glutSwapBuffers();
ft_overall.Update((float)(glutGet(GLUT_ELAPSED_TIME) - before));
}
/////////////////////////////////////////////////////////////////////////////
// MAIN /////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(screen_width, screen_height);
glutInitWindowPosition(100, 100);
glutCreateWindow("FBO Test");
glutDisplayFunc(CB_Display);
glutIdleFunc(CB_Idle);
glutReshapeFunc(CB_Reshape);
glutKeyboardFunc(CB_Keyboard);
glutMouseFunc(CB_Mouse);
glutMotionFunc(CB_Motion);
atexit(CB_Exit);
CB_Init();
glutMainLoop();
return 0;
}
¿Dónde creas 'fbTexID'? ¿Dónde usas 'tex'? – genpfault
Tanto fbTexID como tex son solo GLuints, definidos anteriormente. (En mi código actual son miembros de una estructura más grande, sustituí los nombres más simples en mi publicación aquí por simplicidad y claridad. Gracias. – RedMarbleGames
Solo para lectores futuros y porque acabo de tropezar con esto: Multisampling y FBO podrían producir problemas, también. –