glBindFragDataLocation

异构计算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)初始化环境

[cpp]  view plain  copy
  1. //1、初始化环境  
  2.     glutInit(&argc, argv);  
  3.     //glutInitWindowSize(512,512);    
  4.     //glutInitWindowPosition(100,100);   
  5.     glutCreateWindow("GLEW Test");   
  6.     glewExperimental = GL_TRUE;  
  7.     glewInit();  
  8.     if (!glewIsSupported("GL_VERSION_4_0"))   
  9.     {  
  10.         std::cerr << "Failed to initialize GLEW with OpenGL 4.0!" << std::endl;  
  11.         return EXIT_FAILURE;  
  12.     }  

(2)创建链接、编译shader程序

[cpp]  view plain  copy
  1. //2、读取、创建shader程序,编译连接等  
  2.     auto program_id = ShaderProgram("shader/gl_texture.vert""shader/gl_texture.frag");  
  3.     glUseProgram(program_id);  

ShaerProgram函数的代码如下:

[cpp]  view plain  copy
  1. GLuint ShaderProgram(const std::string &vertex_shader_file, const std::string &fragment_shader_file) {  
  2.   // 创建shader程序  
  3.   auto vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);  
  4.   auto fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);  
  5.   auto result = GL_FALSE;  
  6.   auto info_length = 0;  
  7.   
  8.   // 读取shader源码  
  9.   std::ifstream vertex_shader_stream(vertex_shader_file);  
  10.   std::string vertex_shader_code((std::istreambuf_iterator<char>(vertex_shader_stream)), std::istreambuf_iterator<char>());  
  11.   
  12.   std::ifstream fragment_shader_stream(fragment_shader_file);  
  13.   std::string fragment_shader_code((std::istreambuf_iterator<char>(fragment_shader_stream)), std::istreambuf_iterator<char>());  
  14.   
  15.   // 编译顶点shader代码  
  16.   std::cout << "Compiling Vertex Shader ..." << std::endl;  
  17.   auto vertex_shader_code_ptr = vertex_shader_code.c_str();  
  18.   glShaderSource(vertex_shader_id, 1, &vertex_shader_code_ptr, NULL);  
  19.   glCompileShader(vertex_shader_id);  
  20.   
  21.   // Check vertex shader log  
  22.   glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);  
  23.   if (result == GL_FALSE) {  
  24.     glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_length);  
  25.     std::string vertex_shader_log((unsigned int)info_length, ' ');  
  26.     glGetShaderInfoLog(vertex_shader_id, info_length, NULL, &vertex_shader_log[0]);  
  27.     std::cout << vertex_shader_log << std::endl;  
  28.   }  
  29.   
  30.   // 编译片元着色器代码  
  31.   std::cout << "Compiling Fragment Shader ..." << std::endl;  
  32.   auto fragment_shader_code_ptr = fragment_shader_code.c_str();  
  33.   glShaderSource(fragment_shader_id, 1, &fragment_shader_code_ptr, NULL);  
  34.   glCompileShader(fragment_shader_id);  
  35.   
  36.   // Check fragment shader log  
  37.   glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);  
  38.   if (result == GL_FALSE) {  
  39.     glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_length);  
  40.     std::string fragment_shader_log((unsigned long)info_length, ' ');  
  41.     glGetShaderInfoLog(fragment_shader_id, info_length, NULL, &fragment_shader_log[0]);  
  42.     std::cout << fragment_shader_log << std::endl;  
  43.   }  
  44.   
  45.   // 创建链接程序  
  46.   std::cout << "Linking Shader Program ..." << std::endl;  
  47.   auto program_id = glCreateProgram();  
  48.   glAttachShader(program_id, vertex_shader_id);  
  49.   glAttachShader(program_id, fragment_shader_id);  
  50.   glBindFragDataLocation(program_id, 0, "FragmentColor");  
  51.   glLinkProgram(program_id);  
  52.   
  53.   //打印编译信息,如果编译错误,就可以看见错误信息了  
  54.   glGetProgramiv(program_id, GL_LINK_STATUS, &result);  
  55.   if (result == GL_FALSE) {  
  56.     glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_length);  
  57.     std::string program_log((unsigned long)info_length, ' ');  
  58.     glGetProgramInfoLog(program_id, info_length, NULL, &program_log[0]);  
  59.     std::cout << program_log << std::endl;  
  60.   }  
  61.   glDeleteShader(vertex_shader_id);  
  62.   glDeleteShader(fragment_shader_id);  
  63.   
  64.   return program_id;  
  65. }  

二、编写shader程序

上面在创建shader程序的时候,需要shader源码,对于图像处理来说,顶点着色器我们一般不需要用到,主要是利用片元着色器进行图像处理的相关计算。

(1)顶点着色器源码文件gl_texture.vert的代码如下:

[cpp]  view plain  copy
  1. #version 400  
  2. //顶点着色器输入  
  3. in vec2 Position;  
  4. in vec2 TexCoord;  
  5.   
  6. //顶点着色器的输出,这个变量会进入片元着色器  
  7. out vec2 fragTexCoord;  
  8.   
  9. void main() {  
  10.   // Copy the input to the fragment shader  
  11.   fragTexCoord =TexCoord.xy;  
  12.   
  13.   // Calculate the final position on screen  
  14.   // Note the visible portion of the screen is in <-1,1> range for x and y coordinates  
  15.   gl_Position = vec4(Position.x,Position.y, 0.0, 1.0);  
  16. }  
(2)片元着色器gl_texture.frag
[cpp]  view plain  copy
  1. #version 400  
  2. // 需要注意opengl纹理输入  
  3. uniform sampler2D Texture;  
  4.   
  5. // 来自顶点着色器  
  6. in vec2 fragTexCoord;  
  7. //输出   
  8. out vec4 FragmentColor;  
  9.   
  10. void main() {  
  11.   // Lookup the color in Texture on coordinates given by fragTexCoord  
  12.   vec3 pColor = texture(Texture, fragTexCoord.xy).rgb;  
  13.     //转换成灰度图像  
  14.   float gray=pColor.r*0.2126+ 0.7152* pColor.g + 0.0722*pColor.b;  
  15.   FragmentColor =vec4(gray, gray,gray, 1.0);  
  16.   
  17. }  

三、opencv读取图片、输入纹理、启用纹理等

[cpp]  view plain  copy
  1. //3、设置纹理相关参数、或者输入shader计算所需要的数据  
  2.     auto texture_id = LoadImage("2.jpg", height_width, height_width);//读入一张图片,转成纹理格式,把并把图片数据拷贝到opengl纹理单元。  
  3.     auto texture_attrib = glGetUniformLocation(program_id, "Texture");//找到shader程序中,变量名为Texture,类型为uniform的变量索引  
  4.     glUniform1i(texture_attrib,0);  
  5.     glActiveTexture(GL_TEXTURE0 + 0);//启用第一个纹理,并绑定纹理数据  
  6.     glBindTexture(GL_TEXTURE_2D, texture_id);  

LoadImage函数代码如下:

[cpp]  view plain  copy
  1. GLuint LoadImage(const std::string &image_file, unsigned int width, unsigned int height)  
  2. {  
  3.   // Create new texture object  
  4.   GLuint texture_id;  
  5.   glGenTextures(1, &texture_id);  
  6.   glBindTexture(GL_TEXTURE_2D, texture_id);  
  7.    
  8.   // Set mipmaps  
  9.   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  
  10.   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  
  11.   
  12.   cv::Mat texture_cv=cv::imread(image_file);  
  13.   if (texture_cv.empty())  
  14.   {  
  15.       return -1;  
  16.   }  
  17.   else   
  18.   {  
  19.       glTexImage2D(GL_TEXTURE_2D, 0, 3, texture_cv.cols, texture_cv.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, texture_cv.data);  
  20.       return texture_id;  
  21.   }   
  22.   
  23. }  

四、绘制渲染、计算

通过绘制一个矩形,该矩形的纹理刚好就是我们的图片,这样就可以实现gpu图片处理了:

[cpp]  view plain  copy
  1. //4、设置渲染相关参数(矩形4个顶点、及其对应的纹理坐标)  
  2.     InitializeGeometry(program_id);  
  3. //5、绘制、渲染  
  4.     glClearColor(0.f,0.f,0.f,1.f);  
  5.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  6.     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  

InitializeGeometry函数:

[cpp]  view plain  copy
  1. void InitializeGeometry(GLuint program_id)   
  2. {  
  3.   // Generate a vertex array object  
  4.   GLuint vao;  
  5.   glGenVertexArrays(1, &vao);  
  6.   glBindVertexArray(vao);  
  7.   
  8.   // 顶点缓存  
  9.   GLfloat tempv[12] = {1.0f,1.0f,-1.0f,1.0f,1.0f,-1.0f,-1.0f, -1.0f};  //二维顶点坐标,分别为矩形的四个顶点坐标  
  10.   std::vector<GLfloat> vertex_buffer(tempv , tempv+12);    
  11.   // Generate a vertex buffer object  
  12.   GLuint vbo;  
  13.   glGenBuffers(1, &vbo);  
  14.   glBindBuffer(GL_ARRAY_BUFFER, vbo);  
  15.   glBufferData(GL_ARRAY_BUFFER, vertex_buffer.size() * sizeof(GLfloat), vertex_buffer.data(), GL_STATIC_DRAW);  
  16.   
  17.   // Setup vertex array lookup  
  18.   auto position_attrib = glGetAttribLocation(program_id, "Position");  
  19.   glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, 0);  
  20.   glEnableVertexAttribArray(position_attrib);  
  21.   
  22.   // Generate another vertex buffer object for texture coordinates  
  23.   GLfloat temptex[8] = {1.0f,0.0f,0.0f,0.0f,1.0f,1.0f,0.0f, 1.0f};   
  24.   std::vector<GLfloat> texcoord_buffer(temptex,temptex+8);  
  25.   
  26.   GLuint tbo;  
  27.   glGenBuffers(1, &tbo);  
  28.   glBindBuffer(GL_ARRAY_BUFFER, tbo);  
  29.   glBufferData(GL_ARRAY_BUFFER, texcoord_buffer.size() * sizeof(GLfloat), texcoord_buffer.data(), GL_STATIC_DRAW);  
  30.   
  31.   auto texcoord_attrib = glGetAttribLocation(program_id, "TexCoord");  
  32.   glVertexAttribPointer(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 0, 0);  
  33.   glEnableVertexAttribArray(texcoord_attrib);  
  34.   
  35.   glBindVertexArray(vao);  
  36. }  

