diff --git a/src/core.c b/src/core.c
index 2c5bad743..efff62063 100644
--- a/src/core.c
+++ b/src/core.c
@@ -153,8 +153,7 @@ static int gamepadStream = -1;                  // Gamepad device file descripto
 #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
 static EGLDisplay display;          // Native display device (physical screen connection)
 static EGLSurface surface;          // Surface to draw on, framebuffers (connected to context)
-static EGLContext context;          // Graphic context, mode in which drawing can be done 
-
+static EGLContext context;          // Graphic context, mode in which drawing can be done
 static uint64_t baseTime;                   // Base time measure for hi-res timer
 static bool windowShouldClose = false;      // Flag to set window for closing
 #endif
@@ -207,6 +206,12 @@ static double targetTime = 0.0;             // Desired time for one frame, if 0
 static char configFlags = 0;
 static bool showLogo = false;
 
+static bool customCamera = true;
+//static int cameraMode = CUSTOM;     // FREE, FIRST_PERSON, THIRD_PERSON
+
+static bool enabledPostpro = false;
+static unsigned int fboShader = 0;
+
 //----------------------------------------------------------------------------------
 // Other Modules Functions Declaration (required by core)
 //----------------------------------------------------------------------------------
@@ -264,7 +269,7 @@ static void CommandCallback(struct android_app *app, int32_t cmd);           //
 void InitWindow(int width, int height, const char *title)
 {
     TraceLog(INFO, "Initializing raylib (v1.2.2)");
-    
+
     // Store window title (could be useful...)
     windowTitle = title;
 
@@ -303,7 +308,7 @@ void InitWindow(int width, int height, const char *title)
 void InitWindow(int width, int height, struct android_app *state)
 {
     TraceLog(INFO, "Initializing raylib (v1.2.2)");
-    
+
     app_dummy();
 
     screenWidth = width;
@@ -399,7 +404,7 @@ void CloseWindow(void)
 
 // Detect if KEY_ESCAPE pressed or Close icon pressed
 bool WindowShouldClose(void)
-{   
+{
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
     // While window minimized, stop loop execution
     while (windowMinimized) glfwPollEvents();
@@ -434,7 +439,7 @@ void SetCustomCursor(const char *cursorImage)
     cursor = LoadTexture(cursorImage);
 
 #if defined(PLATFORM_DESKTOP)
-	// NOTE: emscripten not implemented
+    // NOTE: emscripten not implemented
     glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
 #endif
     customCursor = true;
@@ -473,6 +478,8 @@ void BeginDrawing(void)
     currentTime = GetTime();            // Number of elapsed seconds since InitTimer() was called
     updateTime = currentTime - previousTime;
     previousTime = currentTime;
+    
+    if (enabledPostpro) rlEnableFBO();
 
     rlClearScreenBuffers();
 
@@ -480,14 +487,17 @@ void BeginDrawing(void)
 
     rlMultMatrixf(GetMatrixVector(downscaleView));       // If downscale required, apply it here
 
-//  rlTranslatef(0.375, 0.375, 0);      // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
+    //rlTranslatef(0.375, 0.375, 0);    // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
                                         // NOTE: Not required with OpenGL 3.3+
 }
 
 // End canvas drawing and Swap Buffers (Double Buffering)
 void EndDrawing(void)
 {
-    rlglDraw();                     //  Draw Buffers (Only OpenGL 3+ and ES2)
+    rlglDraw();                     // Draw Buffers (Only OpenGL 3+ and ES2)
+    
+    // TODO: Set postprocessing shader to be passed: SetPostproShader()?
+    if (enabledPostpro) rlglDrawPostpro(fboShader); // Draw postprocessing effect (shader)               
 
     SwapBuffers();                  // Copy back buffer to front buffer
     PollInputEvents();              // Poll user events
@@ -515,7 +525,7 @@ void EndDrawing(void)
 // Initializes 3D mode for drawing (Camera setup)
 void Begin3dMode(Camera camera)
 {
-    rlglDraw();                         //  Draw Buffers (Only OpenGL 3+ and ES2)
+    rlglDraw();                         // Draw Buffers (Only OpenGL 3+ and ES2)
 
     rlMatrixMode(RL_PROJECTION);        // Switch to projection matrix
 
@@ -533,14 +543,21 @@ void Begin3dMode(Camera camera)
     rlLoadIdentity();                   // Reset current matrix (MODELVIEW)
 
     // Setup Camera view
-    Matrix matLookAt = MatrixLookAt(camera.position, camera.target, camera.up);
-    rlMultMatrixf(GetMatrixVector(matLookAt));      // Multiply MODELVIEW matrix by view matrix (camera)
+    if (customCamera)
+    {
+        Matrix matLookAt = MatrixLookAt(camera.position, camera.target, camera.up);
+        rlMultMatrixf(GetMatrixVector(matLookAt));      // Multiply MODELVIEW matrix by view matrix (camera)
+    }
+    else
+    {
+        // TODO: Add support for multiple automatic camera modes
+    }
 }
 
 // Ends 3D mode and returns to default 2D orthographic mode
 void End3dMode(void)
 {
-    rlglDraw();                         //  Draw Buffers (Only OpenGL 3+ and ES2)
+    rlglDraw();                         // Draw Buffers (Only OpenGL 3+ and ES2)
 
     rlMatrixMode(RL_PROJECTION);        // Switch to projection matrix
     rlPopMatrix();                      // Restore previous matrix (PROJECTION) from matrix stack
@@ -738,7 +755,7 @@ void SetMousePosition(Vector2 position)
 {
     mousePosition = position;
 #if defined(PLATFORM_DESKTOP)
-	// NOTE: emscripten not implemented
+    // NOTE: emscripten not implemented
     glfwSetCursorPos(window, position.x, position.y);
 #endif
 }
@@ -900,7 +917,7 @@ Vector2 GetTouchPosition(void)
 
     //eventDrag
     int32_t iIndex = FindIndex( eventDrag, vec_pointers_[0] );
-    
+
     if (iIndex == -1) return false;
 
     float x = AMotionEvent_getX(eventDrag, iIndex);
@@ -951,7 +968,7 @@ static void InitDisplay(int width, int height)
     displayWidth = screenWidth;
     displayHeight = screenHeight;
 #endif
-    
+
     glfwDefaultWindowHints();                     // Set default windows hints
 
     glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);     // Avoid window being resizable
@@ -961,7 +978,7 @@ static void InitDisplay(int width, int height)
     //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);    // Default OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
     //glfwWindowHint(GLFW_AUX_BUFFERS, 0);        // Number of auxiliar buffers
 
-    // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version 
+    // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version
     // with forward compatibility to older OpenGL versions.
     // For example, if using OpenGL 1.1, driver can provide a 3.3 context fordward compatible.
 
@@ -972,7 +989,7 @@ static void InitDisplay(int width, int height)
             glfwWindowHint(GLFW_SAMPLES, 4);       // Enables multisampling x4 (MSAA), default is 0
             TraceLog(INFO, "Enabled MSAA x4");
         }
-        
+
         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);        // Choose OpenGL major version (just hint)
         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);        // Choose OpenGL minor version (just hint)
         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.2 and above!
@@ -1188,6 +1205,18 @@ void InitGraphics(void)
 #endif
 }
 
