异构计算GLSL学习笔记(1)
原文地址:http://blog.csdn.net/hjimce/article/details/51475644
作者:hjimce
最近开始学习深度学习的一些gpu编程,大体学了cuda后,感觉要在手机上跑深度学习,没有nvidia显卡,不能加速。所以只能老老实实的学习opengl的shader编程,进行gpu通用计算加速,总的感觉shader编程比cuda编程难,还好自己之前研究生的时候,经常用opengl的一些API函数,对opengl比较了解。对于每一种语言,最简单的学习程序就是:hello world,下面记录一下shader编程最简单的例子,利用gpu加速实现彩色图像转灰度图像。大体包含五个步骤:
1、初始化环境、创建shader程序等;
2、根据需求,编写片元着色器的纹理图像处理代码;
3、从cpu传入数据(利用uniform类型变量,传数据到shader中,如果是图片可以直接采用纹理传入);
4、设置渲染模型、纹理坐标,绘制矩形,进行渲染计算;
5、取回图片处理后的数据;
一、opengl与shader环境初始化、编译链接
(1)初始化环境
//1、初始化环境 glutInit(&argc, argv); //glutInitWindowSize(512,512); //glutInitWindowPosition(100,100); glutCreateWindow("GLEW Test"); glewExperimental = GL_TRUE; glewInit(); if (!glewIsSupported("GL_VERSION_4_0")) { std::cerr << "Failed to initialize GLEW with OpenGL 4.0!" << std::endl; return EXIT_FAILURE; }
(2)创建链接、编译shader程序
//2、读取、创建shader程序,编译连接等 auto program_id = ShaderProgram("shader/gl_texture.vert", "shader/gl_texture.frag"); glUseProgram(program_id);
ShaerProgram函数的代码如下:
GLuint ShaderProgram(const std::string &vertex_shader_file, const std::string &fragment_shader_file) { // 创建shader程序 auto vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); auto fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER); auto result = GL_FALSE; auto info_length = 0; // 读取shader源码 std::ifstream vertex_shader_stream(vertex_shader_file); std::string vertex_shader_code((std::istreambuf_iterator<char>(vertex_shader_stream)), std::istreambuf_iterator<char>()); std::ifstream fragment_shader_stream(fragment_shader_file); std::string fragment_shader_code((std::istreambuf_iterator<char>(fragment_shader_stream)), std::istreambuf_iterator<char>()); // 编译顶点shader代码 std::cout << "Compiling Vertex Shader ..." << std::endl; auto vertex_shader_code_ptr = vertex_shader_code.c_str(); glShaderSource(vertex_shader_id, 1, &vertex_shader_code_ptr, NULL); glCompileShader(vertex_shader_id); // Check vertex shader log glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result); if (result == GL_FALSE) { glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_length); std::string vertex_shader_log((unsigned int)info_length, ' '); glGetShaderInfoLog(vertex_shader_id, info_length, NULL, &vertex_shader_log[0]); std::cout << vertex_shader_log << std::endl; } // 编译片元着色器代码 std::cout << "Compiling Fragment Shader ..." << std::endl; auto fragment_shader_code_ptr = fragment_shader_code.c_str(); glShaderSource(fragment_shader_id, 1, &fragment_shader_code_ptr, NULL); glCompileShader(fragment_shader_id); // Check fragment shader log glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result); if (result == GL_FALSE) { glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_length); std::string fragment_shader_log((unsigned long)info_length, ' '); glGetShaderInfoLog(fragment_shader_id, info_length, NULL, &fragment_shader_log[0]); std::cout << fragment_shader_log << std::endl; } // 创建链接程序 std::cout << "Linking Shader Program ..." << std::endl; auto program_id = glCreateProgram(); glAttachShader(program_id, vertex_shader_id); glAttachShader(program_id, fragment_shader_id); glBindFragDataLocation(program_id, 0, "FragmentColor"); glLinkProgram(program_id); //打印编译信息,如果编译错误,就可以看见错误信息了 glGetProgramiv(program_id, GL_LINK_STATUS, &result); if (result == GL_FALSE) { glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_length); std::string program_log((unsigned long)info_length, ' '); glGetProgramInfoLog(program_id, info_length, NULL, &program_log[0]); std::cout << program_log << std::endl; } glDeleteShader(vertex_shader_id); glDeleteShader(fragment_shader_id); return program_id; }
二、编写shader程序
上面在创建shader程序的时候,需要shader源码,对于图像处理来说,顶点着色器我们一般不需要用到,主要是利用片元着色器进行图像处理的相关计算。
(1)顶点着色器源码文件gl_texture.vert的代码如下:
#version 400 //顶点着色器输入 in vec2 Position; in vec2 TexCoord; //顶点着色器的输出,这个变量会进入片元着色器 out vec2 fragTexCoord; void main() { // Copy the input to the fragment shader fragTexCoord =TexCoord.xy; // Calculate the final position on screen // Note the visible portion of the screen is in <-1,1> range for x and y coordinates gl_Position = vec4(Position.x,Position.y, 0.0, 1.0); }(2)片元着色器gl_texture.frag
#version 400 // 需要注意opengl纹理输入 uniform sampler2D Texture; // 来自顶点着色器 in vec2 fragTexCoord; //输出 out vec4 FragmentColor; void main() { // Lookup the color in Texture on coordinates given by fragTexCoord vec3 pColor = texture(Texture, fragTexCoord.xy).rgb; //转换成灰度图像 float gray=pColor.r*0.2126+ 0.7152* pColor.g + 0.0722*pColor.b; FragmentColor =vec4(gray, gray,gray, 1.0); }
三、opencv读取图片、输入纹理、启用纹理等
//3、设置纹理相关参数、或者输入shader计算所需要的数据 auto texture_id = LoadImage("2.jpg", height_width, height_width);//读入一张图片,转成纹理格式,把并把图片数据拷贝到opengl纹理单元。 auto texture_attrib = glGetUniformLocation(program_id, "Texture");//找到shader程序中,变量名为Texture,类型为uniform的变量索引 glUniform1i(texture_attrib,0); glActiveTexture(GL_TEXTURE0 + 0);//启用第一个纹理,并绑定纹理数据 glBindTexture(GL_TEXTURE_2D, texture_id);
GLuint LoadImage(const std::string &image_file, unsigned int width, unsigned int height) { // Create new texture object GLuint texture_id; glGenTextures(1, &texture_id); glBindTexture(GL_TEXTURE_2D, texture_id); // Set mipmaps glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); cv::Mat texture_cv=cv::imread(image_file); if (texture_cv.empty()) { return -1; } else { glTexImage2D(GL_TEXTURE_2D, 0, 3, texture_cv.cols, texture_cv.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, texture_cv.data); return texture_id; } }
四、绘制渲染、计算
通过绘制一个矩形,该矩形的纹理刚好就是我们的图片,这样就可以实现gpu图片处理了:
//4、设置渲染相关参数(矩形4个顶点、及其对应的纹理坐标) InitializeGeometry(program_id); //5、绘制、渲染 glClearColor(0.f,0.f,0.f,1.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
InitializeGeometry函数:
void InitializeGeometry(GLuint program_id) { // Generate a vertex array object GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); // 顶点缓存 GLfloat tempv[12] = {1.0f,1.0f,-1.0f,1.0f,1.0f,-1.0f,-1.0f, -1.0f}; //二维顶点坐标,分别为矩形的四个顶点坐标 std::vector<GLfloat> vertex_buffer(tempv , tempv+12); // Generate a vertex buffer object GLuint vbo; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, vertex_buffer.size() * sizeof(GLfloat), vertex_buffer.data(), GL_STATIC_DRAW); // Setup vertex array lookup auto position_attrib = glGetAttribLocation(program_id, "Position"); glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(position_attrib); // Generate another vertex buffer object for texture coordinates GLfloat temptex[8] = {1.0f,0.0f,0.0f,0.0f,1.0f,1.0f,0.0f, 1.0f}; std::vector<GLfloat> texcoord_buffer(temptex,temptex+8); GLuint tbo; glGenBuffers(1, &tbo); glBindBuffer(GL_ARRAY_BUFFER, tbo); glBufferData(GL_ARRAY_BUFFER, texcoord_buffer.size() * sizeof(GLfloat), texcoord_buffer.data(), GL_STATIC_DRAW); auto texcoord_attrib = glGetAttribLocation(program_id, "TexCoord"); glVertexAttribPointer(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(texcoord_attrib); glBindVertexArray(vao); }
五、取出数据到opencv中
渲染完毕后,我们就要把图像处理的结果保存回opencv的cv::Mat上,然后看看处理结果
void show(int height=height_width,int width=height_width) { cv::Mat img=cv::Mat::zeros(height, width, CV_8UC3); //use fast 4-byte alignment (default anyway) if possible //glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4); //set length of one complete row in destination data (doesn't need to equal img.cols) //glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize()); glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data);//opencv存储为BGR顺序 cv::flip(img, img, 0);//需要翻转 cv::imshow("result",img); cv::waitKey(0); }
#include "stdafx.h" #include <stdlib.h> #include "include/glew.h" #include "include/GLUT.H" #include <opencv2/opencv.hpp> #include "shader.h" #define height_width 512 void show(int height=height_width,int width=height_width) { cv::Mat img=cv::Mat::zeros(height, width, CV_8UC3); //use fast 4-byte alignment (default anyway) if possible //glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4); //set length of one complete row in destination data (doesn't need to equal img.cols) //glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize()); glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data);//opencv存储为BGR顺序 cv::flip(img, img, 0);//需要翻转 cv::imshow("result",img); cv::waitKey(0); } int main( int argc, char** argv ) { //1、初始化环境 glutInit(&argc, argv); //glutInitWindowSize(512,512); //glutInitWindowPosition(100,100); glutCreateWindow("GLEW Test"); glewExperimental = GL_TRUE; glewInit(); if (!glewIsSupported("GL_VERSION_4_0")) { std::cerr << "Failed to initialize GLEW with OpenGL 4.0!" << std::endl; return EXIT_FAILURE; } //2、读取、创建shader程序,编译连接等 auto program_id = ShaderProgram("shader/gl_texture.vert", "shader/gl_texture.frag"); glUseProgram(program_id); //3、设置纹理相关参数、或者输入shader计算所需要的数据 auto texture_id = LoadImage("2.jpg", height_width, height_width);//读入一张图片,转成纹理格式,把并把图片数据拷贝到opengl纹理单元。 auto texture_attrib = glGetUniformLocation(program_id, "Texture");//找到shader程序中,变量名为Texture,类型为uniform的变量索引 glUniform1i(texture_attrib,0); glActiveTexture(GL_TEXTURE0 + 0);//启用第一个纹理,并绑定纹理数据 glBindTexture(GL_TEXTURE_2D, texture_id); //4、设置渲染相关参数(矩形4个顶点、及其对应的纹理坐标) InitializeGeometry(program_id); //5、绘制、渲染 glClearColor(0.f,0.f,0.f,1.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); //6、取回数据到opencv,并显示结果 show();// return 0; }
结果:
原图 处理结果