/////////////////////////////////////////////////////////////////////////////// // main.cpp // ======== // testing Frame Buffer Object (FBO) for "Render To Texture" with MSAA // OpenGL draws the scene directly to a texture object. // // GL_EXT_framebuffer_object extension is promoted to a core feature of OpenGL // version 3.0 (GL_ARB_framebuffer_object) // // AUTHOR: Song Ho Ahn (song.ahn@gmail.com) // CREATED: 2008-05-16 // UPDATED: 2016-11-14 /////////////////////////////////////////////////////////////////////////////// // in order to get function prototypes from glext.h, define GL_GLEXT_PROTOTYPES before including glext.h #define GL_GLEXT_PROTOTYPES #ifdef __APPLE__ #include #else #include #endif #include #include #include #include #include #include "glext.h" #include "glInfo.h" // glInfo struct #include "Timer.h" #include "teapot.h" using std::stringstream; using std::string; using std::cout; using std::endl; using std::ends; // GLUT CALLBACK functions //////////////////////////////////////////////////// void displayCB(); void reshapeCB(int w, int h); void timerCB(int millisec); void idleCB(); void keyboardCB(unsigned char key, int x, int y); void mouseCB(int button, int stat, int x, int y); void mouseMotionCB(int x, int y); // CALLBACK function when exit() called /////////////////////////////////////// void exitCB(); // function declearations ///////////////////////////////////////////////////// void initGL(); int initGLUT(int argc, char **argv); bool initSharedMem(); void clearSharedMem(); void initLights(); void setCamera(float posX, float posY, float posZ, float targetX, float targetY, float targetZ); void drawString(const char *str, int x, int y, float color[4], void *font); void drawString3D(const char *str, float pos[3], float color[4], void *font); void showInfo(); void showFPS(); void toOrtho(); void toPerspective(); void draw(); // FBO utils bool checkFramebufferStatus(GLuint fbo); void printFramebufferInfo(GLuint fbo); std::string convertInternalFormatToString(GLenum format); std::string getTextureParameters(GLuint id); std::string getRenderbufferParameters(GLuint id); // constants const int SCREEN_WIDTH = 400; const int SCREEN_HEIGHT = 300; const float CAMERA_DISTANCE = 6.0f; const int TEXT_WIDTH = 8; const int TEXT_HEIGHT = 13; const int TEXTURE_WIDTH = 256; // NOTE: texture size cannot be larger than const int TEXTURE_HEIGHT = 256; // the rendering window size in non-FBO mode // global variables GLuint fboId; // ID of FBO GLuint textureId; // ID of texture GLuint rboColorId, rboDepthId; // IDs of Renderbuffer objects void *font = GLUT_BITMAP_8_BY_13; int screenWidth; int screenHeight; bool mouseLeftDown; bool mouseRightDown; float mouseX, mouseY; float cameraAngleX; float cameraAngleY; float cameraDistance; bool fboSupported; bool fboUsed; int fboSampleCount; int drawMode; Timer timer, t1; float playTime; // to compute rotation angle float renderToTextureTime; // elapsed time for render-to-texture // function pointers for FBO extension // Windows needs to get function pointers from ICD OpenGL drivers, // because opengl32.dll does not support extensions higher than v1.1. #ifdef _WIN32 // ARB Framebuffer object PFNGLGENFRAMEBUFFERSPROC pglGenFramebuffers = 0; // FBO name generation procedure PFNGLDELETEFRAMEBUFFERSPROC pglDeleteFramebuffers = 0; // FBO deletion procedure PFNGLBINDFRAMEBUFFERPROC pglBindFramebuffer = 0; // FBO bind procedure PFNGLCHECKFRAMEBUFFERSTATUSPROC pglCheckFramebufferStatus = 0; // FBO completeness test procedure PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC pglGetFramebufferAttachmentParameteriv = 0; // return various FBO parameters PFNGLGENERATEMIPMAPPROC pglGenerateMipmap = 0; // FBO automatic mipmap generation procedure PFNGLFRAMEBUFFERTEXTURE1DPROC pglFramebufferTexture1D = 0; // FBO texdture attachement procedure PFNGLFRAMEBUFFERTEXTURE2DPROC pglFramebufferTexture2D = 0; // FBO texdture attachement procedure PFNGLFRAMEBUFFERTEXTURE3DPROC pglFramebufferTexture3D = 0; // FBO texdture attachement procedure PFNGLFRAMEBUFFERTEXTURELAYERPROC pglFramebufferTextureLayer = 0; // FBO texdture attachement procedure PFNGLFRAMEBUFFERRENDERBUFFERPROC pglFramebufferRenderbuffer = 0; // FBO renderbuffer attachement procedure PFNGLISFRAMEBUFFERPROC pglIsFramebuffer = 0; // FBO state = true/false PFNGLBLITFRAMEBUFFERPROC pglBlitFramebuffer = 0; // FBO copy // Renderbuffer object PFNGLGENRENDERBUFFERSPROC pglGenRenderbuffers = 0; // renderbuffer generation procedure PFNGLDELETERENDERBUFFERSPROC pglDeleteRenderbuffers = 0; // renderbuffer deletion procedure PFNGLBINDRENDERBUFFERPROC pglBindRenderbuffer = 0; // renderbuffer bind procedure PFNGLRENDERBUFFERSTORAGEPROC pglRenderbufferStorage = 0; // renderbuffer memory allocation procedure PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC pglRenderbufferStorageMultisample = 0; // renderbuffer memory allocation with multisample PFNGLGETRENDERBUFFERPARAMETERIVPROC pglGetRenderbufferParameteriv = 0; // return various renderbuffer parameters PFNGLISRENDERBUFFERPROC pglIsRenderbuffer = 0; // determine renderbuffer object type #define glGenFramebuffers pglGenFramebuffers #define glDeleteFramebuffers pglDeleteFramebuffers #define glBindFramebuffer pglBindFramebuffer #define glCheckFramebufferStatus pglCheckFramebufferStatus #define glGetFramebufferAttachmentParameteriv pglGetFramebufferAttachmentParameteriv #define glGenerateMipmap pglGenerateMipmap #define glFramebufferTexture1D pglFramebufferTexture1D #define glFramebufferTexture2D pglFramebufferTexture2D #define glFramebufferTexture3D pglFramebufferTexture3D #define glFramebufferTextureLayer pglFramebufferTextureLayer #define glFramebufferRenderbuffer pglFramebufferRenderbuffer #define glIsFramebuffer pglIsFramebuffer #define glBlitFramebuffer pglBlitFramebuffer #define glGenRenderbuffers pglGenRenderbuffers #define glDeleteRenderbuffers pglDeleteRenderbuffers #define glBindRenderbuffer pglBindRenderbuffer #define glRenderbufferStorage pglRenderbufferStorage #define glRenderbufferStorageMultisample pglRenderbufferStorageMultisample #define glGetRenderbufferParameteriv pglGetRenderbufferParameteriv #define glIsRenderbuffer pglIsRenderbuffer #endif // function pointers for WGL_EXT_swap_control #ifdef _WIN32 typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void); PFNWGLSWAPINTERVALEXTPROC pwglSwapIntervalEXT = 0; PFNWGLGETSWAPINTERVALEXTPROC pwglGetSwapIntervalEXT = 0; #define wglSwapIntervalEXT pwglSwapIntervalEXT #define wglGetSwapIntervalEXT pwglGetSwapIntervalEXT #endif /////////////////////////////////////////////////////////////////////////////// // draw a textured cube with GL_TRIANGLES /////////////////////////////////////////////////////////////////////////////// void draw() { glBindTexture(GL_TEXTURE_2D, textureId); glColor4f(1, 1, 1, 1); glBegin(GL_TRIANGLES); // front faces glNormal3f(0,0,1); // face v0-v1-v2 glTexCoord2f(1,1); glVertex3f(1,1,1); glTexCoord2f(0,1); glVertex3f(-1,1,1); glTexCoord2f(0,0); glVertex3f(-1,-1,1); // face v2-v3-v0 glTexCoord2f(0,0); glVertex3f(-1,-1,1); glTexCoord2f(1,0); glVertex3f(1,-1,1); glTexCoord2f(1,1); glVertex3f(1,1,1); // right faces glNormal3f(1,0,0); // face v0-v3-v4 glTexCoord2f(0,1); glVertex3f(1,1,1); glTexCoord2f(0,0); glVertex3f(1,-1,1); glTexCoord2f(1,0); glVertex3f(1,-1,-1); // face v4-v5-v0 glTexCoord2f(1,0); glVertex3f(1,-1,-1); glTexCoord2f(1,1); glVertex3f(1,1,-1); glTexCoord2f(0,1); glVertex3f(1,1,1); // top faces glNormal3f(0,1,0); // face v0-v5-v6 glTexCoord2f(1,0); glVertex3f(1,1,1); glTexCoord2f(1,1); glVertex3f(1,1,-1); glTexCoord2f(0,1); glVertex3f(-1,1,-1); // face v6-v1-v0 glTexCoord2f(0,1); glVertex3f(-1,1,-1); glTexCoord2f(0,0); glVertex3f(-1,1,1); glTexCoord2f(1,0); glVertex3f(1,1,1); // left faces glNormal3f(-1,0,0); // face v1-v6-v7 glTexCoord2f(1,1); glVertex3f(-1,1,1); glTexCoord2f(0,1); glVertex3f(-1,1,-1); glTexCoord2f(0,0); glVertex3f(-1,-1,-1); // face v7-v2-v1 glTexCoord2f(0,0); glVertex3f(-1,-1,-1); glTexCoord2f(1,0); glVertex3f(-1,-1,1); glTexCoord2f(1,1); glVertex3f(-1,1,1); // bottom faces glNormal3f(0,-1,0); // face v7-v4-v3 glTexCoord2f(0,0); glVertex3f(-1,-1,-1); glTexCoord2f(1,0); glVertex3f(1,-1,-1); glTexCoord2f(1,1); glVertex3f(1,-1,1); // face v3-v2-v7 glTexCoord2f(1,1); glVertex3f(1,-1,1); glTexCoord2f(0,1); glVertex3f(-1,-1,1); glTexCoord2f(0,0); glVertex3f(-1,-1,-1); // back faces glNormal3f(0,0,-1); // face v4-v7-v6 glTexCoord2f(0,0); glVertex3f(1,-1,-1); glTexCoord2f(1,0); glVertex3f(-1,-1,-1); glTexCoord2f(1,1); glVertex3f(-1,1,-1); // face v6-v5-v4 glTexCoord2f(1,1); glVertex3f(-1,1,-1); glTexCoord2f(0,1); glVertex3f(1,1,-1); glTexCoord2f(0,0); glVertex3f(1,-1,-1); glEnd(); glBindTexture(GL_TEXTURE_2D, 0); } /////////////////////////////////////////////////////////////////////////////// int main(int argc, char **argv) { // init global vars initSharedMem(); // register exit callback atexit(exitCB); // init GLUT and GL initGLUT(argc, argv); initGL(); // create a texture object glGenTextures(1, &textureId); glBindTexture(GL_TEXTURE_2D, textureId); 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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap generation included in OpenGL v1.4 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glBindTexture(GL_TEXTURE_2D, 0); // get OpenGL info glInfo glInfo; glInfo.getInfo(); glInfo.printSelf(); #ifdef _WIN32 // check if FBO is supported by your video card if(glInfo.isExtensionSupported("GL_ARB_framebuffer_object")) { // get pointers to GL functions glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffers"); glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)wglGetProcAddress("glDeleteFramebuffers"); glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer"); glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)wglGetProcAddress("glCheckFramebufferStatus"); glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)wglGetProcAddress("glGetFramebufferAttachmentParameteriv"); glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)wglGetProcAddress("glGenerateMipmap"); glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)wglGetProcAddress("glFramebufferTexture1D"); glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2D"); glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)wglGetProcAddress("glFramebufferTexture3D"); glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)wglGetProcAddress("glFramebufferTextureLayer"); glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)wglGetProcAddress("glFramebufferRenderbuffer"); glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)wglGetProcAddress("glIsFramebuffer"); glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)wglGetProcAddress("glBlitFramebuffer"); glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)wglGetProcAddress("glGenRenderbuffers"); glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)wglGetProcAddress("glDeleteRenderbuffers"); glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)wglGetProcAddress("glBindRenderbuffer"); glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)wglGetProcAddress("glRenderbufferStorage"); glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)wglGetProcAddress("glRenderbufferStorageMultisample"); glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)wglGetProcAddress("glGetRenderbufferParameteriv"); glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)wglGetProcAddress("glIsRenderbuffer"); // check once again FBO extension if(glGenFramebuffers && glDeleteFramebuffers && glBindFramebuffer && glCheckFramebufferStatus && glGetFramebufferAttachmentParameteriv && glGenerateMipmap && glFramebufferTexture1D && glFramebufferTexture2D && glFramebufferTexture3D && glFramebufferTextureLayer && glFramebufferRenderbuffer && glIsFramebuffer && glBlitFramebuffer && glGenRenderbuffers && glDeleteRenderbuffers && glBindRenderbuffer && glRenderbufferStorage && glRenderbufferStorageMultisample && glGetRenderbufferParameteriv && glIsRenderbuffer) { fboSupported = fboUsed = true; std::cout << "Video card supports GL_ARB_framebuffer_object." << std::endl; } else { fboSupported = fboUsed = false; std::cout << "Video card does NOT support GL_ARB_framebuffer_object." << std::endl; } } // check EXT_swap_control is supported if(glInfo.isExtensionSupported("WGL_EXT_swap_control")) { // get pointers to WGL functions wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT"); if(wglSwapIntervalEXT && wglGetSwapIntervalEXT) { // disable v-sync wglSwapIntervalEXT(0); std::cout << "Video card supports WGL_EXT_swap_control." << std::endl; } } #else // for linux, do not need to get function pointers, it is up-to-date if(glInfo.isExtensionSupported("GL_ARB_framebuffer_object")) { fboSupported = fboUsed = true; std::cout << "Video card supports GL_ARB_framebuffer_object." << std::endl; } else { fboSupported = fboUsed = false; std::cout << "Video card does NOT support GL_ARB_framebuffer_object." << std::endl; } #endif if(fboSupported) { // create a framebuffer object, you need to delete them when program exits. glGenFramebuffers(1, &fboId); glBindFramebuffer(GL_FRAMEBUFFER, fboId); // create a renderbuffer object to store depth info // NOTE: A depth renderable image should be attached the FBO for depth test. // If we don't attach a depth renderable image to the FBO, then // the rendering output will be corrupted because of missing depth test. // If you also need stencil test for your rendering, then you must // attach additional image to the stencil attachement point, too. glGenRenderbuffers(1, &rboDepthId); glBindRenderbuffer(GL_RENDERBUFFER, rboDepthId); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, TEXTURE_WIDTH, TEXTURE_HEIGHT); //glRenderbufferStorageMultisample(GL_RENDERBUFFER, fboSampleCount, GL_DEPTH_COMPONENT, TEXTURE_WIDTH, TEXTURE_HEIGHT); glBindRenderbuffer(GL_RENDERBUFFER, 0); // attach a texture to FBO color attachement point glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0); //glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0); // attach a renderbuffer to depth attachment point glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepthId); //@@ disable color buffer if you don't attach any color buffer image, //@@ for example, rendering the depth buffer only to a texture. //@@ Otherwise, glCheckFramebufferStatus will not be complete. //glDrawBuffer(GL_NONE); //glReadBuffer(GL_NONE); // check FBO status printFramebufferInfo(fboId); bool status = checkFramebufferStatus(fboId); if(!status) fboUsed = false; glBindFramebuffer(GL_FRAMEBUFFER, 0); } // start timer, the elapsed time will be used for rotating the teapot timer.start(); // the last GLUT call (LOOP) // window will be shown and display callback is triggered by events // NOTE: this call never return main(). glutMainLoop(); /* Start GLUT event-processing loop */ return 0; } /////////////////////////////////////////////////////////////////////////////// // initialize GLUT for windowing /////////////////////////////////////////////////////////////////////////////// int initGLUT(int argc, char **argv) { // GLUT stuff for windowing // initialization openGL window. // It must be called before any other GLUT routine. glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL); // display mode glutInitWindowSize(screenWidth, screenHeight); // window size glutInitWindowPosition(100, 100); // window location // finally, create a window with openGL context // Window will not displayed until glutMainLoop() is called // It returns a unique ID. int handle = glutCreateWindow(argv[0]); // param is the title of window // register GLUT callback functions glutDisplayFunc(displayCB); //glutTimerFunc(33, timerCB, 33); // redraw only every given millisec glutIdleFunc(idleCB); // redraw whenever system is idle glutReshapeFunc(reshapeCB); glutKeyboardFunc(keyboardCB); glutMouseFunc(mouseCB); glutMotionFunc(mouseMotionCB); return handle; } /////////////////////////////////////////////////////////////////////////////// // initialize OpenGL // disable unused features /////////////////////////////////////////////////////////////////////////////// void initGL() { glShadeModel(GL_SMOOTH); // shading mathod: GL_SMOOTH or GL_FLAT glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment // enable /disable features glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); glEnable(GL_CULL_FACE); // track material ambient and diffuse from surface color, call it before glEnable(GL_COLOR_MATERIAL) glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glClearColor(0, 0, 0, 0); // background color glClearStencil(0); // clear stencil buffer glClearDepth(1.0f); // 0 is near, 1 is far glDepthFunc(GL_LEQUAL); initLights(); } /////////////////////////////////////////////////////////////////////////////// // write 2d text using GLUT // The projection matrix must be set to orthogonal before call this function. /////////////////////////////////////////////////////////////////////////////// void drawString(const char *str, int x, int y, float color[4], void *font) { glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT); // lighting and color mask glDisable(GL_LIGHTING); // need to disable lighting for proper text color glDisable(GL_TEXTURE_2D); glColor4fv(color); // set text color glRasterPos2i(x, y); // place text position // loop all characters in the string while(*str) { glutBitmapCharacter(font, *str); ++str; } glEnable(GL_TEXTURE_2D); glEnable(GL_LIGHTING); glPopAttrib(); } /////////////////////////////////////////////////////////////////////////////// // draw a string in 3D space /////////////////////////////////////////////////////////////////////////////// void drawString3D(const char *str, float pos[3], float color[4], void *font) { glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT); // lighting and color mask glDisable(GL_LIGHTING); // need to disable lighting for proper text color glDisable(GL_TEXTURE_2D); glColor4fv(color); // set text color glRasterPos3fv(pos); // place text position // loop all characters in the string while(*str) { glutBitmapCharacter(font, *str); ++str; } glDisable(GL_TEXTURE_2D); glEnable(GL_LIGHTING); glPopAttrib(); } /////////////////////////////////////////////////////////////////////////////// // initialize global variables /////////////////////////////////////////////////////////////////////////////// bool initSharedMem() { screenWidth = SCREEN_WIDTH; screenHeight = SCREEN_HEIGHT; mouseLeftDown = mouseRightDown = false; mouseX = mouseY = 0; cameraAngleX = cameraAngleY = 45; cameraDistance = 4; drawMode = 0; // 0:fill, 1: wireframe, 2:points fboId = rboColorId = rboDepthId = textureId = 0; fboSupported = fboUsed = false; playTime = renderToTextureTime = 0; return true; } /////////////////////////////////////////////////////////////////////////////// // clean up global variables /////////////////////////////////////////////////////////////////////////////// void clearSharedMem() { glDeleteTextures(1, &textureId); textureId = 0; // clean up FBO, RBO if(fboSupported) { glDeleteFramebuffers(1, &fboId); fboId = 0; glDeleteRenderbuffers(1, &rboDepthId); rboDepthId = 0; } } /////////////////////////////////////////////////////////////////////////////// // initialize lights /////////////////////////////////////////////////////////////////////////////// void initLights() { // set up light colors (ambient, diffuse, specular) 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); // position the light float lightPos[4] = {0, 0, 20, 1}; // positional light glLightfv(GL_LIGHT0, GL_POSITION, lightPos); glEnable(GL_LIGHT0); // MUST enable each light source after configuration } /////////////////////////////////////////////////////////////////////////////// // set camera position and lookat direction /////////////////////////////////////////////////////////////////////////////// void setCamera(float posX, float posY, float posZ, float targetX, float targetY, float targetZ) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(posX, posY, posZ, targetX, targetY, targetZ, 0, 1, 0); // eye(x,y,z), focal(x,y,z), up(x,y,z) } /////////////////////////////////////////////////////////////////////////////// // display info messages /////////////////////////////////////////////////////////////////////////////// void showInfo() { // backup current model-view matrix glPushMatrix(); // save current modelview matrix glLoadIdentity(); // reset modelview matrix // set to 2D orthogonal projection glMatrixMode(GL_PROJECTION); // switch to projection matrix glPushMatrix(); // save current projection matrix glLoadIdentity(); // reset projection matrix gluOrtho2D(0, screenWidth, 0, screenHeight); // set to orthogonal projection float color[4] = {1, 1, 1, 1}; stringstream ss; ss << "FBO: "; if(fboUsed) ss << "on" << ends; else ss << "off" << ends; drawString(ss.str().c_str(), 1, screenHeight-TEXT_HEIGHT, color, font); ss.str(""); // clear buffer ss << std::fixed << std::setprecision(3); ss << "Render-To-Texture Time: " << renderToTextureTime << " ms" << ends; drawString(ss.str().c_str(), 1, screenHeight-(2*TEXT_HEIGHT), color, font); ss.str(""); ss << "Press SPACE to toggle FBO." << ends; drawString(ss.str().c_str(), 1, 1, color, font); // unset floating format ss << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield); // restore projection matrix glPopMatrix(); // restore to previous projection matrix // restore modelview matrix glMatrixMode(GL_MODELVIEW); // switch to modelview matrix glPopMatrix(); // restore to previous modelview matrix } /////////////////////////////////////////////////////////////////////////////// // display frame rates /////////////////////////////////////////////////////////////////////////////// void showFPS() { static Timer timer; static int count = 0; static std::string fps = "0.0 FPS"; double elapsedTime = 0.0;; ++count; // backup current model-view matrix glPushMatrix(); // save current modelview matrix glLoadIdentity(); // reset modelview matrix // set to 2D orthogonal projection glMatrixMode(GL_PROJECTION); // switch to projection matrix glPushMatrix(); // save current projection matrix glLoadIdentity(); // reset projection matrix gluOrtho2D(0, screenWidth, 0, screenHeight); // set to orthogonal projection float color[4] = {1, 1, 0, 1}; // update fps every second elapsedTime = timer.getElapsedTime(); if(elapsedTime >= 1.0) { std::stringstream ss; ss << std::fixed << std::setprecision(1); ss << (count / elapsedTime) << " FPS" << std::ends; // update fps string ss << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield); fps = ss.str(); count = 0; // reset counter timer.start(); // restart timer } int textWidth = (int)fps.size() * TEXT_WIDTH; drawString(fps.c_str(), screenWidth-textWidth, screenHeight-TEXT_HEIGHT, color, font); // restore projection matrix glPopMatrix(); // restore to previous projection matrix // restore modelview matrix glMatrixMode(GL_MODELVIEW); // switch to modelview matrix glPopMatrix(); // restore to previous modelview matrix } /////////////////////////////////////////////////////////////////////////////// // check FBO completeness /////////////////////////////////////////////////////////////////////////////// bool checkFramebufferStatus(GLuint fbo) { // check FBO status glBindFramebuffer(GL_FRAMEBUFFER, fbo); // bind GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); switch(status) { case GL_FRAMEBUFFER_COMPLETE: std::cout << "Framebuffer complete." << std::endl; return true; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: std::cout << "[ERROR] Framebuffer incomplete: Attachment is NOT complete." << std::endl; return false; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: std::cout << "[ERROR] Framebuffer incomplete: No image is attached to FBO." << std::endl; return false; /* case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: std::cout << "[ERROR] Framebuffer incomplete: Attached images have different dimensions." << std::endl; return false; case GL_FRAMEBUFFER_INCOMPLETE_FORMATS: std::cout << "[ERROR] Framebuffer incomplete: Color attached images have different internal formats." << std::endl; return false; */ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: std::cout << "[ERROR] Framebuffer incomplete: Draw buffer." << std::endl; return false; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: std::cout << "[ERROR] Framebuffer incomplete: Read buffer." << std::endl; return false; case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: std::cout << "[ERROR] Framebuffer incomplete: Multisample." << std::endl; return false; case GL_FRAMEBUFFER_UNSUPPORTED: std::cout << "[ERROR] Framebuffer incomplete: Unsupported by FBO implementation." << std::endl; return false; default: std::cout << "[ERROR] Framebuffer incomplete: Unknown error." << std::endl; return false; } glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind } /////////////////////////////////////////////////////////////////////////////// // print out the FBO infos /////////////////////////////////////////////////////////////////////////////// void printFramebufferInfo(GLuint fbo) { // bind fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo); std::cout << "\n===== FBO STATUS =====\n"; // print max # of colorbuffers supported by FBO int colorBufferCount = 0; glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &colorBufferCount); std::cout << "Max Number of Color Buffer Attachment Points: " << colorBufferCount << std::endl; // get max # of multi samples int multiSampleCount = 0; glGetIntegerv(GL_MAX_SAMPLES, &multiSampleCount); std::cout << "Max Number of Samples for MSAA: " << multiSampleCount << std::endl; int objectType; int objectId; // print info of the colorbuffer attachable image for(int i = 0; i < colorBufferCount; ++i) { glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType); if(objectType != GL_NONE) { glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &objectId); std::string formatName; std::cout << "Color Attachment " << i << ": "; if(objectType == GL_TEXTURE) { std::cout << "GL_TEXTURE, " << getTextureParameters(objectId) << std::endl; } else if(objectType == GL_RENDERBUFFER) { std::cout << "GL_RENDERBUFFER, " << getRenderbufferParameters(objectId) << std::endl; } } } // print info of the depthbuffer attachable image glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType); if(objectType != GL_NONE) { glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &objectId); std::cout << "Depth Attachment: "; switch(objectType) { case GL_TEXTURE: std::cout << "GL_TEXTURE, " << getTextureParameters(objectId) << std::endl; break; case GL_RENDERBUFFER: std::cout << "GL_RENDERBUFFER, " << getRenderbufferParameters(objectId) << std::endl; break; } } // print info of the stencilbuffer attachable image glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType); if(objectType != GL_NONE) { glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &objectId); std::cout << "Stencil Attachment: "; switch(objectType) { case GL_TEXTURE: std::cout << "GL_TEXTURE, " << getTextureParameters(objectId) << std::endl; break; case GL_RENDERBUFFER: std::cout << "GL_RENDERBUFFER, " << getRenderbufferParameters(objectId) << std::endl; break; } } std::cout << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, 0); } /////////////////////////////////////////////////////////////////////////////// // return texture parameters as string using glGetTexLevelParameteriv() /////////////////////////////////////////////////////////////////////////////// std::string getTextureParameters(GLuint id) { if(glIsTexture(id) == GL_FALSE) return "Not texture object"; int width, height, format; std::string formatName; glBindTexture(GL_TEXTURE_2D, id); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); // get texture width glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); // get texture height glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); // get texture internal format glBindTexture(GL_TEXTURE_2D, 0); formatName = convertInternalFormatToString(format); std::stringstream ss; ss << width << "x" << height << ", " << formatName; return ss.str(); } /////////////////////////////////////////////////////////////////////////////// // return renderbuffer parameters as string using glGetRenderbufferParameteriv /////////////////////////////////////////////////////////////////////////////// std::string getRenderbufferParameters(GLuint id) { if(glIsRenderbuffer(id) == GL_FALSE) return "Not Renderbuffer object"; int width, height, format, samples; std::string formatName; glBindRenderbuffer(GL_RENDERBUFFER, id); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); // get renderbuffer width glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); // get renderbuffer height glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &format); // get renderbuffer internal format glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); // get multisample count glBindRenderbuffer(GL_RENDERBUFFER, 0); formatName = convertInternalFormatToString(format); std::stringstream ss; ss << width << "x" << height << ", " << formatName << ", MSAA(" << samples << ")"; return ss.str(); } /////////////////////////////////////////////////////////////////////////////// // convert OpenGL internal format enum to string /////////////////////////////////////////////////////////////////////////////// std::string convertInternalFormatToString(GLenum format) { std::string formatName; switch(format) { case GL_STENCIL_INDEX: // 0x1901 formatName = "GL_STENCIL_INDEX"; break; case GL_DEPTH_COMPONENT: // 0x1902 formatName = "GL_DEPTH_COMPONENT"; break; case GL_ALPHA: // 0x1906 formatName = "GL_ALPHA"; break; case GL_RGB: // 0x1907 formatName = "GL_RGB"; break; case GL_RGBA: // 0x1908 formatName = "GL_RGBA"; break; case GL_LUMINANCE: // 0x1909 formatName = "GL_LUMINANCE"; break; case GL_LUMINANCE_ALPHA: // 0x190A formatName = "GL_LUMINANCE_ALPHA"; break; case GL_R3_G3_B2: // 0x2A10 formatName = "GL_R3_G3_B2"; break; case GL_ALPHA4: // 0x803B formatName = "GL_ALPHA4"; break; case GL_ALPHA8: // 0x803C formatName = "GL_ALPHA8"; break; case GL_ALPHA12: // 0x803D formatName = "GL_ALPHA12"; break; case GL_ALPHA16: // 0x803E formatName = "GL_ALPHA16"; break; case GL_LUMINANCE4: // 0x803F formatName = "GL_LUMINANCE4"; break; case GL_LUMINANCE8: // 0x8040 formatName = "GL_LUMINANCE8"; break; case GL_LUMINANCE12: // 0x8041 formatName = "GL_LUMINANCE12"; break; case GL_LUMINANCE16: // 0x8042 formatName = "GL_LUMINANCE16"; break; case GL_LUMINANCE4_ALPHA4: // 0x8043 formatName = "GL_LUMINANCE4_ALPHA4"; break; case GL_LUMINANCE6_ALPHA2: // 0x8044 formatName = "GL_LUMINANCE6_ALPHA2"; break; case GL_LUMINANCE8_ALPHA8: // 0x8045 formatName = "GL_LUMINANCE8_ALPHA8"; break; case GL_LUMINANCE12_ALPHA4: // 0x8046 formatName = "GL_LUMINANCE12_ALPHA4"; break; case GL_LUMINANCE12_ALPHA12:// 0x8047 formatName = "GL_LUMINANCE12_ALPHA12"; break; case GL_LUMINANCE16_ALPHA16:// 0x8048 formatName = "GL_LUMINANCE16_ALPHA16"; break; case GL_INTENSITY: // 0x8049 formatName = "GL_INTENSITY"; break; case GL_INTENSITY4: // 0x804A formatName = "GL_INTENSITY4"; break; case GL_INTENSITY8: // 0x804B formatName = "GL_INTENSITY8"; break; case GL_INTENSITY12: // 0x804C formatName = "GL_INTENSITY12"; break; case GL_INTENSITY16: // 0x804D formatName = "GL_INTENSITY16"; break; case GL_RGB4: // 0x804F formatName = "GL_RGB4"; break; case GL_RGB5: // 0x8050 formatName = "GL_RGB5"; break; case GL_RGB8: // 0x8051 formatName = "GL_RGB8"; break; case GL_RGB10: // 0x8052 formatName = "GL_RGB10"; break; case GL_RGB12: // 0x8053 formatName = "GL_RGB12"; break; case GL_RGB16: // 0x8054 formatName = "GL_RGB16"; break; case GL_RGBA2: // 0x8055 formatName = "GL_RGBA2"; break; case GL_RGBA4: // 0x8056 formatName = "GL_RGBA4"; break; case GL_RGB5_A1: // 0x8057 formatName = "GL_RGB5_A1"; break; case GL_RGBA8: // 0x8058 formatName = "GL_RGBA8"; break; case GL_RGB10_A2: // 0x8059 formatName = "GL_RGB10_A2"; break; case GL_RGBA12: // 0x805A formatName = "GL_RGBA12"; break; case GL_RGBA16: // 0x805B formatName = "GL_RGBA16"; break; case GL_DEPTH_COMPONENT16: // 0x81A5 formatName = "GL_DEPTH_COMPONENT16"; break; case GL_DEPTH_COMPONENT24: // 0x81A6 formatName = "GL_DEPTH_COMPONENT24"; break; case GL_DEPTH_COMPONENT32: // 0x81A7 formatName = "GL_DEPTH_COMPONENT32"; break; case GL_DEPTH_STENCIL: // 0x84F9 formatName = "GL_DEPTH_STENCIL"; break; case GL_RGBA32F: // 0x8814 formatName = "GL_RGBA32F"; break; case GL_RGB32F: // 0x8815 formatName = "GL_RGB32F"; break; case GL_RGBA16F: // 0x881A formatName = "GL_RGBA16F"; break; case GL_RGB16F: // 0x881B formatName = "GL_RGB16F"; break; case GL_DEPTH24_STENCIL8: // 0x88F0 formatName = "GL_DEPTH24_STENCIL8"; break; default: std::stringstream ss; ss << "Unknown Format(0x" << std::hex << format << ")" << std::ends; formatName = ss.str(); } return formatName; } /////////////////////////////////////////////////////////////////////////////// // set projection matrix as orthogonal /////////////////////////////////////////////////////////////////////////////// void toOrtho() { // set viewport to be the entire window glViewport(0, 0, (GLsizei)screenWidth, (GLsizei)screenHeight); // set orthographic viewing frustum glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, screenWidth, 0, screenHeight, -1, 1); // switch to modelview matrix in order to set scene glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /////////////////////////////////////////////////////////////////////////////// // set the projection matrix as perspective /////////////////////////////////////////////////////////////////////////////// void toPerspective() { // set viewport to be the entire window glViewport(0, 0, (GLsizei)screenWidth, (GLsizei)screenHeight); // set perspective viewing frustum glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0f, (float)(screenWidth)/screenHeight, 1.0f, 1000.0f); // FOV, AspectRatio, NearClip, FarClip // switch to modelview matrix in order to set scene glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } //============================================================================= // CALLBACKS //============================================================================= void displayCB() { // get the total elapsed time playTime = (float)timer.getElapsedTime(); // compute rotation angle const float ANGLE_SPEED = 90; // degree/s float angle = ANGLE_SPEED * playTime; // render to texture ////////////////////////////////////////////////////// t1.start(); // 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); // camera transform glLoadIdentity(); glTranslatef(0, 0, -CAMERA_DISTANCE); // with FBO // render directly to a texture if(fboUsed) { // set the rendering destination to FBO glBindFramebuffer(GL_FRAMEBUFFER, fboId); // clear buffer glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // draw a rotating teapot at the origin glPushMatrix(); glRotatef(angle*0.5f, 1, 0, 0); glRotatef(angle, 0, 1, 0); glRotatef(angle*0.7f, 0, 0, 1); glTranslatef(0, -1.575f, 0); drawTeapot(); glPopMatrix(); // back to normal window-system-provided framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind // trigger mipmaps generation explicitly // NOTE: If GL_GENERATE_MIPMAP is set to GL_TRUE, then glCopyTexSubImage2D() // triggers mipmap generation automatically. However, the texture attached // onto a FBO should generate mipmaps manually via glGenerateMipmap(). glBindTexture(GL_TEXTURE_2D, textureId); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } // without FBO // render to the backbuffer and copy the backbuffer to a texture else { // clear buffer glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushAttrib(GL_COLOR_BUFFER_BIT | GL_PIXEL_MODE_BIT); // for GL_DRAW_BUFFER and GL_READ_BUFFER glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); // draw a rotating teapot at the origin glPushMatrix(); glRotatef(angle*0.5f, 1, 0, 0); glRotatef(angle, 0, 1, 0); glRotatef(angle*0.7f, 0, 0, 1); glTranslatef(0, -1.575f, 0); drawTeapot(); glPopMatrix(); // copy the framebuffer pixels to a texture glBindTexture(GL_TEXTURE_2D, textureId); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT); glBindTexture(GL_TEXTURE_2D, 0); glPopAttrib(); // GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT } // measure the elapsed time of render-to-texture t1.stop(); renderToTextureTime = t1.getElapsedTimeInMilliSec(); /////////////////////////////////////////////////////////////////////////// // rendering as normal //////////////////////////////////////////////////// // back to normal viewport and projection matrix glViewport(0, 0, screenWidth, screenHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0f, (float)(screenWidth)/screenHeight, 1.0f, 100.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // tramsform camera glTranslatef(0, 0, -cameraDistance); glRotatef(cameraAngleX, 1, 0, 0); // pitch glRotatef(cameraAngleY, 0, 1, 0); // heading // clear framebuffer glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glPushMatrix(); // draw a cube with the dynamic texture draw(); glPopMatrix(); // draw info messages showInfo(); showFPS(); glutSwapBuffers(); } void reshapeCB(int width, int height) { screenWidth = width; screenHeight = height; toPerspective(); } void timerCB(int millisec) { glutTimerFunc(millisec, timerCB, millisec); glutPostRedisplay(); } void idleCB() { glutPostRedisplay(); } void keyboardCB(unsigned char key, int x, int y) { switch(key) { case 27: // ESCAPE exit(0); break; case ' ': if(fboSupported) fboUsed = !fboUsed; std::cout << "FBO mode: " << (fboUsed ? "on" : "off") << std::endl; break; case 'd': // switch rendering modes (fill -> wire -> point) case 'D': drawMode = ++drawMode % 3; if(drawMode == 0) // fill mode { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } else if(drawMode == 1) // wireframe mode { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); } else // point mode { glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); } break; default: ; } } void mouseCB(int button, int state, int x, int y) { mouseX = x; mouseY = y; if(button == GLUT_LEFT_BUTTON) { if(state == GLUT_DOWN) { mouseLeftDown = true; } else if(state == GLUT_UP) mouseLeftDown = false; } else if(button == GLUT_RIGHT_BUTTON) { if(state == GLUT_DOWN) { mouseRightDown = true; } else if(state == GLUT_UP) mouseRightDown = false; } } void mouseMotionCB(int x, int y) { if(mouseLeftDown) { cameraAngleY += (x - mouseX); cameraAngleX += (y - mouseY); mouseX = x; mouseY = y; } if(mouseRightDown) { cameraDistance -= (y - mouseY) * 0.2f; mouseY = y; } } void exitCB() { clearSharedMem(); }