r3dux.org

A number-pimping side project from the valleys in *NEW* upside-down flavour.

  • Home
  • ABOUT
  • OLD SITE
  • SEARCH
  • FEEDBACK

Simple OpenGL FBO Textures

r3dux | May 26, 2011

I 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.

YouTube Preview Image

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.
}

Related posts:

  1. Simple GLSL Bump-Mapping / Normal-Mapping
  2. How To: Create a Simple Fireworks Effect in OpenGL and SDL
  3. Simple OpenGL Keyboard and Mouse FPS Controls
  4. Single-Call OpenGL Texture Loader in DevIL
  5. C++/OpenGL/GLSL Texture Manipulation
Categories
Coding
Tags
C++, FBO, Fragment, GLSL, OpenGL, Shader, Texture, Vertex
Comments rss
Comments rss
Trackback
Trackback
Print This Post Print This Post

« Blake Griffin in RAGE? OpenGL – A Modified Phong-Blinn Light Model For Shadowed Areas »

Leave a Reply

Click here to cancel reply.

Translate

Categories

Archives

Tags

3D ActionScript ActionScript 3.0 Adobe AI Ballarat Bash C++ Class Convert CS4 Effect Error Film Flash FPS GLFW Glitch GLSL Hack How-To install Java Kinect Linux Live Mash-Up Microsoft Motion mount OpenGL Particle Problem PS3 Remix Retro script Slides Sound Ubuntu Video VirtualBox Wii Windows XBox

Gamercard

OpenR3dux

Misc.

Flattr this

RSS Feed

r3dux twitter feed



“I like my women like I like my coffee......bitter.”

rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox