Wednesday, February 27, 2013

Intro to FBOs


Frame Buffer Objects
Frame buffer objects (FBOs) are OpenGL objects that hold texture data which can be filled by rendering off screen, or as one of my course’s TA calls it rendering in Magic Land. FBOs are really important for graphics because they can be used to apply post processing effects, which is the act of applying an effect to an image or texture. A FBO will hold user defined textures and you can have more than one texture or even no textures attached. To apply a post process you have to render a full screen quad and run a shader that does the screen process. In the end you will have a 2D texture that is essentially a screen picture of that frame of your application. Then you just render another full screen quad and attach that texture to it too display it to the screen.

Setting up an FBO is pretty easy. You just have to create a handle then set some parameters and check if the FBO has correctly been made by quarrying openGL. Below I will go over a bare bone Create FBO function that has the capability to set up a multiple render targets (MRTs). So this function is pretty bad in the sense that it has very little flexibility and may not do what you’d want it to do. The function call accepts a Boolean to tell it which FBO attachment types to make. Although there are only 2 types the function can make, depth and colour, the normal option is really just another colour frame buffer. The function accepts pointers to texture handles and the width and height of the screen. The rest of the code below will be explained through the comments in the code. For explanations on some of the openGL code used you can just Google them for their full break down.

GLuint Game::CreateFBO(bool useColour, bool useDepth, bool useNormal, GLuint *colourTextureOut,                 GLuint *depthTextureOut, GLuint *normalTextureOut, unsigned int width, unsigned int height)
{
//this is a simple check to see if we are creating any of the frame buffers and if we are not  //return 0.
            if (!useColour && !useDepth && !useNormal)
                        return 0;
            //this is our fbo handle which is much like a texture handle.
            unsigned int fboHandle;

            // generate FBO in graphics memory
            glGenFramebuffers(1, &fboHandle);
            glBindFramebuffer(GL_FRAMEBUFFER, fboHandle);

            // create a texture for colour storage
            if (useColour && colourTextureOut)
            {
                        //create and bind fbo
                        glGenTextures(1, colourTextureOut);
                        glBindTexture(GL_TEXTURE_2D, *colourTextureOut);
//Create texture notice how we made it using GL_RGBA8 which is basically for color as //well as GL_RGBA as parameters.
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA,                    GL_UNSIGNED_BYTE, 0);

                        //set up parameters
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
                       
//Notice how we use the color attachment 0 colour attachments are important for //using MRT as each frame buffer must use its own colour attachment.
                        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *colourTextureOut, 0);
            }

            // create a texture for depth storage
            if (useDepth && depthTextureOut)
            {
                        //Create fbo
                        glGenTextures(1, depthTextureOut);
                        glBindTexture(GL_TEXTURE_2D, *depthTextureOut);
                        //Create texture
                        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
                        //set up parameters
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);

                        // we use the depth attachment for depth
                        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, *depthTextureOut, 0);
            }

            if (useNormal && normalTextureOut)
            {
                        //create and bind fbo
                        glGenTextures(1, normalTextureOut);
                        glBindTexture(GL_TEXTURE_2D, *normalTextureOut);
                        //Create texture
                        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
                        //set up parameters
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
                       
                        // as the color attachment 0 has been used we need to use the next one.
                        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, *normalTextureOut, 0);
            }
           
            // so now that we created the FBO we need to check the status of it
            int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
            if (status != GL_FRAMEBUFFER_COMPLETE)
            {          //if the FBO fails we need to delete it and all the frame buffers created
                        std::cout<<"FBO Failure" << std::endl;
                        glBindFramebuffer(GL_FRAMEBUFFER, 0);
                        glBindTexture(GL_TEXTURE_2D, 0);
                        glDeleteFramebuffers(1, &fboHandle);
                        glDeleteTextures(1, colourTextureOut);
                        glDeleteTextures(1, depthTextureOut);
                        glDeleteTextures(1, normalTextureOut);
                        *colourTextureOut       = 0;
                        *depthTextureOut       = 0;
                        *normalTextureOut      = 0;
                        return 0;
            }

            //disable some stuff
            glBindTexture(GL_TEXTURE_2D, NULL);
            glBindFramebuffer(GL_FRAMEBUFFER, NULL);
            return fboHandle;
}

                 Now that you know how to make an FBO, let me show you really quickly how you would use one. It’s simple, just like most openGL code you have to tell openGL what you’re doing. First off the GLenum is an enumeration of the color attachments that the FBO has attached. From above you’ll see that we had the color attachments 0 and 1. The next thing we do is tell openGL that we are binding a Frame Buffer and then pass it the type and the FBO handle created by the function above. Next we tell openGL that we are drawing to buffers and tell it how many by passing it the GLenum we just created. The next step is to draw the scene. Once done, you can unbind the frame buffer by binding with NULL.

