Я пытаюсь выучить язык OpenGL, и я хотел бы написать небольшой код, который делает горизонтальное размытие (HR) на изображении, а затем вертикальное размытие (VB) для предыдущего результата.
Я использовал для этого Framebuffer, но я не уверен, как использовать текстуру в Framebuffer в качестве новой текстуры.
Это мой код:
// 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);
}
Вершинный шейдер для HB:
void main()
{
gl_Position = ftransform();
}
Вершинный шейдер для VB:
void main()
{
gl_Position = ftransform();
}
Фрагмент шейдера для 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;
}
Фрагмент шейдера для 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;
}
В конце у меня полный черный экран, который не является посещаемым результатом (я проверил).Все шейдеры работали нормально, полная последовательность двух шейдеров не работает.Можете ли вы сказать мне, что я сделал как ошибку в моем коде?
Спасибо за вашу помощь!