1

I'm trying to learn OpenGL language, and I would like to do a little code that do a horizontal blur (HR) on an image, and then a vertical blur (VB) on the previous result.

I used a Framebuffer to this purpose, but I'm not sure how to use the texture in the Framebuffer as the new texture.

This is my code :

// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <ctime>

#include <iostream>

//#include <opencv.hpp>
#include <opencv/cv.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv/highgui.h>
using namespace cv;

// Include GLEW
#include <GL/glew.h> 

// Include GLFW
#include <GLFW/glfw3.h> 

#include <shader.hpp>
using namespace std;

int width = 512;// 1024;
int height = 512;// 768;

// perspective projection
bool perspective_ = false;

// shader variable pointers
GLint uniform_srcTex;
GLint uniform_srcTex1;
GLint uniform_offset_x;
GLint uniform_offset_y;

////////////////////////////////////////
// glfw callbacks for keystroke and error
void error_callback(int error, const char* description)
{
    fputs(description, stderr);
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

void char_callback(GLFWwindow* window, unsigned int key)
{
    if (key == 'p' || key == 'P')
        perspective_ = true;
    if (key == 'o' || key == 'O')
        perspective_ = false;
}

/////////////////////////////////////////////
// texture loading
bool loadtexture(string fileName, GLuint & texIndex, GLuint texUnit, bool isRect)
{

// texture load through OpenCV
    Mat image = cv::imread(fileName, CV_LOAD_IMAGE_UNCHANGED);   // Read the file
    if (!image.data) // Check for invalid input
    {
        cout << "Could not open or find the image\n";
        return false;
    }
    cout << "Loaded " << fileName.c_str() << " (" << image.channels() << " channels)\n";

    int colorTransform = (image.channels() == 4) ? CV_BGRA2RGBA : (image.channels() == 3) ? CV_BGR2RGB : CV_GRAY2RGB;
    //if (image[index].channels() >= 3)
    //{
        cv::cvtColor(image, image, colorTransform);
    //}

    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &texIndex);
    glActiveTexture(texUnit);
    GLenum target = GL_TEXTURE_2D;
    if (isRect)
    {
        target = GL_TEXTURE_RECTANGLE;
    }
    glBindTexture(target, texIndex);
    if (image.channels() > 3)
    {
        glTexImage2D(target, 0, GL_RGBA8, image.cols, image.rows, 0, GL_RGBA, (image.depth()<2)?GL_UNSIGNED_BYTE: ((image.depth()<4) ? GL_UNSIGNED_SHORT : GL_FLOAT), image.ptr());
    }
    else
    {
        glTexImage2D(target, 0, GL_RGB, image.cols, image.rows, 0, GL_RGB, (image.depth()<2) ? GL_UNSIGNED_BYTE : ((image.depth()<4) ? GL_UNSIGNED_SHORT : GL_FLOAT), image.ptr());
    }
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    // glGenerateMipmap(GL_TEXTURE_2D);
    return true;
    }

void consoleMessage()
{
    cout << "Renderer : " << string((char*)glGetString(GL_RENDERER)) << endl;
    cout << "OpenGL version: " << string((char*)glGetString(GL_VENDOR)) << " / " << string((char*)glGetString(GL_VERSION)) << endl;
    cout << "GLSL version: " << string((char*)glGetString(GL_SHADING_LANGUAGE_VERSION)) << endl;
    cout << "GLEW version: " << string((char*)glewGetString(GLEW_VERSION)) << endl << endl;

    GLint MaxTextureUnits;
    glGetIntegerv(GL_MAX_TEXTURE_UNITS, &MaxTextureUnits);
    cout << "Max supported textures : " << MaxTextureUnits << endl << endl;
}