void Game::DeferredFirstPass(float elapsed, float time)
{
            GLenum Buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
           
            glBindFramebuffer(GL_FRAMEBUFFER, GbufferFBO);
            glDrawBuffers(2, Buffers);

            glViewport(0, 0, windowWidth, windowHeight);
            glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

            this->RenderScene(0.0f, 0.0f);

            glBindFramebuffer(GL_FRAMEBUFFER, NULL);
            glViewport(0, 0, windowWidth, windowHeight);
}

                The Last smidgen of information I will give is how to use the MRT s in the shader. This is extremely easy and it really is just making your out colour an array like the code below shows. Out putting to a different index will output to a different texture in the current FBO.
#version 140

uniform sampler2D TextureData;

in vec2 texcoord;
in vec3 normal;

out vec4 outColor[2];

void main ()
{          //render to two targets save packed normals in one and textured object in the other
            outColor[1] = vec4(normal * 0.5f + 0.5f ,1.0f);
            outColor[0] = texture(TextureData, texcoord.st);
}

                The code snippets have been from a tech demo I’ve made that shows off deferred lighting, just one of the many post-processing effects that use FBOs and MTRs. Below is a screen shot of my tech demo.

Monday, February 25, 2013

Intro To Black Magic... Also Know as Shaders


Intro to Shaders
                Shaders are like mystical black magic in a text file, the visual effects they can achieve is incredible. Shaders are responsible for all most all of the graphical advancements in games. A shader manipulates data in the graphics pipeline at certain programmable steps. I cannot stress this enough: what is passed to a shader is just DATA and you can do whatever you want to DATA! There are currently 5 programmable stages in the pipeline the vertex shader, Tessellation Control & Evaluation Shaders, Geometry Shaders, Fragment Shaders and Compute Shaders. Below is a diagram of the full openGL graphics pipeline.
From http://img.hexus.net/v2/lowe/News/Khronos/OGLCS.jpg
 Most games can get away with only using the vertex and fragment shaders to create Interesting visual effects. This blog will be focused on the vertex and fragment shader as they are the easiest to understand. Below is a simplified version of the Graphics pipeline that makes it easier to understand what happens.
From http://goanna.cs.rmit.edu.au/`gl/teaching/Interactive3D/2012/images/pipline.png
                Shaders are written in a couple of programming languages such as HLSL, GLSL and CG. These languages have different syntax but they all do the same stuff. The program language I use is GLSL because I found it to be congruent with openGL, the graphics APU I use. A Vertex has a specific job to process individual vertices and send out its new position to the pipeline. Here is a simple Vertex shader in GLSL.

#version 150
uniform mat4 uni_ModelViewProjection;
in vec3 in_vertex;

void main(void)
{
                gl_Position = uni_ModelViewProjection * vec4(in_vertex, 1.0);
}

All this Shader does is takes a vertex -in_vertex- and transforms it by the modelVeiwProjection matrix which both are variables passed in from the c++ program. It then outputs the new vertex in the system variable gl_Position. A vertex Shader must set gl_Position! If it doesn’t set it the shader will not compile.

The Fragment shader is next which manipulates the pixel colour of an object per fragment. The only thing the following example does, is set every output color to red.  A fragment shader must send out a colour vec4 or vec3.

#version 150
out vec4 out_color;

void main(void)
{
                out_color =  vec4 (1.0f, 0.0f, 0.0f, 0.0f);
}

                With vertex and fragment shaders you can make amazing visual effects from the extremely easy one I showed to very nice and complex ones like these:
From http://www.userinter.net/wp-content/uploads/2011/04/Shader-Effects.jpg

From http://devmaster.net/assets/post/3021/featured_image/sized_for_post_17ced3ab-a06c-4edd-9b0d-a1ef4be7c4b7.jpg