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

 

本程序共分为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和GLEW
	initGLUT(argc,argv);
	glewInit();

	//初始化FBO
	initFBO(unWidth, unHeight);

	//为输入、输出数据创建纹理缓存
	createTextures();

	//初始化CReader
	CReader 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);

	//定义纹理的数据类型为float
	glTexImage2D(textureParameters.texTarget,0,textureParameters.texInternalFormat,
		         unWidth,unHeight,0,textureParameters.texFormat,GL_FLOAT,0);

}

void performComputation(void)
{
	//关联输出缓存yTexID与FBO
	glFramebufferTexture2DEXT(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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值