+void InitPostShader(void)
+{
+    rlglInitPostpro();
+    
+    enabledPostpro = true;
+}
+
+void SetPostShader(unsigned int shader)
+{
+    fboShader = shader;
+}
+
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
 // GLFW3 Error Callback, runs on GLFW3 error
 static void ErrorCallback(int error, const char *description)
@@ -1233,7 +1262,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
 static void CharCallback(GLFWwindow *window, unsigned int key)
 {
     lastKeyPressed = key;
-    
+
     //TraceLog(INFO, "Char Callback Key pressed: %i\n", key);
 }
 
@@ -1267,14 +1296,14 @@ static void WindowIconifyCallback(GLFWwindow* window, int iconified)
     {
         // The window was iconified
         PauseMusicStream();
-        
+
         windowMinimized = true;
     }
     else
     {
         // The window was restored
         ResumeMusicStream();
-        
+
         windowMinimized = false;
     }
 }
@@ -1353,7 +1382,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
         //size_t pointerCount =  AMotionEvent_getPointerCount(event);
         //float AMotionEvent_getPressure(const AInputEvent *motion_event, size_t pointer_index); // 0 to 1
         //float AMotionEvent_getSize(const AInputEvent *motion_event, size_t pointer_index); // Pressed area
-        
+
         // Detect DOUBLE TAP event
         bool tapDetected = touchTap;
 
@@ -1362,19 +1391,19 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
             case AMOTION_EVENT_ACTION_DOWN:
             {
                 int64_t eventTime = AMotionEvent_getEventTime(event);
-                
+
                 if (eventTime - lastTapTime <= DOUBLE_TAP_TIMEOUT)
                 {
                     float x = AMotionEvent_getX(event, 0) - lastTapX;
                     float y = AMotionEvent_getY(event, 0) - lastTapY;
-                    
+
                     float densityFactor = 1.0f;
-                    
+
                     if ((x*x + y*y) < (DOUBLE_TAP_SLOP*DOUBLE_TAP_SLOP*densityFactor))
                     {
                         // Doubletap detected
                         doubleTap = true;
-                        
+
                     }
                 }
             } break;
@@ -1385,12 +1414,12 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
                     lastTapTime = AMotionEvent_getEventTime(event);
                     lastTapX = AMotionEvent_getX(event, 0);
                     lastTapY = AMotionEvent_getY(event, 0);
-                    
+
                 }
             } break;
         }
-        
-        
+
+
         // Detect DRAG event
         //int32_t action = AMotionEvent_getAction(event);
 
@@ -1407,7 +1436,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
                 stdVector[indexPosition] = AMotionEvent_getPointerId(event, 0);
                 indexPosition++;
                 TraceLog(INFO, "ACTION_DOWN");
-                
+
                 //ret = GESTURE_STATE_START;
             } break;
             case AMOTION_EVENT_ACTION_POINTER_DOWN:
@@ -1415,7 +1444,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
                 stdVector[indexPosition] = AMotionEvent_getPointerId(event, index);
                 indexPosition++;
                 TraceLog(INFO, "ACTION_POINTER_DOWN");
-                
+
             } break;
             case AMOTION_EVENT_ACTION_UP:
             {
@@ -1423,12 +1452,12 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
                 indexPosition--;
                 //ret = GESTURE_STATE_END;
                 TraceLog(INFO, "ACTION_UP");
-                
+
             } break;
             case AMOTION_EVENT_ACTION_POINTER_UP:
             {
                 int32_t releasedPointerId = AMotionEvent_getPointerId(event, index);
-                
+
                 int i = 0;
                 for (i = 0; i < MAX_TOUCH_POINTS; i++)
                 {
@@ -1438,27 +1467,27 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
                         {
                             stdVector[k] = stdVector[k + 1];
                         }
-                        
+
                         //indexPosition--;
                         indexPosition = 0;
                         break;
                     }
                 }
-                
+
                 if (i <= 1)
                 {
                     // Reset pinch or drag
                     //if (count == 2) //ret = GESTURE_STATE_START;
                 }
                 TraceLog(INFO, "ACTION_POINTER_UP");
-                
+
             } break;
             case AMOTION_EVENT_ACTION_MOVE:
             {
                 if (count == 1)
                 {
                     //TraceLog(INFO, "DRAG gesture detected");
-                
+
                     drag = true; //ret = GESTURE_STATE_MOVE;
                 }
                 else break;
@@ -1470,7 +1499,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event)
         }
 
         //--------------------------------------------------------------------
-        
+
         return 1;
     }
     else if (type == AINPUT_EVENT_TYPE_KEY)
