GPU编程之GLSL(五)——二维离散卷积

来源:互联网 发布:oa软件收费吗 编辑:程序博客网 时间:2024/06/05 07:47

 

本程序共分为7个文件:

1个主程序文件,2个CTimer文件,2个CReader文件,2个着色器文件

 

两个CReader文件和2个着色器文件分别在前边的(二)和(四)中给出,这里就不详细解释了

 

首先我们来讲一下比较简单的文件——CTimer文件:

主要是为了获取时间,以便比较程序性能

这是CTimer.h文件

 

#ifndef TIMER_H_#define TIMER_H_#include"windows.h"#include<stdlib.h>class CTimer{public:CTimer(void){init();};long getTime(void);void reset(void);private:SYSTEMTIME time;long _lStart;long _lStop;void init(void);};#endif /*TIMER_H_*/

 

这是CTimer.cpp文件:

#include "CTimer.h"void CTimer::init(void){_lStart = 0;_lStop = 0;GetSystemTime(&time);_lStart=(time.wSecond*1000)+time.wMilliseconds;}long CTimer::getTime(void){GetSystemTime(&time);_lStop=(time.wSecond*1000)+time.wMilliseconds -_lStart;return _lStop;}void CTimer::reset(void){init();}


 

接下来是主程序文件:

共分为6大部分:初始化,准备纹理缓存,配置GLSL,绘制矩形,计时,读回结果

1、初始化:包含对glut、GLEW、FBO的初始化

2、准备纹理缓存:定义两块纹理,一块保存输入一块保存输出,简单生成数据传入输入数据的纹理缓存中

3、配置GLSL:建立一个程序对象,一个着色器对象,贾周着色器程序文件,编译着色器对象以及添加、链接、启用程序对象,设置uniform变量

4、绘制矩形:输出缓存yTexID与FBO关联(计算的结果需要写入保存输出数据的纹理缓存),激活已经保存有输入数据的纹理单元,设置显然对象,使矩形中的每一个像素都会被覆盖到,保证矩形与纹理图同一尺寸

5、计时:glFinish()可以起到线程同步的作用,该函数是控制进入等待状态,直到所有调用的OpenGL命令都执行完成,它才返回。在计时器开始和结束的时候都要调用该函数以保证线程同步

6、读回结果:将结果传回pfOutput缓存,最后完成清理工作

 

