OpenGL进行简单的通用计算实例
博主作为OpenGL新手,最近要用OpenGL进行并行的数据计算,突然发现这样的资料还是很少的,大部分资料和参考书都是讲用OpenGL进行渲染的。好不容易找到一本书《GPGPU编程技术,从OpenGL、CUDA到OpenCL》,里面对并行处理的发展进行了系统性的介绍,还是很不错的。小白博主很兴奋,看完书中第三章后恍然大悟了很多,就贴出书中代码3-3的例子,实现一番,并用一副图片数据做了实现。
实现环境:Window7 32bit, VS2013+OpenGL3.3+GLEW+GLFW。
OpenGL用来进行通用数据计算的流程如下图,数据从CPU(应用程序)中通过“用绘制来调用”发送到纹理缓存,以纹理映射的方式给到着色器,最后经过片段着色器的计算(GLSL语言)后,再将结果输出到纹理缓存,最后CPU(应用程序)再从纹理缓存中读取结果数据,至此计算完成。
1.书中代码3-3,输入一组数据到纹理缓存,然后再从纹理缓存中读取这组数据,代码以及实验结果如下:
数据类型就设置为float,将数据发送至纹理缓存要用这个函数glTexSubImage2D( );
#include <iostream> #include <stdlib.h> #include <stdio.h> #define GLEW_STATIC #include <GL/glew.h> #include <GLFW/glfw3.h> using namespace std; #define WIDTH 2 #define HEIGHT 3 int main(int argc, char** argv) { int nWidth=(int)WIDTH; int nHeight=(int)HEIGHT; int nSize=(int)nWidth*nHeight; //创建输入数据 float* pfInput=new float[4*nSize]; float* pfOutput=new float[4*nSize]; for (int i=0; i<nSize*4; i++) { pfInput[i]= (float)(i + 1.2345); } //初始化并设置GLFW glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); //创建GLFW窗口 GLFWwindow* window = glfwCreateWindow(3, 2, "LearnOpenGL", nullptr, nullptr); glfwMakeContextCurrent(window); //初始化GLEW //glewExperimental = GL_TRUE; glewInit(); //创建FBO并绑定 GLuint fb; glGenFramebuffersEXT(1, &fb); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); //创建纹理对象并绑定 GLuint tex; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); //设置纹理参数 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); //将纹理关联到FBO glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0); //将纹理数据设置为单精度浮点数 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA_FLOAT32_ATI, nWidth, nHeight,0, GL_RGBA, GL_FLOAT, NULL); //将数据传至输入纹理缓存 //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nWidth, nHeight, 0, GL_RGBA, GL_FLOAT, pfInput); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, nWidth, nHeight, GL_RGBA, GL_FLOAT, pfInput); //从输出纹理缓存中读出数据 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); glReadPixels(0,0, nWidth, nHeight, GL_RGBA, GL_FLOAT, pfOutput); //打印并检查结果 bool bCap=true; for (int i=0; i<nSize*4; i++) { cout<<i<<":\t"<<pfInput[i]<<'\t'<<pfOutput[i]<<endl; if (pfInput[i]!=pfOutput[i]) bCap=false; } if (bCap) cout<<"Round trip complete!"<<endl; else cout<<"Round trip failed!" <<endl; delete pfInput; delete pfOutput; glDeleteFramebuffers(1, &fb); glDeleteTextures(1, &tex); system("pause"); return 0; }
2.读取一幅图像,写入纹理缓存并从纹理缓存读出,配合OpenCV使用。代码如下(原理差不多):
1 #include <iostream> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #define GLEW_STATIC 5 #include <GL/glew.h> 6 #include <GLFW/glfw3.h> 7 #include <opencv.hpp> 8 using namespace std; 9 using namespace cv; 10 #define WIDTH 2 11 #define HEIGHT 3 12 int main(int argc, char** argv) 13 { 14 //读取图像 15 Mat srcImg=imread("./lena.jpg"); 16 Mat dstImg=Mat::zeros(srcImg.size(), srcImg.type()); 17 18 int nWidth=srcImg.cols; 19 int nHeight=srcImg.rows; 20 int nSize=(int)nWidth*nHeight; 21 22 //BGR转换到RGB空间 23 cvtColor(srcImg, srcImg, CV_BGR2RGB); 24 cvtColor(dstImg, dstImg, CV_BGR2RGB); 25 26 //获取图像数据指针 27 uchar* puInput=srcImg.data; 28 uchar* puOutput=dstImg.data; 29 30 //初始化并设置GLFW 31 glfwInit(); 32 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 33 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 34 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 35 glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 36 //创建GLFW窗口 37 GLFWwindow* window = glfwCreateWindow(3, 2, "LearnOpenGL", nullptr, nullptr); 38 glfwMakeContextCurrent(window); 39 //初始化GLEW 40 glewExperimental = GL_TRUE; 41 glewInit(); 42 43 //创建FBO并绑定 44 GLuint fb; 45 glGenFramebuffersEXT(1, &fb); 46 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); 47 48 //创建纹理对象并绑定 49 GLuint tex; 50 glGenTextures(1, &tex); 51 glBindTexture(GL_TEXTURE_2D, tex); 52 //设置纹理参数 53 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 54 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 55 //将纹理关联到FBO 56 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); 57 58 //将数据传至输入纹理缓存 59 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, nWidth, nHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, puInput); 60 61 //从输出纹理缓存中读出数据 62 glReadPixels(0,0, nWidth, nHeight, GL_RGB, GL_UNSIGNED_BYTE, puOutput); 63 64 //保存输出图像 65 imwrite("./LenaIsBack.jpg", dstImg); 66 67 glDeleteFramebuffers(1, &fb); 68 glDeleteTextures(1, &tex); 69 70 system("pause"); 71 72 return 0; 73 }
数据类型要设置为unsigned_byte,将数据送至纹理缓存要用这个函数glTexImage2D();最终保存出来的结果我没有再转换到BGR空间,所以输入的Lena和输出的Lena将会下面这样(仅供参考,哈哈)~:
其实最好设置两个纹理缓存对象,一个用于输入,一个用于输出,把输出的纹理缓存绑定的FBO(帧缓冲对象)。用GLSL语言在着色器中写出需要进行计算的算法就可以实现通用数据的处理了。对了,渲染的窗口还是要建立的,这样OpenGL以为它是在进行渲染到屏幕的操作,其实我们通过帧缓冲和纹理缓冲实现的是通用数据计算的过程。
今天就到这里,我继续去看书了,每天进步一点点点点。