@@ -1672,13 +1701,13 @@ static void PollInputEvents(void)
     // Keyboard polling
     // Automatically managed by GLFW3 through callback
     lastKeyPressed = -1;
-    
+
     // Register previous keys states
     for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
-    
+
     // Register previous mouse states
     for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i];
-    
+
     glfwPollEvents();       // Register keyboard/mouse events... and window events!
 #elif defined(PLATFORM_ANDROID)
 
@@ -1702,8 +1731,8 @@ static void PollInputEvents(void)
         {
             // NOTE: Never close window, native activity is controlled by the system!
             //TraceLog(INFO, "Closing Window...");
-            //windowShouldClose = true; 
-            
+            //windowShouldClose = true;
+
             //ANativeActivity_finish(app->activity);
         }
     }
diff --git a/src/raylib.h b/src/raylib.h
index ef487a305..3c849bddc 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -92,7 +92,7 @@
 #define FLAG_MSAA_4X_HINT      16
 #define FLAG_VSYNC_HINT        32
 
-// Keyboard Function Keys 
+// Keyboard Function Keys
 #define KEY_SPACE            32
 #define KEY_ESCAPE          256
 #define KEY_ENTER           257
@@ -268,9 +268,15 @@ typedef struct Model {
     unsigned int vaoId;
     unsigned int vboId[4];
     unsigned int textureId;
+    unsigned int shaderId;
     //Matrix transform;
 } Model;
 
+// Shader type
+typedef struct Shader {
+    unsigned int id;
+} Shader;
+
 // Sound source type
 typedef struct Sound {
     unsigned int source;
@@ -334,6 +340,9 @@ Color Fade(Color color, float alpha);                       // Color fade-in or
 void SetupFlags(char flags);                                // Enable some window configurations
 void ShowLogo(void);                                        // Activates raylib logo at startup (can be done with flags)
 
+void InitPostShader(void);
+void SetPostShader(unsigned int shader);
+
 //------------------------------------------------------------------------------------
 // Input Handling Functions (Module: core)
 //------------------------------------------------------------------------------------
@@ -449,7 +458,7 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
 void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color);         // Draw sphere wires
 void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone
 void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires
-void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]);	   // Draw a quad
+void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]);    // Draw a quad
 void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color);                    // Draw a plane
 void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions
 void DrawGrid(int slices, float spacing);                                                          // Draw a grid (centered at (0, 0, 0))
@@ -466,6 +475,7 @@ Model LoadHeightmap(Image heightmap, float maxHeight);
 Model LoadCubicmap(Image cubicmap);                                                                // Load a map image as a 3d model (cubes based)
 void UnloadModel(Model model);                                                                     // Unload 3d model from memory
 void SetModelTexture(Model *model, Texture2D texture);                                             // Link a texture to a model
+void SetModelShader(Model *model, unsigned int shader);
 
 void DrawModel(Model model, Vector3 position, float scale, Color tint);                            // Draw a model (with texture if set)
 void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint);      // Draw a model with extended parameters
@@ -474,6 +484,8 @@ void DrawModelWires(Model model, Vector3 position, float scale, Color color);
 void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint);                         // Draw a billboard texture
 void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec
 
+unsigned int LoadCustomShader(char *vsFileName, char *fsFileName);                                 // Load a custom shader (vertex shader + fragment shader)
+
 //------------------------------------------------------------------------------------
 // Audio Loading and Playing Functions (Module: audio)
 //------------------------------------------------------------------------------------
diff --git a/src/rlgl.c b/src/rlgl.c
index 7977ea84d..67ef0a484 100644
--- a/src/rlgl.c
+++ b/src/rlgl.c
@@ -32,20 +32,20 @@
 #include <stdlib.h>         // Declares malloc() and free() for memory management, rand()
 
 #if defined(GRAPHICS_API_OPENGL_11)
-	#ifdef __APPLE__            // OpenGL include for OSX
-		#include <OpenGL/gl.h>
-	#else
-    	#include <GL/gl.h>      // Basic OpenGL include
-	#endif
+    #ifdef __APPLE__            // OpenGL include for OSX
+        #include <OpenGL/gl.h>
+    #else
+        #include <GL/gl.h>      // Basic OpenGL include
+    #endif
 #endif
 
 #if defined(GRAPHICS_API_OPENGL_33)
     #define GLEW_STATIC
-	#ifdef __APPLE__            // OpenGL include for OSX
+    #ifdef __APPLE__            // OpenGL include for OSX
         #include <OpenGL/gl3.h>
-   	#else
-	    #include <GL/glew.h>    // Extensions loading lib
-    	//#include "glad.h"     // TODO: Other extensions loading lib? --> REVIEW
+    #else
+        #include <GL/glew.h>    // Extensions loading lib
+        //#include "glad.h"     // TODO: Other extensions loading lib? --> REVIEW
     #endif
 #endif
 
@@ -164,6 +164,12 @@ static GLuint simpleVertexLoc, simpleTexcoordLoc, simpleNormalLoc, simpleColorLo
 static GLuint simpleProjectionMatrixLoc, simpleModelviewMatrixLoc;
 static GLuint simpleTextureLoc;
 
+// Custom Shader program attibutes binding locations
+static GLuint customVertexLoc, customTexcoordLoc, customNormalLoc, customColorLoc;
+static GLuint customProjectionMatrixLoc, customModelviewMatrixLoc;
+static GLuint customTextureLoc;
+static bool customShader = false;
+
 // Vertex Array Objects (VAO)
 static GLuint vaoLines, vaoTriangles, vaoQuads;
 
@@ -182,6 +188,9 @@ static bool useTempBuffer = false;
 
 // Support for VAOs (OpenGL ES2 could not support VAO extensions)
 static bool vaoSupported = false;
+
+// Framebuffer object and texture
+static GLuint fbo, fboColorTexture, fboDepthTexture, fboShader = 0;
 #endif
 
 #if defined(GRAPHICS_API_OPENGL_ES2)
