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 !