这个需要设置好顶点坐标和纹理坐标。

五、取出数据到opencv中

渲染完毕后,我们就要把图像处理的结果保存回opencv的cv::Mat上,然后看看处理结果

[cpp]  view plain  copy
  1. void show(int height=height_width,int width=height_width)  
  2. {  
  3.     cv::Mat img=cv::Mat::zeros(height, width, CV_8UC3);  
  4.   
  5.     //use fast 4-byte alignment (default anyway) if possible  
  6.     //glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4);  
  7.   
  8.     //set length of one complete row in destination data (doesn't need to equal img.cols)  
  9. //glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize());  
  10.     glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data);//opencv存储为BGR顺序  
  11.     cv::flip(img, img, 0);//需要翻转  
  12.     cv::imshow("result",img);  
  13.     cv::waitKey(0);  
  14.   
  15. }  

最后贴一下主程序流程的完整代码:
[cpp]  view plain  copy
  1. #include "stdafx.h"  
  2. #include <stdlib.h>  
  3. #include "include/glew.h"  
  4. #include "include/GLUT.H"  
  5. #include <opencv2/opencv.hpp>  
  6. #include "shader.h"  
  7. #define height_width 512  
  8.   
  9.   
  10. void show(int height=height_width,int width=height_width)  
  11. {  
  12.     cv::Mat img=cv::Mat::zeros(height, width, CV_8UC3);  
  13.   
  14.     //use fast 4-byte alignment (default anyway) if possible  
  15.     //glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4);  
  16.   
  17.     //set length of one complete row in destination data (doesn't need to equal img.cols)  
  18. //glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize());  
  19.     glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data);//opencv存储为BGR顺序  
  20.     cv::flip(img, img, 0);//需要翻转  
  21.     cv::imshow("result",img);  
  22.     cv::waitKey(0);  
  23.   
  24. }  
  25.   
  26.   
  27. int main( int argc, char** argv )  
  28. {  
  29.   
  30.   
  31. //1、初始化环境  
  32.     glutInit(&argc, argv);  
  33.     //glutInitWindowSize(512,512);    
  34.     //glutInitWindowPosition(100,100);   
  35.     glutCreateWindow("GLEW Test");   
  36.     glewExperimental = GL_TRUE;  
  37.     glewInit();  
  38.     if (!glewIsSupported("GL_VERSION_4_0"))   
  39.     {  
  40.         std::cerr << "Failed to initialize GLEW with OpenGL 4.0!" << std::endl;  
  41.         return EXIT_FAILURE;  
  42.     }  
  43. //2、读取、创建shader程序,编译连接等  
  44.     auto program_id = ShaderProgram("shader/gl_texture.vert""shader/gl_texture.frag");  
  45.     glUseProgram(program_id);  
  46.   
  47. //3、设置纹理相关参数、或者输入shader计算所需要的数据  
  48.     auto texture_id = LoadImage("2.jpg", height_width, height_width);//读入一张图片,转成纹理格式,把并把图片数据拷贝到opengl纹理单元。  
  49.     auto texture_attrib = glGetUniformLocation(program_id, "Texture");//找到shader程序中,变量名为Texture,类型为uniform的变量索引  
  50.     glUniform1i(texture_attrib,0);  
  51.     glActiveTexture(GL_TEXTURE0 + 0);//启用第一个纹理,并绑定纹理数据  
  52.     glBindTexture(GL_TEXTURE_2D, texture_id);  
  53.   
  54.   
  55. //4、设置渲染相关参数(矩形4个顶点、及其对应的纹理坐标)  
  56.     InitializeGeometry(program_id);  
  57. //5、绘制、渲染  
  58.     glClearColor(0.f,0.f,0.f,1.f);  
  59.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  60.     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  
  61. //6、取回数据到opencv,并显示结果  
  62.   
  63.     show();//  
  64.   
  65.   
  66.   
  67.   
  68.     return 0;  
  69. }  

结果:

       

原图                                                                                               处理结果

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值