@@ -208,7 +217,6 @@ static void InitializeBuffersGPU(void);
 static void UpdateBuffers(void);
 
 // Custom shader files loading (external)
-static GLuint LoadCustomShader(char *vertexFileName, char *fragmentFileName);
 static char *TextFileRead(char *fn);
 #endif
 
@@ -313,21 +321,21 @@ void rlRotatef(float angleDeg, float x, float y, float z)
     // TODO: Support rotation in multiple axes
     Matrix rot = MatrixIdentity();
 
-	// OPTION 1: It works...
+    // OPTION 1: It works...
     if (x == 1) rot = MatrixRotateX(angleDeg*DEG2RAD);
     else if (y == 1) rot = MatrixRotateY(angleDeg*DEG2RAD);
     else if (z == 1) rot = MatrixRotateZ(angleDeg*DEG2RAD);
-	
-	// OPTION 2: Requires review...
-	//Vector3 vec = (Vector3){ 0, 1, 0 };
+
+    // OPTION 2: Requires review...
+    //Vector3 vec = (Vector3){ 0, 1, 0 };
     //VectorNormalize(&vec);
     //rot = MatrixFromAxisAngle(vec, angleDeg*DEG2RAD);       // Working?
-    
+
     // OPTION 3: TODO: Review, it doesn't work!
     //Vector3 vec = (Vector3){ x, y, z };
     //VectorNormalize(&vec);
     //rot = MatrixRotate(angleDeg*vec.x, angleDeg*vec.x, angleDeg*vec.x);
-	
+
     MatrixTranspose(&rot);
 
     *currentMatrix = MatrixMultiply(*currentMatrix, rot);
@@ -696,6 +704,17 @@ void rlDeleteTextures(unsigned int id)
     glDeleteTextures(1, &id);
 }
 
+// Unload shader from GPU memory
+void rlDeleteShader(unsigned int id)
+{
+    glDeleteProgram(id);
+}
+
+void rlEnableFBO(void)
+{
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+}
+
 // Unload vertex data (VAO) from GPU memory
 void rlDeleteVertexArrays(unsigned int id)
 {
@@ -786,7 +805,7 @@ void rlglInit(void)
 #endif
 
 #if defined(GRAPHICS_API_OPENGL_ES2)
-	// NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance...
+    // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance...
 #if !defined(PLATFORM_WEB)
     glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
     glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
@@ -851,7 +870,8 @@ void rlglInit(void)
     // Init default Shader (GLSL 110) -> Common for GL 3.3+ and ES2
     defaultShaderProgram = LoadDefaultShader();
     simpleShaderProgram = LoadSimpleShader();
-    //customShaderProgram = LoadShaders("simple150.vert", "simple150.frag");
+    //simpleShaderProgram = LoadCustomShader("custom.vs", "custom.fs");     // Works ok
+    //customShaderProgram = LoadCustomShader("simple150.vert", "simple150.frag");
 
     // Get handles to GLSL input vars locations for defaultShaderProgram
     //-------------------------------------------------------------------
@@ -866,7 +886,7 @@ void rlglInit(void)
     // Get handles to GLSL uniform vars locations (fragment-shader)
     defaultTextureLoc = glGetUniformLocation(defaultShaderProgram, "texture0");
     //--------------------------------------------------------------------
-    
+
     // Get handles to GLSL input vars locations for simpleShaderProgram
     //-------------------------------------------------------------------
     simpleVertexLoc = glGetAttribLocation(simpleShaderProgram, "vertexPosition");
@@ -908,10 +928,53 @@ void rlglInit(void)
     }
 
     drawsCounter = 1;
-    draws[drawsCounter - 1].textureId = whiteTexture;
+    draws[drawsCounter - 1].textureId = whiteTexture;    
 #endif
 }
 
+// Init postpro system
+void rlglInitPostpro(void)
+{
+    // Create the texture that will serve as the color attachment for the framebuffer
+    glGenTextures(1, &fboColorTexture);
+    glBindTexture(GL_TEXTURE_2D, fboColorTexture);
+    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_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GetScreenWidth(), GetScreenHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+    glBindTexture(GL_TEXTURE_2D, 0);
+    
+    // Create the texture that will serve as the depth attachment for the framebuffer.
+    glGenTextures(1, &fboDepthTexture);
+    glBindTexture(GL_TEXTURE_2D, fboDepthTexture);
+    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);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GetScreenWidth(), GetScreenHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    // Create the framebuffer object
+    glGenFramebuffers(1, &fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    // Attach colort texture and depth texture to FBO
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboColorTexture, 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fboDepthTexture, 0);
+
+    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    
+    if (status != GL_FRAMEBUFFER_COMPLETE) TraceLog(WARNING, "Framebuffer object could not be created...");
+    else TraceLog(INFO, "[FBO ID %i] Framebuffer object created successfully", fbo);
+
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    
+    fboShader = 0;
+    
+    // TODO: Init simple quad VAO and data here?
+}
+
 // Vertex Buffer Object deinitialization (memory free)
 void rlglClose(void)
 {
@@ -950,6 +1013,7 @@ void rlglClose(void)
     //glDeleteShader(v);
     //glDeleteShader(f);
     glDeleteProgram(defaultShaderProgram);
+    glDeleteProgram(simpleShaderProgram);
 
     // Free vertex arrays memory
     free(lines.vertices);
@@ -965,6 +1029,8 @@ void rlglClose(void)
 
     // Free GPU texture
     glDeleteTextures(1, &whiteTexture);
+    
+    if (fbo != 0) glDeleteFramebuffers(1, &fbo);
 
     free(draws);
 #endif
@@ -977,13 +1043,16 @@ void rlglDraw(void)
 
     if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0))
     {
-        glUseProgram(defaultShaderProgram);        // Use our shader
+        if (fbo == 0) glUseProgram(defaultShaderProgram);   // Use our default shader
+        else glUseProgram(fboShader);                       // Use our postpro shader
 
+        glUseProgram(defaultShaderProgram);
+        
         glUniformMatrix4fv(defaultProjectionMatrixLoc, 1, false, GetMatrixVector(projection));
         glUniformMatrix4fv(defaultModelviewMatrixLoc, 1, false, GetMatrixVector(modelview));
         glUniform1i(defaultTextureLoc, 0);
     }
