OpenGL es 2.0 Gaussian blur no triângulo

Recentemente, aprendi o OpenGL es 2.0, e agora tento fazer um borrão gaussiano nos triângulos gerar por mim mesmo. Tenho alguns exemplos difíceis de entender na web e a maioria aplica o desfoque em uma imagem. Eu sei que tenho que usar o framebuffer, mas não sei desenhar triângulo nisso e aplicar desfoque. É possível ver um código real e completo em C ++ com boa explicação?

EDIT:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#define GLFW_INCLUDE_ES2
#include <GLFW/glfw3.h>
#include "shaders.hpp"
#include "camera.hpp"

unsigned int vbo, cbo, tbo;
GLuint _fbo, _fbo2, _tex, _tex2;


static const GLuint WIDTH = 800;
static const GLuint HEIGHT = 600;
GLuint pos, col, tex, normal;
camera * _camera = new camera();

static const GLfloat vertices[] = {
  0.0f,  1.0f, 0.0f,
  1.0f, -1.0f, 0.0f,
  -1.0f, -1.0f, 0.0f
};

static const GLfloat colors[] = {
  0.0f,  0.5f, 1.0f,
  0.5f,  0.5f, 1.0f,
  0.5f,  0.5f, 1.0f
};

static const GLfloat texture[] = {
  1.0f, 1.0f,
  1.0f, 0.0f,
  0.0f, 1.0f
};

int main(void){
  GLFWwindow* window;
  shaders * shaderBasic;
  GLuint pId;

  glm::mat4 projection; static glm::mat4 view; static glm::mat4 model;

  glfwInit();
  glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  window = glfwCreateWindow(WIDTH, HEIGHT, __FILE__, NULL, NULL);
  glfwMakeContextCurrent(window);

  printf("GL_VERSION  : %s\n", glGetString(GL_VERSION) );
  printf("GL_RENDERER : %s\n", glGetString(GL_RENDERER) );

  std::string vs, fs;
  vs = "basic.vs";
  fs = "basic.fs";
  shaderBasic = new shaders(vs, fs);
  shaderBasic->CompileShader();
  shaderBasic->LinkShader();
  pId = shaderBasic->getProgramId();

  pos = glGetAttribLocation(pId, "position");
  col = glGetAttribLocation(pId, "colors");
  tex = glGetAttribLocation(pId, "tex");

  fs = "lastBlur.fs";
  shaders * blurShader;
  GLuint pIdBlur;
  blurShader = new shaders(vs, fs);
  blurShader->CompileShader();
  blurShader->LinkShader();
  pIdBlur = blurShader->getProgramId();

  _camera->setPositionCamera(glm::vec3(0, 0, -1));
  _camera->setLookAtCamera(glm::vec3(0, 0, 0));
  _camera->setFieldOfView(45);
  _camera->setAspect(WIDTH, HEIGHT);
  _camera->setViewport(WIDTH, HEIGHT);
  _camera->getMatricies(projection, view, model);

  glGenFramebuffers(1, &_fbo);
  glGenTextures(1, &_tex);
  glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
  glBindTexture(GL_TEXTURE_2D, _tex);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH/2, HEIGHT/2, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _tex, 0);
  glBindTexture(GL_TEXTURE_2D, 0);
  glBindFramebuffer(GL_FRAMEBUFFER, 0);

  if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
  else{
    std::cout << "FRAMEBUFFER COMPLETE" << std::endl;
  }
  auto sampTex = glGetUniformLocation(pIdBlur, "texture0");
  std::cerr << "sampTex : " << sampTex << std::endl;
  glUniform1i(sampTex, 0);  
  while (!glfwWindowShouldClose(window)) {
    //    glViewport(0, 0, WIDTH, HEIGHT);

    glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
    glClearColor(0.0f, 0.0f, 0.4f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //    glViewport(0, 0, WIDTH/2, HEIGHT/2);
    glUseProgram(pIdBlur);
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(pos, 3, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(pos);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glGenBuffers(1, &cbo);
    glBindBuffer(GL_ARRAY_BUFFER, cbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
    glVertexAttribPointer(col, 2, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(col);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glGenBuffers(1, &tbo);
    glBindBuffer(GL_ARRAY_BUFFER, tbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(texture), texture, GL_STATIC_DRAW);
    glVertexAttribPointer(tex, 2, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(tex);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glDrawArrays(GL_TRIANGLES, 0, 3);


    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glUseProgram(pId);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, _tex);
    glDrawArrays(GL_TRIANGLES, 0, 3);    

    glfwPollEvents();
    glfwSwapBuffers(window);
  }
  glDeleteBuffers(1, &vbo);
  glfwTerminate();
  return EXIT_SUCCESS;
}

Shader do borrão:

#version 100
precision mediump float;

uniform sampler2D texture0;
varying vec3 vColor;
varying vec2 TexCoords;

vec4 blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) {
    vec4 color = vec4(0.0);
    vec2 off1 = vec2(1.411764705882353) * direction;
    vec2 off2 = vec2(3.2941176470588234) * direction;
    vec2 off3 = vec2(5.176470588235294) * direction;
    color += texture2D(image, uv) * 0.1964825501511404;
    color += texture2D(image, uv + (off1 / resolution)) * 0.2969069646728344;
    color += texture2D(image, uv - (off1 / resolution)) * 0.2969069646728344;
    color += texture2D(image, uv + (off2 / resolution)) * 0.09447039785044732;
    color += texture2D(image, uv - (off2 / resolution)) * 0.09447039785044732;
    color += texture2D(image, uv + (off3 / resolution)) * 0.010381362401148057;
    color += texture2D(image, uv - (off3 / resolution)) * 0.010381362401148057;
    return color;
}

void main(){
    gl_FragColor = blur13(texture0, TexCoords, vec2(400, 300), vec2(1.0, 0.0));
}

questionAnswers(2)

yourAnswerToTheQuestion