博主作為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以為它是在進行渲染到屏幕的操作,其實我們通過幀緩沖和紋理緩沖實現的是通用數據計算的過程。
今天就到這里,我繼續去看書了,每天進步一點點點點。