-	
+
     // NOTE: We draw in this order: triangle shapes, textured quads and lines
 
     if (triangles.vCounter > 0)
@@ -1116,6 +1185,67 @@ void rlglDraw(void)
 #endif
 }
 
+void rlglDrawPostpro(unsigned int shaderId)
+{
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    
+    // TODO: Draw screen quad with texture
+/*
+    const float quadPositions[] = { 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 
+                                   -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0,  1.0, 0.0 };
+    const float quadTexcoords[] = { 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0 };
+
+    glBindBuffer(GL_ARRAY_BUFFER, quadVbo);
+
+    glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), quadPositions);
+    glVertexAttribPointer(ATTRIB_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), quadTexcoords);
+
+    glEnableVertexAttribArray(ATTRIB_VERTEX);
+    glEnableVertexAttribArray(ATTRIB_TEXCOORD0);
+
+    glBindTexture(GL_TEXTURE_2D, fboColorTexture);
+
+    glDrawArrays(GL_TRIANGLES, 0, 2*3);
+    
+    // Quad render using triangle strip
+    glBindBuffer(GL_ARRAY_BUFFER, uiVBO[1]);
+    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); 
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glUseProgram(0);
+*/
+    rlEnableTexture(fboColorTexture);
+
+    rlPushMatrix();
+        rlBegin(RL_QUADS);
+            rlColor4ub(255, 255, 255, 255);
+            rlNormal3f(0.0f, 0.0f, 1.0f);                          // Normal vector pointing towards viewer
+
+            // Bottom-left corner for texture and quad
+            rlTexCoord2f(0.0f, 1.0f);
+            rlVertex2f(0.0f, 0.0f);
+
+            // Bottom-right corner for texture and quad
+            rlTexCoord2f(0.0f, 0.0f);
+            rlVertex2f(0.0f, GetScreenHeight());
+
+            // Top-right corner for texture and quad
+            rlTexCoord2f(1.0f, 0.0f);
+            rlVertex2f(GetScreenWidth(), GetScreenHeight());
+
+            // Top-left corner for texture and quad
+            rlTexCoord2f(1.0f, 1.0f);
+            rlVertex2f(GetScreenWidth(), 0.0f);
+        rlEnd();
+    rlPopMatrix();
+    
+    fboShader = shaderId;
+    
+    rlglDraw();
+}
+
 // Draw a 3d model
 void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires)
 {
@@ -1159,23 +1289,35 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal
 #endif
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-    glUseProgram(simpleShaderProgram);        // Use our simple shader
+    if (customShader) glUseProgram(model.shaderId);
+    else glUseProgram(simpleShaderProgram);        // Use our simple shader
 
     VectorScale(&rotation, DEG2RAD);
-    
+
     // Get transform matrix (rotation -> scale -> translation)
     Matrix transform = MatrixTransform(position, rotation, scale);
     Matrix modelviewworld = MatrixMultiply(transform, modelview);
 
     // NOTE: Drawing in OpenGL 3.3+, transform is passed to shader
-    glUniformMatrix4fv(simpleProjectionMatrixLoc, 1, false, GetMatrixVector(projection));
-    glUniformMatrix4fv(simpleModelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld));
-    glUniform1i(simpleTextureLoc, 0);
+    if (customShader)
+    {
+        glUniformMatrix4fv(customProjectionMatrixLoc, 1, false, GetMatrixVector(projection));
+        glUniformMatrix4fv(customModelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld));
+        glUniform1i(customTextureLoc, 0);
+    }
+    else
+    {
+        glUniformMatrix4fv(simpleProjectionMatrixLoc, 1, false, GetMatrixVector(projection));
+        glUniformMatrix4fv(simpleModelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld));
+        glUniform1i(simpleTextureLoc, 0);
+    }
 
     // Apply color tinting to model
     // NOTE: Just update one uniform on fragment shader
     float vColor[4] = { (float)color.r/255, (float)color.g/255, (float)color.b/255, (float)color.a/255 };