////////////////////////////////////////
// main file
int main()
{
    // start GL context and O/S window using the GLFW helper library
    if (!glfwInit()) // Initialise GLFW
    {
        fprintf(stderr, "ERROR: could not start GLFW3\n");
        return 1;
    }

    GLFWwindow* window = glfwCreateWindow(width, height, "test1", NULL, NULL);
    if (!window)
    {
        fprintf(stderr, "ERROR: could not open window with GLFW3\n");
        glfwTerminate();
        return 1;
    }
    glfwMakeContextCurrent(window); // Initialise GLEW

    // Set key callback function
    glfwSetErrorCallback(error_callback); 
    glfwSetKeyCallback(window, key_callback); 
    glfwSetCharCallback(window, char_callback); 

    // start GLEW extension handler
    glewExperimental = GL_TRUE; 
    glewInit(); // Initialise GLEW

    // get version info
    consoleMessage();

    GLuint srcTexIndex;

    if (!loadtexture("blablabla.png", srcTexIndex, GL_TEXTURE0, true)) 
    { 
        return -1;
    }

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    ////////////////////////////////////////
    // Load shaders
    GLuint shader_image_programme_HB = LoadShaders("shaders/SimpleVertexShader_VS_HB.glsl", "shaders/AddGrain_FS_HB.glsl");
    GLuint shader_image_programme_VB = LoadShaders("shaders/SimpleVertexShader_VS_VB.glsl", "shaders/AddGrain_FS_VB.glsl");

    ////////////////////////////////////////
    // shader parameter bindings

    uniform_srcTex = glGetUniformLocation(shader_image_programme_HB, "srcTex"); 
    uniform_offset_x = glGetUniformLocation(shader_image_programme_HB, "offset_x");
    uniform_offset_y = glGetUniformLocation(shader_image_programme_HB, "offset_y");

    const int nb_frame = 4096;

    vector<float> offset_x(nb_frame, 0.0);
    vector<float> offset_y(nb_frame, 0.0);

    int frame_index = 0;

    glUseProgram(shader_image_programme_HB); 

    glUniform1i(uniform_srcTex, 0); //Texture unit 0

    // input texture (loaded texture)
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_RECTANGLE, srcTexIndex);

    // setup the projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, (double)width, 0, (double)height, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
    GLuint FramebufferName;
    glGenFramebuffers(1, &FramebufferName);
    glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);

    // The texture we're going to render to
    GLuint renderedTexture;
    glGenTextures(1, &renderedTexture);

    glActiveTexture(GL_TEXTURE1);
    // "Bind" the newly created texture : all future texture functions will modify this texture
    glBindTexture(GL_TEXTURE_2D, renderedTexture);

    // Give an empty image to OpenGL ( the last "0" )
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, 0);

    // Poor filtering. Needed !
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);

    // Set "renderedTexture" as our colour attachement #0
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);

    // Set the list of draw buffers.
    GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
    glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers

    // Always check that our framebuffer is ok
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        printf("Error with Frame Buffer !!!\n");
        return 1;
    }

    // Render to our framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    cout << "ok HB" << endl;


    GLuint srcTexIndex1;

    uniform_srcTex1 = glGetUniformLocation(shader_image_programme_VB, "srcTex"); 
    uniform_offset_x = glGetUniformLocation(shader_image_programme_VB, "offset_x");
    uniform_offset_y = glGetUniformLocation(shader_image_programme_VB, "offset_y");

    frame_index = 0;

    glUseProgram(shader_image_programme_VB); // On dit à OpenGL qu'on veut utiliser les shaders

    glUniform1i(uniform_srcTex1, 1); //Texture unit 1

    // input texture (loaded texture)
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_RECTANGLE, srcTexIndex1);

    // setup the projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, (double)width, 0, (double)height, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
    GLuint FramebufferName1;
    glGenFramebuffers(1, &FramebufferName1);
    glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName1);

    // The texture we're going to render to
    GLuint renderedTexture1;
    glGenTextures(1, &renderedTexture1);

    glActiveTexture(GL_TEXTURE2);
    // "Bind" the newly created texture : all future texture functions will modify this texture
    glBindTexture(GL_TEXTURE_2D, renderedTexture1);

    // Give an empty image to OpenGL ( the last "0" )
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, 0);

    // Poor filtering. Needed !
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);

    // Set "renderedTexture" as our colour attachement #1
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, renderedTexture1, 0);

    // Set the list of draw buffers.
    GLenum DrawBuffers1[1] = { GL_COLOR_ATTACHMENT1 };
    glDrawBuffers(1, DrawBuffers1); // "1" is the size of DrawBuffers

    // Always check that our framebuffer is ok
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        printf("Error with Frame Buffer !!!\n");
        return 1;
    }

    // Render to our framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0);


    cout << "ok VB" << endl;

    glViewport(0, 0, width, height); // Render on the whole framebuffer, complete from the lower left corner to the upper right

    // Returned data
    Mat myData = Mat::zeros(height, width, CV_32FC3);

    ////////////////////////////////////////
    // endless rendering loop
    int nbFrames = 0;

    clock_t start = clock();

    while (!glfwWindowShouldClose(window))
    {


        //////////////////////////////////////////////////
        // PASS #1
        // output buffer cleanup
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

        glUniform1f(uniform_offset_x, offset_x[frame_index]);
        glUniform1f(uniform_offset_y, offset_y[frame_index]);

        // Define the drawing area by setting the corresponding vertices
        glBegin(GL_QUADS);
        glVertex2f(0., 0.);
        glVertex2f(0., (float)height);
        glVertex2f((float)width, (float)height);
        glVertex2f((float)width, 0.);
        glEnd();


        if (nbFrames == 0)
        {
            glReadPixels(0, 0, width, height, GL_RGB, GL_FLOAT, myData.ptr(0));

            myData.convertTo(myData, CV_8U, 255.0, 0.0);

            imwrite("C:/Temp/images/testOpenGL.png", myData);
        }


        ///////////////////////////////////////
        // EVENTS + FB swap 
        // Permet de quitter la fenêtre avec la touche esc

        // update other events like input handling 
        glfwPollEvents();

        // put the stuff we've been drawing onto the display
        glfwSwapBuffers(window);

        nbFrames++;
        frame_index = (frame_index + 1) % 4096;
    }

    clock_t duration = clock() - start;

    //printf("%d processed frames\n", nbFrames);

    cout << nbFrames << " in " << duration << " ms : " << 1000.0*(float)nbFrames / (float)duration << " frame/s" << endl;

    // close GL context and any other GLFW resources
    glfwDestroyWindow(window);
    glfwTerminate();
    exit(EXIT_SUCCESS);
}

