Simple OpenGL FBO Textures
r3dux | May 26, 2011I was playing around with FBOs and rendering to textures the other week and came up with this. A spinning yellow torus is rendered to a texture, then a second spinning torus is textured using the texture we just created of the first spinning torus… Yeah, I need to stop using donuts as my test objects.
Full source code & shaders after the jump…
Main C++ File
#include <iostream> #include <ctime> #include <math.h> // Needed for sin/cos/tan etc. #include <GL/glew.h> // Include glew, which calls GL.h for us #include "glfw.h" // Include OpenGL Framework library #include <GL/glut.h> #include <GLTools.h> // OpenGL toolkit #include <GLMatrixStack.h> #include <GLFrame.h> #include <GLFrustum.h> #include <GLGeometryTransform.h> #include <GLBatch.h> #include <StopWatch.h> // Include the DevIL libraties for image loading #define ILUT_USE_OPENGL #include <IL/il.h> #include <IL/ilu.h> #include <IL/ilut.h> #include "../../GLTools/r3dTools.h" // Include our custom tools to do things like load images using namespace std; GLint windowWidth = 800; // Define our window width GLint windowHeight = 600; // Define our window height GLfloat fieldOfView = 45.0f; // Frustrum field of view GLfloat zNear = 1.0f; // Frustrum near distance GLfloat zFar = 1000.0f; // Frustrum far distance GLint frameCount = 0; // Keep track of how many frames we've drawn GLFrame viewFrame; GLFrustum viewFrustum; GLTriangleBatch torusBatch; GLTriangleBatch torusBatch2; GLMatrixStack modelViewMatrix; GLMatrixStack projectionMatrix; GLGeometryTransform transformPipeline; // Handles for our shader programs GLuint r3dShaderProgram; GLuint r3dTextureProgram; // Diffuse shader uniform locations. // Note: The type needs to be int not uint because if the location of the uniform variable cannot be found glGetUniformLocation returns -1! GLint locMVMatrix; GLint locMVPMatrix; GLint locNormalMatrix; GLint locLightPos; GLint locAmbientColour; GLint locDiffuseColour; // Texture shader uniform locations. GLint locTexTextureMap; GLint locTexMVMatrix; GLint locTexMVPMatrix; GLint locTexNormalMatrix; GLint locTexLightPos; GLint locTexAmbientColour; GLint locTexDiffuseColour; GLuint fbo; // The frame buffer object GLuint fboDepth; // The depth buffer for the frame buffer object GLuint fboTexture; // The handle for the texture we draw to in the framebuffer GLenum windowBuff[] = { GL_BACK_LEFT }; GLenum fboBuff[] = { GL_COLOR_ATTACHMENT0 }; void GLFWCALL resizeWindow(GLsizei theNewWidth, GLsizei theNewHeight) { // Set our globals so we can query the window size at any time windowWidth = theNewWidth; windowHeight = theNewHeight; // Set the viewport to the full size of the new window glViewport(0, 0, windowWidth, windowHeight); // 0, 0 is the BOTTOM-LEFT!! // Create the projection matrix, and load it on to the projection matrix stack viewFrustum.SetPerspective(fieldOfView, float(windowWidth)/float(windowHeight), zNear, zFar); projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); // Get the projection matrix from our frustum (defined in the line above) and load it into our proper projection matrix stack // Set the transformation pipeline to use the two matrix stacks transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix); // ----- Resize the FBO and associated depth & colour buffers ------ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glBindRenderbuffer(GL_RENDERBUFFER, fboDepth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, windowWidth, windowHeight); glBindTexture(GL_TEXTURE_2D, fboTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, windowWidth, windowHeight, 0, GL_RGBA, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Attach texture to first color attachment and the depth to the depth attachment glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture, 0); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboDepth); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // ----- End of FBO resize section ----- cout << "Resized window." << endl; } // Function to set some initial OpenGL state-machine properties void initGL() { // ----- Initialise GLEW ----- GLenum err = glewInit(); if (GLEW_OK != err) { cout << "GLEW initialisation error: " << glewGetErrorString(err) << endl; exit(-1); } cout << "GLEW intialised successfully. Using GLEW version: " << glewGetString(GLEW_VERSION) << endl; // Set the window title glfwSetWindowTitle("Shaded FBO Textures | r3dux.org"); // Lock to vsync glfwSwapInterval(1); // Initialise all DevIL functionality ilutRenderer(ILUT_OPENGL); ilInit(); iluInit(); ilutInit(); ilutRenderer(ILUT_OPENGL); // Tell DevIL that we're using OpenGL for our rendering // Background glClearColor(0.5f, 0.5f, 0.5f, 1.0f ); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); viewFrame.MoveForward(4.0f); // Make the torus' gltMakeTorus(torusBatch, 0.80f, 0.25f, 52, 26); gltMakeTorus(torusBatch2, 0.5f, 0.2f, 52, 26); // Load shaders and bind attributes r3dShaderProgram = gltLoadShaderPairWithAttributes("r3dPointLightDiffuseShader.vp", "r3dPointLightDiffuseShader.fp", 2, // We're passing in 2 attributes... GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_NORMAL, "vNormal"); locMVMatrix = glGetUniformLocation(r3dShaderProgram, "mvMatrix"); checkValidUniformLocation(locMVMatrix, "mvMatrix"); locMVPMatrix = glGetUniformLocation(r3dShaderProgram, "mvpMatrix"); checkValidUniformLocation(locMVPMatrix, "mvpMatrix"); locNormalMatrix = glGetUniformLocation(r3dShaderProgram, "normalMatrix"); checkValidUniformLocation(locNormalMatrix, "normalMatrix"); locLightPos = glGetUniformLocation(r3dShaderProgram, "vLightPosition"); checkValidUniformLocation(locLightPos, "vLightPosition"); locDiffuseColour = glGetUniformLocation(r3dShaderProgram, "diffuseColour"); checkValidUniformLocation(locDiffuseColour, "diffuseColour"); // Load shaders and bind attributes r3dTextureProgram = gltLoadShaderPairWithAttributes("r3dTexShader.vp", "r3dTexShader.fp", 3, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_NORMAL, "vNormal", GLT_ATTRIBUTE_TEXTURE0, "vTexCoord0"); locTexMVMatrix = glGetUniformLocation(r3dTextureProgram, "mvMatrix"); checkValidUniformLocation(locTexMVMatrix, "mvMatrix"); locTexMVPMatrix = glGetUniformLocation(r3dTextureProgram, "mvpMatrix"); checkValidUniformLocation(locTexMVPMatrix, "mvpMatrix"); locTexNormalMatrix = glGetUniformLocation(r3dTextureProgram, "normalMatrix"); checkValidUniformLocation(locTexNormalMatrix, "normalMatrix"); locTexLightPos = glGetUniformLocation(r3dTextureProgram, "vLightPosition"); checkValidUniformLocation(locTexLightPos, "vLightPosition"); locTexDiffuseColour = glGetUniformLocation(r3dTextureProgram, "diffuseColour"); checkValidUniformLocation(locTexDiffuseColour, "diffuseColour"); locTexTextureMap = glGetUniformLocation(r3dTextureProgram, "theTexture"); checkValidUniformLocation(locTexTextureMap, "theTexture"); // Create and bind an FBO glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); // Create depth renderbuffer glGenRenderbuffers(1, &fboDepth); glBindRenderbuffer(GL_RENDERBUFFER, fboDepth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, windowWidth, windowHeight); // Create the texture glGenTextures(1, &fboTexture); glBindTexture(GL_TEXTURE_2D, fboTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, windowWidth, windowHeight, 0, GL_RGBA, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Attach texture to first color attachment and the depth to the depth attachment glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture, 0); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboDepth); GLenum fboStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); // Check the status of our generated frame buffer if (fboStatus != GL_FRAMEBUFFER_COMPLETE) // If the frame buffer does not report back as complete... { std::cout << "Couldn't create frame buffer" << std::endl; // ...Output an error to the console exit(0); // Exit the application } glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Reset framebuffer binding checkGLError("InitGL-end"); } // Function to draw our OpenGL scene void drawScene() { static CStopWatch rotTimer; // Set up to draw to our FBO texture... glActiveTexture(GL_TEXTURE0); // Try to do everything using the default texture unit for now (texture unit 0) - can spread things across texture units when it works!!! // Specify that we're going to draw to our framebuffer and not the screen glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glDrawBuffers(1, fboBuff); // Clear the window and the depth buffer glClearColor(0.3f, 0.3f, 0.3f, 1.0f ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); modelViewMatrix.PushMatrix(viewFrame); modelViewMatrix.LoadIdentity(); modelViewMatrix.Translate(0.0f, 0.0f, -3.5f); modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds() * 160.0f, 0.0f, 1.0f, 0.5f); // Rotate the torus being drawn for the texture at quite a fast rate GLfloat vEyeLightPos[] = { -100.0f, 100.0f, 100.0f }; GLfloat vDiffuseColour[] = { 1.0f, 1.0f, 0.0f, 1.0f }; // Texture torus is drawn in yellow glUseProgram(r3dShaderProgram); // Specify our shader program // Specify all the uniform variables for our shader program glUniform4fv(locDiffuseColour, 1, vDiffuseColour); glUniform3fv(locLightPos, 1, vEyeLightPos); glUniformMatrix4fv(locMVMatrix, 1, GL_FALSE, transformPipeline.GetModelViewMatrix()); glUniformMatrix4fv(locMVPMatrix, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix()); glUniformMatrix3fv(locNormalMatrix, 1, GL_FALSE, transformPipeline.GetNormalMatrix()); torusBatch.Draw(); modelViewMatrix.PopMatrix(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Unbind our frambuffer for drawing // ----- End of draw yellow torus as texture to FBO section ----- // Now set to draw to window glDrawBuffers(1, windowBuff); // Clear the window and the depth buffer glClearColor(0.4f, 0.2f, 0.2f, 1.0f ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Draw full screen quad with blur shader and all blur textures projectionMatrix.PushMatrix(); projectionMatrix.LoadIdentity(); modelViewMatrix.PushMatrix(); modelViewMatrix.LoadIdentity(); modelViewMatrix.Translate(0.0f, 0.0f, -2.0f); modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds() * 60.0f, 0.0f, 1.0f, 0.5f); // Create the projection matrix, and load it on to the projection matrix stack viewFrustum.SetPerspective(fieldOfView, float(windowWidth)/float(windowHeight), zNear, zFar); projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); // Get the projection matrix from our frustum (defined in the line above) and load it into our proper projection matrix stack // Set the transformation pipeline to use the two matrix stacks transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix); glBindTexture(GL_TEXTURE_2D, fboTexture); // Select the texture in our framebuffer // Set the blur program as the current one glUseProgram(r3dTextureProgram); GLfloat vTexEyeLightPos[] = { 100.0f, 0.0f, 0.0f }; GLfloat vTexDiffuseColour[] = { 0.0f, 0.5f, 1.0f, 1.0f }; // Specify all the uniform variables for our shader program glUniform4fv(locTexDiffuseColour, 1, vTexDiffuseColour); glUniform3fv(locTexLightPos, 1, vTexEyeLightPos); glUniformMatrix4fv(locTexMVMatrix, 1, GL_FALSE, transformPipeline.GetModelViewMatrix()); glUniformMatrix4fv(locTexMVPMatrix, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix()); glUniformMatrix3fv(locTexNormalMatrix, 1, GL_FALSE, transformPipeline.GetNormalMatrix()); glUniform1i(locTexTextureMap, 0); // The last digit here is the texture unit to use (as in the GL_TEXTURE0, GL_TEXTURE1 type texture unit) torusBatch2.Draw(); modelViewMatrix.PopMatrix(); projectionMatrix.PopMatrix(); // ----- Stop Drawing Stuff! ------ glfwSwapBuffers(); // Swap the buffers to display the scene (so we don't have to watch it being drawn!) } // End of drawScene function void ShutdownRC(void) { // Make sure default FBO is bound glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); // Cleanup textures glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); glDeleteTextures(1, &fboTexture); // Cleanup RBOs glDeleteRenderbuffers(1, &fboDepth); // Cleanup FBOs glDeleteFramebuffers(1, &fbo); } // Our main function int main() { srand((unsigned)time(NULL)); // Seed the random number generator // Define our buffer settings int redBits = 8, greenBits = 8, blueBits = 8; int alphaBits = 8, depthBits = 32, stencilBits = 8; // Flag to keep our main loop running bool running = true; // Initialise glfw glfwInit(); // Ask for 4x AntiAliasing (this doesn't mean we'll get it - it'll work only if the GLX_ARB_multisample extension is available) glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4); // Create a window if(!glfwOpenWindow(windowWidth, windowHeight, redBits, greenBits, blueBits, alphaBits, depthBits, stencilBits, GLFW_WINDOW)) { cout << "Failed to open window!" << endl; glfwTerminate(); return 0; } // Call our initGL function to set up our OpenGL options initGL(); // Specify the callback function for when the window is resized glfwSetWindowSizeCallback(resizeWindow); while (running == true) { // Draw our scene drawScene(); // Increase our frame counter frameCount++; checkGLError("Main loop"); // Exit if ESC was pressed or the window was closed running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam( GLFW_OPENED); } ShutdownRC(); glfwTerminate(); return 0; } |
Point light diffuse vertex shader (first yellow torus)
#version 330 // Incoming per-vertex attribute values in vec4 vVertex; in vec3 vNormal; // Uniform values shared across all vertexes (and read only!) uniform vec4 diffuseColour; uniform vec3 vLightPosition; uniform mat4 mvpMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; // Outgoing colour value to fragment shader smooth out vec4 vVaryingColour; void main(void) { // Get vertex normal in eye coodinates vec3 vEyeNormal = normalMatrix * vNormal; // Get vertex position in eye coordinates vec4 vPosition4 = mvMatrix * vVertex; vec3 vPosition3 = vPosition4.xyz / vPosition4.w; // Normalise vertex position // Get vector to light source vec3 vLightDir = normalize(vLightPosition - vPosition3); // Calculate the intensity of the light on the surface by calculating the dot product of the geometry's normal and the light direction float diffuseIntensity = max(0.0, dot(vEyeNormal, vLightDir)); // Multiply diffuse intensity by (uniform) diffuse colour and force alpha to 1.0 vVaryingColour.rgb = diffuseIntensity * diffuseColour.rgb; // Transform the geometry gl_Position = mvpMatrix * vVertex; } |
Point light diffuse fragment shader (first yellow torus)
#version 330 // Input from our vertex shader in vec4 vVaryingColour; // Output fragments out vec4 vFragColour; void main(void) { vFragColour = vVaryingColour; } |
Texture / Diffuse Shader vertex shader (final pass)
#version 330 // Uniform values shared across all vertexes (and read only!) uniform vec4 diffuseColour; uniform vec3 vLightPosition; uniform mat4 mvpMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; in vec4 vVertex; in vec3 vNormal; in vec2 vTexCoord0; // Outgoing colour value to fragment shader smooth out vec2 vTex; smooth out vec4 vVaryingColour; void main(void) { // Get vertex normal in eye coodinates vec3 vEyeNormal = normalMatrix * vNormal; // Get vertex position in eye coordinates vec4 vPosition4 = mvMatrix * vVertex; vec3 vPosition3 = vPosition4.xyz / vPosition4.w; // Normalise vertex position // Get vector to light source vec3 vLightDir = normalize(vLightPosition - vPosition3); // Calculate the intensity of the light on the surface by calculating the dot product of the geometry's normal and the light direction // Note: Doing this in the vertex shader is the lower quality but faster gouraud method.. throw this in the fragment shader for proper diffuse lighting float diffuseIntensity = max(0.0, dot(vEyeNormal, vLightDir)); // ----- Outgoing to fragment shader ----- // Multiply diffuse intensity by (uniform) diffuse colour and force alpha to 1.0 vVaryingColour.rgb = diffuseIntensity * diffuseColour.rgb; // Pass through the texture vTex = vTexCoord0; // Transform the geometry gl_Position = mvpMatrix * vVertex; } |
Texture / Diffuse Shader fragment shader (final pass)
#version 330 uniform sampler2D theTexture; in vec2 vTex; in vec4 vVaryingColour; out vec4 vFragColour; void main(void) { vFragColour = texture2D(theTexture, vTex); // Sample the texture... vFragColour += vVaryingColour; // ...then add in the diffuse lighting. } |