-    glUniform4fv(simpleColorLoc, 1, vColor);
+
+    if (customShader) glUniform4fv(customColorLoc, 1, vColor);
+    else glUniform4fv(simpleColorLoc, 1, vColor);
 
     if (vaoSupported)
     {
@@ -1206,7 +1348,7 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal
 
     if (vaoSupported) glBindVertexArray(0);     // Unbind VAO
     else glBindBuffer(GL_ARRAY_BUFFER, 0);      // Unbind VBOs
-    
+
     glUseProgram(0);
 #endif
 
@@ -1376,7 +1518,7 @@ Model rlglLoadModel(VertexData mesh)
     model.vboId[2] = 0;         // Normals VBO
 
 #elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-    model.textureId = 1;        // Default whiteTexture
+    model.textureId = whiteTexture;     // Default whiteTexture
 
     GLuint vaoModel = 0;        // Vertex Array Objects (VAO)
     GLuint vertexBuffer[3];     // Vertex Buffer Objects (VBO)
@@ -1412,7 +1554,7 @@ Model rlglLoadModel(VertexData mesh)
     model.vboId[0] = vertexBuffer[0];     // Vertex position VBO
     model.vboId[1] = vertexBuffer[1];     // Texcoords VBO
     model.vboId[2] = vertexBuffer[2];     // Normals VBO
-    
+
     if (vaoSupported)
     {
         if (vaoModel > 0)
@@ -1510,6 +1652,102 @@ unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int heigh
     return id;
 }
 
+// Load a shader (vertex shader + fragment shader) from text data
+unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr)
+{
+    unsigned int program;
+    GLuint vertexShader;
+    GLuint fragmentShader;
+
+    vertexShader = glCreateShader(GL_VERTEX_SHADER);
+    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+    const char *pvs = vShaderStr;
+    const char *pfs = fShaderStr;
+
+    glShaderSource(vertexShader, 1, &pvs, NULL);
+    glShaderSource(fragmentShader, 1, &pfs, NULL);
+    
+    GLint success = 0;
+
+    glCompileShader(vertexShader);
+
+    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
+
+    if (success != GL_TRUE)
+    {
+        TraceLog(WARNING, "[VSHDR ID %i] Failed to compile vertex shader...", vertexShader);
+
+        int maxLength = 0;
+        int length;
+
+        glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);
+
+        char log[maxLength];
+
+        glGetShaderInfoLog(vertexShader, maxLength, &length, log);
+
+        TraceLog(INFO, "%s", log);
+    }
+    else TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader);
+
+    glCompileShader(fragmentShader);
+
+    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
+
+    if (success != GL_TRUE)
+    {
+        TraceLog(WARNING, "[FSHDR ID %i] Failed to compile fragment shader...", fragmentShader);
+
+        int maxLength = 0;
+        int length;
+
+        glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength);
+
+        char log[maxLength];
+
+        glGetShaderInfoLog(fragmentShader, maxLength, &length, log);
+
+        TraceLog(INFO, "%s", log);
+    }
+    else TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader);
+
+    program = glCreateProgram();
+
+    glAttachShader(program, vertexShader);
+    glAttachShader(program, fragmentShader);
+
+    glLinkProgram(program);
+
+    glGetProgramiv(program, GL_LINK_STATUS, &success);
+
+    if (success == GL_FALSE)
+    {
+        TraceLog(WARNING, "[SHDR ID %i] Failed to link shader program...", program);
+
+        int maxLength = 0;
+        int length;
+
+        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
+
+        char log[maxLength];
+
+        glGetProgramInfoLog(program, maxLength, &length, log);
+
+        TraceLog(INFO, "%s", log);
+        
+        glDeleteProgram(program);
+        
+        program = 0;
+    }
+    else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program);
+
+    glDeleteShader(vertexShader);
+    glDeleteShader(fragmentShader);
+
+    return program;
+}
+
 // Read screen pixel data (color buffer)
 unsigned char *rlglReadScreenPixels(int width, int height)
 {
@@ -1534,6 +1772,66 @@ unsigned char *rlglReadScreenPixels(int width, int height)
     return imgData;     // NOTE: image data should be freed
 }
 
+unsigned int LoadCustomShader(char *vsFileName, char *fsFileName)
+{
+    // Shaders loading from external text file
+    char *vShaderStr = TextFileRead(vsFileName);
+    char *fShaderStr = TextFileRead(fsFileName);
+
+    unsigned int shaderId = rlglLoadShader(vShaderStr, fShaderStr);
+
+    if (shaderId != 0) TraceLog(INFO, "[SHDR ID %i] Custom shader loaded successfully", shaderId);
+    else TraceLog(WARNING, "[SHDR ID %i] Custom shader could not be loaded", shaderId);
+
+    return shaderId;
+    
+    // Shader strings must be freed
+    free(vShaderStr);
+    free(fShaderStr);
+    
+    return shaderId;
+}
+
+// Link shader to model
+void SetModelShader(Model *model, unsigned int shader)
+{
+    // Get handles to GLSL input vars locations for simpleShaderProgram
+    customVertexLoc = glGetAttribLocation(shader, "vertexPosition");
+    customTexcoordLoc = glGetAttribLocation(shader, "vertexTexCoord");
+    customNormalLoc = glGetAttribLocation(shader, "vertexNormal");
+
+    // Get handles to GLSL uniform vars locations (vertex-shader)
+    customModelviewMatrixLoc = glGetUniformLocation(shader, "modelviewMatrix");
+    customProjectionMatrixLoc = glGetUniformLocation(shader, "projectionMatrix");
+
+    // Get handles to GLSL uniform vars locations (fragment-shader)
+    customTextureLoc = glGetUniformLocation(shader, "texture0");
+    customColorLoc = glGetUniformLocation(shader, "fragColor");
+
+    if (vaoSupported) glBindVertexArray(model->vaoId);
+
+    // Enable vertex attributes: position
+    glBindBuffer(GL_ARRAY_BUFFER, model->vboId[0]);
+    glEnableVertexAttribArray(customVertexLoc);
+    glVertexAttribPointer(customVertexLoc, 3, GL_FLOAT, 0, 0, 0);
+
+    // Enable vertex attributes: texcoords
+    glBindBuffer(GL_ARRAY_BUFFER, model->vboId[1]);
+    glEnableVertexAttribArray(customTexcoordLoc);
+    glVertexAttribPointer(customTexcoordLoc, 2, GL_FLOAT, 0, 0, 0);
+
+    // Enable vertex attributes: normals
+    glBindBuffer(GL_ARRAY_BUFFER, model->vboId[2]);
+    glEnableVertexAttribArray(customNormalLoc);
+    glVertexAttribPointer(customNormalLoc, 3, GL_FLOAT, 0, 0, 0);
+
+    if (vaoSupported) glBindVertexArray(0);     // Unbind VAO
+
+    model->shaderId = shader;
+
+    customShader = true;
+}
+
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
 
 void PrintProjectionMatrix()