The vertex shader for HB :

void main()
{
    gl_Position = ftransform();
}

The vertex shader for VB :

void main()
{
    gl_Position = ftransform();
}

The fragment shader for HB :

in vec2 texCoordOut;
layout (location = 0) out vec4 outColor0;
uniform sampler2DRect srcTex;

uniform float offset_x;
uniform float offset_y;

const vec2 texOffset = vec2(1.0, 1.0);
const int BLUR_AMOUNT = 100;

void main()
{
    vec2 CoordRef = gl_FragCoord.xy + vec2(offset_x,offset_y);
    vec3 luma = vec3(0);
    luma = texture2DRect( srcTex, CoordRef ).rgb;

    for (int i = 1; i < BLUR_AMOUNT+1; ++i) {
        luma.r += texture2DRect( srcTex, CoordRef + vec2(0, i) ).r * 0.5/BLUR_AMOUNT;
        luma.g += texture2DRect( srcTex, CoordRef + vec2(0, i) ).g * 0.5/BLUR_AMOUNT;
        luma.b += texture2DRect( srcTex, CoordRef + vec2(0, i) ).b * 0.5/BLUR_AMOUNT;
    }
    vec4 out_bw = vec4(luma, 1.0);
    gl_FragColor = out_bw;  
}

The fragment shader for VB :

in vec2 texCoordOut;
layout (location = 0) out vec4 outColor0;
uniform sampler2DRect srcTex; 

uniform float offset_x;
uniform float offset_y;

const vec2 texOffset = vec2(1.0, 1.0);
const int BLUR_AMOUNT = 100;

void main()
{
    vec2 CoordRef = gl_FragCoord.xy + vec2(offset_x,offset_y);
    vec3 luma = vec3(0);
    luma = texture2DRect( srcTex, CoordRef ).rgb;

    for (int i = 1; i < BLUR_AMOUNT+1; ++i) {
        luma.r += texture2DRect( srcTex, CoordRef + vec2(i, 0) ).r * 0.5/BLUR_AMOUNT;
        luma.g += texture2DRect( srcTex, CoordRef + vec2(i, 0) ).g * 0.5/BLUR_AMOUNT;
        luma.b += texture2DRect( srcTex, CoordRef + vec2(i, 0) ).b * 0.5/BLUR_AMOUNT;
    }

    vec4 out_bw = vec4(luma, 1.0);
    gl_FragColor = out_bw;
}

At the end, I have a full black screen, which is not the attended result (I checked). All the shaders worked fine, it's the full sequence of the two shaders that doesn't work. Can you tell me what I did as an error in my code ?

Thank you for your help !

  • It seems you are not rendering anything with these shaders? Since you have two shaders that have to run, you have to render at least two quads (one with each shader). – BDL Jun 07 '18 at 08:57
  • That's not what the glViewport function does ? – Philippine Prevost Jun 07 '18 at 09:00
  • No. glViewport specifies how large the framebuffer is. It does not render anything. glBegin(GL_QUADS) and so on do the rendering. – BDL Jun 07 '18 at 09:01
  • It's the first time I use this function, so to be sure : in the first case, I need to write : glBegin(GL_QUADS);{ glVertex2f(renderedTexture.offset, 0); glVertex2f(renderedTexture.offset + width, 0); glVertex2f(renderedTexture.offset, height); glVertex2f(renderedTexture.offset + width, height); } glEnd(); And same with renderedTexture1, right ? – Philippine Prevost Jun 07 '18 at 09:16
  • I'm not sure what rendertexture.offset is, but basically yes. You need to do the following for each renderpass: bind framebuffer -> bind shader -> bind input texture -> render quad -> unbind framebuffer. – BDL Jun 07 '18 at 09:17
  • Just saw I did one glBegin at the end of the code. I added one before the first "glBindFramebuffer(GL_FRAMMEBUFFER,0);" but I still have my black screen. – Philippine Prevost Jun 07 '18 at 09:31
  • 1
    see [simple complete GL+VAO/VBO+GLSL+shaders example in C++](https://stackoverflow.com/a/31913542/2521214) to have something working to start with. See the `glGetShaderInfoLog` on how to obtain GLSL shaders compilation/link logs to actually see what the problem is. It is under VCL so just port the events to your style of programing. You should build your app incrementaly so start with single quad and very simple shaders no texture ... and when working add textures ... and features one by one so you know exactly where to look for a bug – Spektre Jun 08 '18 at 07:07
  • 1
    also this [Render OpenGL scene to texture using FBO](https://stackoverflow.com/a/43930271/2521214) might help so you have something to compare with. – Spektre Jun 08 '18 at 07:15
  • Rabbid76 I did it but still the same black screen. Spektre, I'm not sure what you are asking me. I had something that works, but with one shader at a time. I also had a combination of both that gave me only the result of the second shader, as if the first had never happened (when I write glUniform1i(uniform_srcTex1, 0); instead of glUniform1i(uniform_srcTex1, 1);, which is normal because I use my input image as the input of the second shader). The black screen comes with glUniform1i(uniform_srcTex1, 1); so I suppose I didn't bind well TEXTURE1 to the FrameBuffer – Philippine Prevost Jun 08 '18 at 08:49
  • I think you have the idea nailed down more or less, and what's left here is just to debug it. As you can see, [so] isn't really a great debugging tool. Try to look for some GL debug tools. You might ask again if/when you have a concrete root cause narrowed down and don't know what to do next, but "a black screen" is just hiding too many important details that only you know. – Bartek Banachewicz Jun 08 '18 at 09:01
  • Also please don't mix immediate mode calls (`glBegin`) and shaders - I recommend picking one GL version (such as 4.5) and sticking to it. – Bartek Banachewicz Jun 08 '18 at 09:02
  • Thank you Bartek Banachewicz, I will try to find GL debug tools. I thought that the answer was obvious, but as I started I couldn't find it. Thank you for your help ! – Philippine Prevost Jun 08 '18 at 09:18

0 Answers0