#include <stdio.h>#include <stdlib.h>#include <iostream>#include <gl/glew.h>#include <gl/glut.h>#include "CReader.h"#include "CTimer.h"#pragma comment(lib, "glew32.lib")#define WIDTH        1024  //数据块的宽度#define HEIGHT       1024  //数据块的长度 #define MASK_RADIUS  2     //掩膜半径using namespace std;void initGLSL(void);void initFBO(unsigned unWidth,unsigned unHeight);void initGLUT(int argc,char** argv);void createTextures(void);void setupTexture(const GLuint texID);void performComputation(void);void transferFromTexture(float* data);void transferToTexture(float* data,GLuint texID);//纹理标识符GLuint yTexID;GLuint xTexID;//GLSL 变量                                          配置GLSL(一)GLuint glslProgram;GLuint fragmentShader;GLuint outParam,inParam,radiusParam;//FBO标识符GLuint fb;//“屏幕外窗口”的标识符,创建一个有效的OpenGL环境GLuint glutWindowHandle;//为OpenGL纹理准备的结构体,包含了纹理格式、内部格式等struct structTextureParameters{GLenum texTarget;              //纹理类型GLenum texInternalFormat;      //内部格式GLenum texFormat;              //纹理格式char* shader_source;           //着色器源文件}textureParameters;//全局变量float* pfInput;                     //输入数据float fRadius = (float)MASK_RADIUS;unsigned unWidth = (unsigned)WIDTH;unsigned unHeight = (unsigned)HEIGHT;unsigned unSize = unWidth * unHeight;int main(int argc, char **argv){//生成输入数据unsigned unNoData = 4 * unSize;   //数据总数pfInput = new float[unNoData];float* pfOutput = new float[unNoData];for(unsigned i = 0;i < unNoData;i++) pfInput[i] = i;//确定纹理参数textureParameters.texTarget         = GL_TEXTURE_RECTANGLE_ARB;textureParameters.texInternalFormat = GL_RGBA32F_ARB;textureParameters.texFormat         = GL_RGBA;//初始化GLUT和GLEWinitGLUT(argc,argv);glewInit();//初始化FBOinitFBO(unWidth, unHeight);//为输入、输出数据创建纹理缓存createTextures();//初始化CReaderCReader reader;//安全起见,先清除输入纹理缓存                配置GLSL(二)textureParameters.shader_source = reader.textFileRead("clean.frag");initGLSL();performComputation();//计时//计算二维离散卷积textureParameters.shader_source = reader.textFileRead("convolution.frag");initGLSL();performComputation();//读回计算结果transferFromTexture (pfOutput);//清理工作glDetachShader(glslProgram, fragmentShader);glDeleteShader(fragmentShader);glDeleteProgram(glslProgram);glDeleteFramebuffersEXT(1,&fb);glDeleteTextures(1,&yTexID);glDeleteTextures(1,&xTexID);glutDestroyWindow(glutWindowHandle);//退出delete pfInput;delete pfOutput;return EXIT_SUCCESS;}//初始化GLUT,创建的窗口是为有一个有效的OpenGL环境void initGLUT(int argc,char **argv){glutInit(&argc,argv);glutWindowHandle = glutCreateWindow("GPGPU Tutorial");}//屏幕外渲染void initFBO(unsigned unWidth,unsigned unHeight){//创建FBO,准备屏幕外帧缓存glGenFramebuffersEXT(1,&fb);//绑定屏幕外帧缓存,即避开了窗口系统默认的渲染目标glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);//设置一个1:1等大的纹理元——像素映射glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(0.0,unWidth,0.0,unHeight);glMatrixMode(GL_MODELVIEW);glLoadIdentity();glViewport(0,0,unWidth,unHeight);}//初始化GLSL运行时组件,并创建着色器                     配置GLSL(三)void initGLSL(void)                                     {//建立程序对象glslProgram = glCreateProgram();//建立片段着色器对象fragmentShader = glCreateShader(GL_FRAGMENT_SHADER_ARB);//为着色器设置着色器程序文件const GLchar* source = textureParameters.shader_source;glShaderSource(fragmentShader, 1, &source, NULL);//编译着色器                              glCompileShader(fragmentShader);//把着色器与程序关联glAttachShader(glslProgram,fragmentShader);//链接到完整的程序,这里使用了默认功能的顶点着色器,用户也可以使用自定义的流经顶点着色器glLinkProgram(glslProgram);//获得uniform变量的位置radiusParam = glGetUniformLocation(glslProgram, "fRadius");}void createTextures(void){//创建纹理,y保存输出数据,x保存输入数据glGenTextures(1, &yTexID);glGenTextures(1, &xTexID);//配置纹理setupTexture (yTexID);setupTexture (xTexID);transferToTexture(pfInput, xTexID);//设定映射参数  将纹理映射参数GL_MODULATE更改为GL_REPLACE//GL_MODULATE代表:把纹理元素的颜色乘以几何图元(进行光照计算之后)的颜色。//GL_REPLACE 代表:简单地覆盖掉纹理下面的结合图形的颜色。这样片段的颜色值将直接采用纹理的颜色。glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_REPLACE);}void setupTexture(const GLuint texID){//激活并绑定将要设置的纹理glBindTexture(textureParameters.texTarget,texID);//关闭滤波算法和边界以外的重复算法//使用GL_NEAREST的原因:将差值得到的纹理坐标和像素对齐,避免了因差值误差引起的误访问//使用GL_CLAMP  的原因:矩阵的边界元素在绘制的矩形尺寸大于纹理图时的处理方式                       //降低错误的发生并尽可能为发现错误提供方便glTexParameteri(textureParameters.texTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST);                   glTexParameteri(textureParameters.texTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST);glTexParameteri(textureParameters.texTarget,GL_TEXTURE_WRAP_S,GL_CLAMP);glTexParameteri(textureParameters.texTarget,GL_TEXTURE_WRAP_T,GL_CLAMP);//定义纹理的数据类型为floatglTexImage2D(textureParameters.texTarget,0,textureParameters.texInternalFormat,         unWidth,unHeight,0,textureParameters.texFormat,GL_FLOAT,0);}void performComputation(void){//关联输出缓存yTexID与FBOglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,                      textureParameters.texTarget,yTexID,0);//将glslProgram设为当前程序对象glUseProgram(glslProgram);//将GL_TEXTURE0设为当前纹理单元glActiveTexture(GL_TEXTURE0);//更新uniform变量,将应用程序的fRadius的值传递至着色器内部glUniform1f(radiusParam,fRadius);//同步线程,以便计时glFinish();//计时开始CTimer timer;long lTime = 0;timer.reset();//将设置写入纹理缓存的类型glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);//将计算范围定义为同样包括矩形的内部glPolygonMode(GL_FRONT,GL_FILL);//用未归一化的纹理坐标设定计算范围glBegin(GL_QUADS);glTexCoord2f(0.0,0.0);glVertex2f(0.0,0.0);glTexCoord2f(unWidth,0.0);glVertex2f(unWidth,0.0);glTexCoord2f(unWidth,unHeight);glVertex2f(unWidth,unHeight);glTexCoord2f(0.0,unHeight);glVertex2f(0.0,unHeight);glEnd();//同步线程,终止计时glFinish();lTime = timer.getTime();cout<<"Time elapsed: "<<lTime<<" ms."<<endl;}//将数据从当前的纹理缓存传至主存储器void transferFromTexture(float* data){glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);glReadPixels(0,0,unWidth,unHeight,textureParameters.texFormat,GL_FLOAT,data);}//将数据传送至纹理缓存,请注意在使用硬件加速时,ATI和NVIDIA显卡的区别void transferToTexture(float* data, GLuint texID){glBindTexture(textureParameters.texTarget,texID);glTexSubImage2D(textureParameters.texTarget,0,0,0,unWidth,unHeight,            textureParameters.texFormat,GL_FLOAT,data);}


 

 

参考文献:

仇德元.《GPGPU编程技术——从GLSL、CDPU到OpenGL》[M].河北省三河市:机械工业出版社,2012年:323.

 

 

 

 

 

 

 

 

 

 

0 0
原创粉丝点击