@@ -1559,10 +1857,12 @@ void PrintModelviewMatrix()
 static GLuint LoadDefaultShader(void)
 {
     // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2
+    // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+
+    // Just defined #version 330 despite shader is #version 110
 
     // Vertex shader directly defined, no external file required
 #if defined(GRAPHICS_API_OPENGL_33)
-    char vShaderStr[] = " #version 110      \n"     // NOTE: Equivalent to version 100 on ES2
+    char vShaderStr[] = " #version 330      \n"     // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2)
 #elif defined(GRAPHICS_API_OPENGL_ES2)
     char vShaderStr[] = " #version 100      \n"     // NOTE: Must be defined this way! 110 doesn't work!
 #endif
@@ -1582,7 +1882,7 @@ static GLuint LoadDefaultShader(void)
 
     // Fragment shader directly defined, no external file required
 #if defined(GRAPHICS_API_OPENGL_33)
-    char fShaderStr[] = " #version 110      \n"     // NOTE: Equivalent to version 100 on ES2
+    char fShaderStr[] = " #version 330      \n"     // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2)
 #elif defined(GRAPHICS_API_OPENGL_ES2)
     char fShaderStr[] = " #version 100      \n"     // NOTE: Must be defined this way! 110 doesn't work!
         "precision mediump float;           \n"     // WebGL, required for emscripten
@@ -1595,63 +1895,12 @@ static GLuint LoadDefaultShader(void)
         "    gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; \n"
         "}                                  \n";
 
-    GLuint program;
-    GLuint vertexShader;
-    GLuint fragmentShader;
-
-    vertexShader = glCreateShader(GL_VERTEX_SHADER);
-    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
-
-    const char *pvs = vShaderStr;
-    const char *pfs = fShaderStr;
-
-    glShaderSource(vertexShader, 1, &pvs, NULL);
-    glShaderSource(fragmentShader, 1, &pfs, NULL);
-
-    GLint success = 0;
-
-    glCompileShader(vertexShader);
-
-    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
-
-    if (success != GL_TRUE)  TraceLog(WARNING, "[VSHDR ID %i] Failed to compile default vertex shader...", vertexShader);
-    else TraceLog(INFO, "[VSHDR ID %i] Default vertex shader compiled successfully", vertexShader);
-
-    glCompileShader(fragmentShader);
-
-    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
-
-    if (success != GL_TRUE)  TraceLog(WARNING, "[FSHDR ID %i] Failed to compile default fragment shader...", fragmentShader);
-    else TraceLog(INFO, "[FSHDR ID %i] Default fragment shader compiled successfully", fragmentShader);
-
-    program = glCreateProgram();
-
-    glAttachShader(program, vertexShader);
-    glAttachShader(program, fragmentShader);
-
-    glLinkProgram(program);
-
-    glGetProgramiv(program, GL_LINK_STATUS, &success);
-
-    if (success == GL_FALSE)
-    {
-        int maxLength;
-        int length;
+    unsigned int shaderId = rlglLoadShader(vShaderStr, fShaderStr);
 
-        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
-
-        char log[maxLength];
-
-        glGetProgramInfoLog(program, maxLength, &length, log);
-
-        TraceLog(INFO, "Shader program fail log: %s", log);
-    }
-    else TraceLog(INFO, "[SHDR ID %i] Default shader program loaded successfully", program);
-
-    glDeleteShader(vertexShader);
-    glDeleteShader(fragmentShader);
+    if (shaderId != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shaderId);
+    else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shaderId);
 
-    return program;
+    return shaderId;
 }
 
 // Load Simple Shader (Vertex and Fragment)
@@ -1659,10 +1908,12 @@ static GLuint LoadDefaultShader(void)
 static GLuint LoadSimpleShader(void)
 {
     // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2
+    // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+
+    // Just defined #version 330 despite shader is #version 110
 
     // Vertex shader directly defined, no external file required
 #if defined(GRAPHICS_API_OPENGL_33)
-    char vShaderStr[] = " #version 110      \n"     // NOTE: Equivalent to version 100 on ES2
+    char vShaderStr[] = " #version 330      \n"     // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2)
 #elif defined(GRAPHICS_API_OPENGL_ES2)
     char vShaderStr[] = " #version 100      \n"     // NOTE: Must be defined this way! 110 doesn't work!
 #endif
@@ -1680,7 +1931,7 @@ static GLuint LoadSimpleShader(void)
 
     // Fragment shader directly defined, no external file required
 #if defined(GRAPHICS_API_OPENGL_33)
-    char fShaderStr[] = " #version 110      \n"     // NOTE: Equivalent to version 100 on ES2
+    char fShaderStr[] = " #version 330      \n"     // NOTE: Actually, #version 110 (quivalent to #version 100 on ES2)
 #elif defined(GRAPHICS_API_OPENGL_ES2)
     char fShaderStr[] = " #version 100      \n"     // NOTE: Must be defined this way! 110 doesn't work!
         "precision mediump float;           \n"     // precision required for OpenGL ES2 (WebGL)
@@ -1693,117 +1944,22 @@ static GLuint LoadSimpleShader(void)
         "    gl_FragColor = texture2D(texture0, fragTexCoord) * fragColor; \n"
         "}                                  \n";
 
-    GLuint program;
-    GLuint vertexShader;
-    GLuint fragmentShader;
-
-    vertexShader = glCreateShader(GL_VERTEX_SHADER);
-    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
-
-    const char *pvs = vShaderStr;
-    const char *pfs = fShaderStr;
-
-    glShaderSource(vertexShader, 1, &pvs, NULL);
-    glShaderSource(fragmentShader, 1, &pfs, NULL);
-
-    GLint success = 0;
-
-    glCompileShader(vertexShader);
-
-    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
-
-    if (success != GL_TRUE)  TraceLog(WARNING, "[VSHDR ID %i] Failed to compile simple vertex shader...", vertexShader);
-    else TraceLog(INFO, "[VSHDR ID %i] Simple vertex shader compiled successfully", vertexShader);
-
-    glCompileShader(fragmentShader);
-
-    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
-
-    if (success != GL_TRUE)  TraceLog(WARNING, "[FSHDR ID %i] Failed to compile simple fragment shader...", fragmentShader);
-    else TraceLog(INFO, "[FSHDR ID %i] Simple fragment shader compiled successfully", fragmentShader);
-
-    program = glCreateProgram();
-
-    glAttachShader(program, vertexShader);
-    glAttachShader(program, fragmentShader);
-
-    glLinkProgram(program);
-
-    glGetProgramiv(program, GL_LINK_STATUS, &success);
-
-    if (success == GL_FALSE)
-    {
-        int maxLength;
-        int length;
-
-        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
-
-        char log[maxLength];
-
-        glGetProgramInfoLog(program, maxLength, &length, log);
-
-        TraceLog(INFO, "Shader program fail log: %s", log);
-    }
-    else TraceLog(INFO, "[SHDR ID %i] Simple shader program loaded successfully", program);
-
-    glDeleteShader(vertexShader);
-    glDeleteShader(fragmentShader);
-
-    return program;
-}
-
-// Load shaders from text files
-static GLuint LoadCustomShader(char *vertexFileName, char *fragmentFileName)
-{
-    // Shaders loading from external text file
-    char *vShaderStr = TextFileRead(vertexFileName);
-    char *fShaderStr = TextFileRead(fragmentFileName);
-
-    GLuint program;
-    GLuint vertexShader;
-    GLuint fragmentShader;
-
-    vertexShader = glCreateShader(GL_VERTEX_SHADER);
-    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
-
-    const char *pvs = vShaderStr;
-    const char *pfs = fShaderStr;
-
-    glShaderSource(vertexShader, 1, &pvs, NULL);
-    glShaderSource(fragmentShader, 1, &pfs, NULL);
-
-    glCompileShader(vertexShader);
-    glCompileShader(fragmentShader);
-
-    TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader);
-    TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader);
-
-    program = glCreateProgram();
-
-    glAttachShader(program, vertexShader);
-    glAttachShader(program, fragmentShader);
-
-    glLinkProgram(program);
-
-    glDeleteShader(vertexShader);
-    glDeleteShader(fragmentShader);
-    
-    free(vShaderStr);
-    free(fShaderStr);
+    unsigned int shaderId = rlglLoadShader(vShaderStr, fShaderStr);
 
-    TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program);
+    if (shaderId != 0) TraceLog(INFO, "[SHDR ID %i] Simple shader loaded successfully", shaderId);
+    else TraceLog(WARNING, "[SHDR ID %i] Simple shader could not be loaded", shaderId);
 
-    return program;
+    return shaderId;
 }
 
-// Read shader text file
+// Read text file
 // NOTE: text chars array should be freed manually
 static char *TextFileRead(char *fileName)
 {
     FILE *textFile;
     char *text = NULL;
 
-    int count=0;
+    int count = 0;
 
     if (fileName != NULL)
     {
@@ -1817,7 +1973,7 @@ static char *TextFileRead(char *fileName)
 
             if (count > 0)
             {
-                text = (char *)malloc(sizeof(char) * (count+1));
+                text = (char *)malloc(sizeof(char) * (count + 1));
                 count = fread(text, sizeof(char), count, textFile);
                 text[count] = '\0';
             }
@@ -1826,7 +1982,7 @@ static char *TextFileRead(char *fileName)
         }
         else TraceLog(WARNING, "[%s] Text file could not be opened", fileName);
     }
-    
+
     return text;
 }
 
@@ -1925,7 +2081,7 @@ static void InitializeBuffersGPU(void)
         glGenVertexArrays(1, &vaoTriangles);
         glBindVertexArray(vaoTriangles);
     }
-    
+
     // Create buffers for our vertex data
     glGenBuffers(2, trianglesBuffer);
 
@@ -1950,7 +2106,7 @@ static void InitializeBuffersGPU(void)
         glGenVertexArrays(1, &vaoQuads);
         glBindVertexArray(vaoQuads);
     }
-    
+
     // Create buffers for our vertex data
     glGenBuffers(4, quadsBuffer);
 
@@ -1994,7 +2150,7 @@ static void UpdateBuffers(void)
     {
         // Activate Lines VAO
         if (vaoSupported) glBindVertexArray(vaoLines);
-    
+
         // Lines - vertex positions buffer
         glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]);
         //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW);
@@ -2011,7 +2167,7 @@ static void UpdateBuffers(void)
     {
         // Activate Triangles VAO
         if (vaoSupported) glBindVertexArray(vaoTriangles);
-    
+
         // Triangles - vertex positions buffer
         glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]);
         //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW);
diff --git a/src/rlgl.h b/src/rlgl.h
index b42b388b0..94cf60722 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -145,22 +145,27 @@ void rlColor4f(float x, float y, float z, float w); // Define one vertex (color)
 void rlEnableTexture(unsigned int id);          // Enable texture usage
 void rlDisableTexture(void);                    // Disable texture usage
 void rlDeleteTextures(unsigned int id);         // Delete OpenGL texture from GPU
+void rlDeleteShader(unsigned int id);           // Delete OpenGL shader program from GPU
 void rlDeleteVertexArrays(unsigned int id);     // Unload vertex data (VAO) from GPU memory
 void rlDeleteBuffers(unsigned int id);          // Unload vertex data (VBO) from GPU memory
 void rlClearColor(byte r, byte g, byte b, byte a);  // Clear color buffer with color
 void rlClearScreenBuffers(void);                // Clear used screen buffers (color and depth)
 int rlGetVersion(void);                         // Returns current OpenGL version
+void rlEnableFBO(void);
 
 //------------------------------------------------------------------------------------
 // Functions Declaration - rlgl functionality
 //------------------------------------------------------------------------------------
 void rlglInit(void);                            // Initialize rlgl (shaders, VAO, VBO...)
+void rlglInitPostpro(void);                     // Initialize postprocessing system
 void rlglClose(void);                           // De-init rlgl
 void rlglDraw(void);                            // Draw VAO/VBO
+void rlglDrawPostpro(unsigned int shaderId);    // Draw with postpro shader
 void rlglInitGraphics(int offsetX, int offsetY, int width, int height);  // Initialize Graphics (OpenGL stuff)
 
 unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps);       // Load in GPU OpenGL texture
 unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format);
+unsigned int rlglLoadShader(char *vShaderStr, char *fShaderStr); // Load a shader from text data
 
 Model rlglLoadModel(VertexData mesh);           // Upload vertex data into GPU and provided VAO/VBO ids
 void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires);