OpenGL学习笔记(十六)缓冲区(一)像素缓冲区

本文为学习OpenGL的学习笔记,如有书写和理解错误还请大佬扶正;

一,缓冲区概念

缓冲区对象,允许应用程序迅速方便地将数据从一个渲染管线移动到另一个渲染管线,以及从一个对象绑定到另一个对象,不仅可以把数据移动到合适的位置,还可以在无需CPU介入的情况下完成这项工作。

缓冲区有很多不同的用途,它们能够保存顶点数据,像素数据,纹理数据,着色器处理输入,或者不同着色器阶段的输出。

缓冲区保存在GPU内存中,它们提供高速和高效的访问,但在GPU中更新数据常常需要重新加载整个对象,在系统内存和GPU内存之间来回移动数据可能是一个缓慢的过程。

二,使用缓冲区

1,创建缓冲区

创建新的缓冲区,只需要调用glGenBuffers函数,来为我们所需任何数量的新缓冲区创建名称,实际缓冲区对象将在第一次使用时创建。

GLuint pixBufferObjs[1];
glGenBuffers(1,pixBufferObjs);

2,绑定缓冲区

一旦有了新缓冲区的名称,就能使用这个名称进行绑定来使用缓冲区了。

在OpenGL中有许多不同的绑定点,绑定点上为了不同目的而使用某个缓冲区,下图列出绑定点及其功能描述;

OpenGL学习笔记(十六)缓冲区(一)像素缓冲区_第1张图片

使用绑定点缓冲区,可以拿绑定点作为参数调用glBindBuffer函数,之后使用函数glReadPixels将像素数据复制到缓冲区中;

例如 :绑定到像素包装缓冲区

glBindBuffer(GL_PIXEL_PACK_BUFFER,pixBuffObjs[0]);

当使用完一个缓冲区后,这个缓冲区需要进行清除,通过调用函数glDeleteBuffer来删除它;

glDeleteBuffers(1,pixBuffObjs);

3,填充缓冲区

可以使用glBufferData函数来简单地将数据直接上传到任何类型的缓冲区中。

例如:填充到像素包装缓冲区

glBindBuffer(GL_PIXEL_PACK_BUFFER,pixBufferObjs[0]);
glBufferData(GL_PIXEL_PACK_BUFFER,pixelDataSize,pixelData,GL_DYNAMIC_COPY);
glBindBuffer(GL_PIXEL_PACK_BUFFER,0);

在调用glBufferData之前,必须将使用的缓冲区进行绑定,对glBufferData使用的目标与绑定的目标相同,第二个参数上上传数据的大小,以字节为单位;第三个参数是要上传数据的本身,如果想要分配一个特定大小的缓冲区却不需要立即对它进行填充,那么这个指针也可以说NULL,第四个参数告诉OpenGL如何使用缓冲区。

缓冲区使用方式:

OpenGL学习笔记(十六)缓冲区(一)像素缓冲区_第2张图片

OpenGL学习笔记(十六)缓冲区(一)像素缓冲区_第3张图片

在不确定缓冲区用途时,对于通常的缓冲区使用方式或条件来说,使用GL_DYNAMIC_DRAW是一个比较安全的值,可以重复调用glBufferData对缓冲区进行填充,还可以改变使用方式,但是重复调用以后,缓冲区中原来的数据会被删除,可以使用函数glBufferSubData对已经存在的缓冲区中的一部分进行更新,而不会导致缓冲区其他部分的内容变为无效。

void glBufferSubData(GLenum target,intptr offset,sizeiptr size,const void*data);

参数与glBufferData函数类似,其中不同的 offset参数 指定从开头起 计算偏移到哪个位置开始更新数据,因为内存已经被分配,所以不能改变缓冲使用方式;

三,像素缓冲区

和所有缓冲区一样像素缓冲区都储存在GPU内存中,可以访问和填充像素缓冲区(缩写PBO),实际上,只有在绑定到一个PBO缓冲区绑定点时,一个缓冲区才真正成为一个像素缓冲区对象;

第一个像素缓冲区对象的绑定点是GL_PIXEL_PACK_BUFFER,当一个像素缓冲区对象被绑定到这个目标上时,任何读取像素的OpenGL操作都会从像素缓冲区对象中获得他们的数据,这些操作包括glReadPixel,glGetTexImage和glGetCompressedTexImage;

  • 如果不绑定PBO时:通常这些操作会从一个帧缓冲区或纹理缓冲区中抽取数据,并将它们读回到客户端内存中;
  • 当绑定PBO时:一个像素缓冲区对象被绑定到包装缓冲区时,像素数据在GPU内存中的像素缓冲区对象中的任务就结束了,而不会下载到客户端;

第二个PBO绑定点上GL_PIXEL_UNPACK_BUFFER,当一个像素缓冲区对象被绑定到这个目标上时,任何绘制像素的OpenGL操作都会向一个绑定的像素缓冲区对象中放入它们的数据。

glTexImage*D,glTexSubImage*D,glCompressedTexImage*D和glCompressedTexSubImage*D就是这样的操作,

  • 如果不绑定PBO时:这些操作将数据从本地CPU内存中读取到帧缓冲区中;
  • 当绑定PBO时:但是当一个像素缓冲区对象作为解包缓冲区被绑定时,会读取操作成为GPU内存中而不是CPU内存中的PBO;

像素缓冲区是一个很好地容器,可以暂时储存GPU本地像素数据,而不用下载在上传,但使用之前需要先为它们分配储存空间,如果没有必要的数据需要填充,可以为数据指针传递NULL简单分配内存而不进行填充。

像素缓冲区经常用来储存来自一个渲染目标的2D图像,纹理或其他数据源,但是缓冲区对象本身是一个一维的,本质上没有宽度或高度,在为2D图像分配储存空间时,可以只是将宽度与高度相乘,再与像素大小相乘来计算所需内存;实际上如果计划为多种数据大小使用相同的PBO,最好关闭对数据大小上限的设定,而不是频繁的对它进行重新设定。

所有对glBufferData的调用都和其他的绘制调用一起通过管线进行处理,意味着OpenGL不需要等待所有活动停止,就可以将新数据发送到GPU。

从缓冲区中读取像素数据

主要作用,可以检查实际的渲染情况,以便确定接下来在屏幕上如何进行渲染,另一个可以应用到后续帧的效果中使用前面帧的像素。使用函数glReadPixel函数读取缓冲区的特定像素,然后将它们复制到CPU内存中。

void* data = (void*)malloc(pixelDataSize);
glReadBuffer(GL_Back_LEFT);
glReadPixels(0,0,GetWidth(),GetHeight(),GL_RGB,GL_UNSIGNED_BYTE,pixelData);

当我们向客户端内存进行写入时,整个管线经常需要被清空,以保证所有影响我们将要进行读取的绘制工作能够完成,这对于应用程序性能可能会冲击很大,所以我们使用缓冲区域对象来解决这个问题,在调用glReadPixels并将glReadPixels调用中的数据指针设为1之前,我们可以将一个缓冲区对象绑定到GL_PIXEL_PACK_BUFFER上,这样就能够将像素重定向到GPU中的一个缓冲区中,并且避免了复制到客户端内存可能带来的性能问题。

glReadBuffer(GL_BACK_LEFT);
glBindBuffer(GL_PIXEL_PACK_BUFFER,pixBufferObjs[0]);
glReadPixels(0,0,GetWidth(),GetHeight,GL_RGB,GL_UNSIGNED_BYTE,NULL);

PBO优点

参考:

OpenGL Pixel Buffer Object (PBO)​

www.songho.ca/opengl/gl_pbo.html正在上传…重新上传取消

PBO的主要优点是可以通过DMA(直接内存访问)在图形卡之间快速传输像素数据,而不会占用CPU周期。而且,PBO的另一个优点是异步DMA传输。让我们比较一下使用像素缓冲区对象的常规纹理传输方法。下图的左侧是从图像源(图像文件或视频流)加载纹理数据的常规方法。首先将源加载到系统内存中,然后使用glTexImage2D()将其从系统内存复制到OpenGL纹理对象。这两个传输过程(加载和复制)均由CPU执行。

OpenGL学习笔记(十六)缓冲区(一)像素缓冲区_第4张图片

相反,在右侧图中,图像源可以直接加载到由OpenGL控制的PBO中。CPU仍涉及将源加载到PBO,但不涉及将像素数据从PBO传输到纹理对象。相反,GPU(OpenGL驱动程序)管理将数据从PBO复制到纹理对象。这意味着OpenGL执行DMA传输操作时不会浪费CPU周期。此外,OpenGL可以安排异步DMA传输以供以后执行。因此,glTexImage2D()立即返回,CPU可以执行其他操作,而无需等待像素传输完成。

四,使用像素缓冲区(PBO)

利用PBO实现运动模糊特效,实现原理,将前面的帧的结果进行储存并于当前帧混合到一起,为了创建可视的运动模糊效果,程序将会储存最后五帧的图像,区别于传统的方法需要将数据复制到CPU中在复制出来,我们使用PBO的方式,无需从CPU回读数据;

效果预览:

OpenGL学习笔记(十六)缓冲区(一)像素缓冲区_第5张图片

代码参考:

Render.CPP

#include                            //GLEW库引入(一定要在GLUT引入之前引入,否则会编译错误),如果要引入其他OpenGL头文件,必须要注意将这个头文件放在前面

#include 
#include            //着色器管理类
#include             //矩阵堆栈
#include                  //矩阵
#include               //投影矩阵
#include                //三角形批次类
#include   //几何变换管道
#include            //时间管理
#include 

#include                      //数学库  
#include 
using namespace std;

//矩阵存储在堆区,而地址是存储在栈区,在大量进行变换的应用的场景中,就需要顶点与大量的变换矩阵进行相乘,这时候就需要大量的构造矩阵,这时候有一个便利的矩阵构造函数可以进行构造矩阵操作矩阵乘法会方便很多,在math3d的这个类被称为GLMatrixsStack。
//使用矩阵堆栈进行矩阵的创建和操作矩阵乘法很方便,但是我们还要方便的管理这些堆栈,就是说我们可以随时方便取到堆栈矩阵的地址,GLGeometryTransform可以设置指针指向我们创建好的堆栈矩阵。

//设置灯光 着色器参数
static GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };      //花托物体颜色
static GLfloat vWhite[] = { 1.0f, 1.0f, 1.0f, 1.0f };     //地板颜色
static GLfloat vLightPos[] = { 0.0f, 3.0f, 0.0f, 1.0f }; //灯光位置

//屏幕尺寸
GLsizei	 screenWidth;			
GLsizei  screenHeight;			

//GLTool类 着色器 矩阵
GLShaderManager		shaderManager;		//着色器管理	
GLFrame				cameraFrame;        //GLFrame是拿来做变化用的。可以用来产生模型视图矩阵。来产生位置的移动 GetCameraMatrix是GLFrame的一个函数,我们通常会用这个来进行设置,我们可以通过此函数来获取一个观察者变换过后的矩阵 视觉坐标系
GLMatrixStack		modelViewMatrix;    
GLMatrixStack		projectionMatrix;		
M3DMatrix44f        orthoMatrix;
GLFrustum			viewFrustum;	    //创建一个透视投影的矩阵		
GLGeometryTransform	transformPipeline;	//矩阵管理器	
			

GLTriangleBatch		torusBatch;//花托
GLBatch				floorBatch;//地板
GLBatch             screenQuad;//四边形 用于后期渲染使用的四边形

GLuint				textures[1];  //贴图ID 
GLuint				blurTextures[6]; //模糊贴图0-5
GLuint				pixBuffObjs[1];  //PBO ID
GLuint				curBlurTarget;     //模糊贴图的Index
GLfloat				speedFactor;      //移动速度因子
GLuint				blurProg;        //模糊着色器ID
void				*pixelData;     //内存
GLuint				pixelDataSize; //尺寸

//glReadPixels()和glGetTexImage()是“打包”像素操作,而glDrawPixels(),glTexImage2D()和glTexSubImage2D()是“拆包”操作。
//当PBO与GL_PIXEL_PACK_BUFFER令牌绑定时,glReadPixels()从OpenGL帧缓冲区读取像素数据,并将数据写入(打包)到PBO中。
//当PBO与GL_PIXEL_UNPACK_BUFFER令牌绑定时,glDrawPixels()从PBO读取(解压缩)像素数据并将其复制到OpenGL帧缓冲区。

void DrawWorld(GLfloat yRot, GLfloat xPos); //绘制模型函数
bool LoadBMPTexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode); //导入BMP贴图

void SetupBlurProg(void); //设置模糊使用的贴图纹理

//这个方法会在RenderScene方法中被调用,要为每一帧去设置激活一个纹理做准备
void AdvanceBlurTaget() { curBlurTarget = ((curBlurTarget + 1) % 6); }
//下面的这些方法会在SetupBlurProg方法中被调用,拿来传入纹理采样器的值
GLuint GetBlurTarget0() { return (1 + ((curBlurTarget + 5) % 6)); }
GLuint GetBlurTarget1() { return (1 + ((curBlurTarget + 4) % 6)); }
GLuint GetBlurTarget2() { return (1 + ((curBlurTarget + 3) % 6)); }
GLuint GetBlurTarget3() { return (1 + ((curBlurTarget + 2) % 6)); }
GLuint GetBlurTarget4() { return (1 + ((curBlurTarget + 1) % 6)); }
GLuint GetBlurTarget5() { return (1 + ((curBlurTarget) % 6)); }

// 将BMP文件作为纹理载入。指定过滤和环绕模式。
bool LoadBMPTexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
	GLbyte *pBits;
	GLint iWidth, iHeight;
	//读取BMP图像
	//如果pBits == NULL的话,就返回false
	pBits = gltReadBMPBits(szFileName, &iWidth, &iHeight);
	if (pBits == NULL)
		return false;

	//设置纹理参数
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);

	//设置纹理参数
	if (minFilter == GL_LINEAR_MIPMAP_LINEAR || minFilter == GL_LINEAR_MIPMAP_NEAREST || minFilter == GL_NEAREST_MIPMAP_LINEAR || minFilter == GL_NEAREST_MIPMAP_NEAREST)
		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
	//设置过滤参数
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
	//加载纹理数据
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, iWidth, iHeight, 0, GL_BGR, GL_UNSIGNED_BYTE, pBits);
	return true;
}



// OpenGL相关的启动代码放在这里,设置环境,加载纹理等。
void SetupRC(void)
{
	//检测初始化过程是否有问题,并打印错误信息
	GLenum err = glewInit();
	if (GLEW_OK != err)
	{
		
		fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
	}

	// 初始化着色器
	shaderManager.InitializeStockShaders();
	glEnable(GL_DEPTH_TEST);

	// 背景颜色
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	//设置花托的大小
	gltMakeTorus(torusBatch, 0.4f, 0.15f, 35, 35);

	//设置地板顶点属性 颜色 UV 法线  顶点位置
	GLfloat alpha = 0.25f;
	floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
	floorBatch.Color4f(0.0f, 1.0f, 0.0f, alpha);
	floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
	floorBatch.Normal3f(0.0, 1.0f, 0.0f);
	floorBatch.Vertex3f(-20.0f, -0.41f, 20.0f);

	floorBatch.Color4f(0.0f, 1.0f, 0.0f, alpha);
	floorBatch.MultiTexCoord2f(0, 10.0f, 0.0f);
	floorBatch.Normal3f(0.0, 1.0f, 0.0f);
	floorBatch.Vertex3f(20.0f, -0.41f, 20.0f);

	floorBatch.Color4f(0.0f, 1.0f, 0.0f, alpha);
	floorBatch.MultiTexCoord2f(0, 10.0f, 10.0f);
	floorBatch.Normal3f(0.0, 1.0f, 0.0f);
	floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);

	floorBatch.Color4f(0.0f, 1.0f, 0.0f, alpha);
	floorBatch.MultiTexCoord2f(0, 0.0f, 10.0f);
	floorBatch.Normal3f(0.0, 1.0f, 0.0f);
	floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
	floorBatch.End();
	//绑定贴图到纹理单元0 后续使用
	glGenTextures(1, textures);
	glBindTexture(GL_TEXTURE_2D, textures[0]);
	LoadBMPTexture("marble.bmp", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);

	// 模糊着色器
	blurProg = gltLoadShaderPairWithAttributes("ShaderPBOBlurVS.vs", "ShaderPBOBlurFS.fs", 2,
		GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "texCoord0");

	// 生成模糊贴图ID
	glGenTextures(6, blurTextures);

	//计算像素数据的大小
	pixelDataSize = screenWidth * screenHeight * 3 * sizeof(unsigned int); // XXX This should be unsigned byte
	void* data = (void*)malloc(pixelDataSize); //申请空间
	memset(data, 0x00, pixelDataSize);//将data中的pixelDataSize字节全部设置为0x00

	// 激活纹理单元
	// 初始化纹理数据
	for (int i = 0; i < 6; i++)
	{
		glActiveTexture(GL_TEXTURE1 + i);
		glBindTexture(GL_TEXTURE_2D, blurTextures[i]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screenWidth, screenHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
	}

	//生成像素缓冲区
	glGenBuffers(1, pixBuffObjs);
	glBindBuffer(GL_PIXEL_PACK_BUFFER, pixBuffObjs[0]);
	glBufferData(GL_PIXEL_PACK_BUFFER, pixelDataSize, pixelData, GL_DYNAMIC_COPY);
	glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

	//根据一个基于宽度和高度的正模型视图投影矩阵进行设置一个批次类
	gltGenerateOrtho2DMat(screenWidth, screenHeight, orthoMatrix, screenQuad);

	//确认运行无误
	gltCheckErrors();
}

// 清理纹理和缓冲对象
void ShutdownRC(void)
{

	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
	glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);

	// 清除
	for (int i = 0; i < 7; i++)
	{
		glActiveTexture(GL_TEXTURE0 + i);
		//将2D纹理绑定到默认的纹理,一般用于打破之前的纹理绑定关系,使OpenGL的纹理绑定状态恢复到默认状态。
		glBindTexture(GL_TEXTURE_2D, 0);
	}

	//删除纹理
	glDeleteTextures(1, textures);
	glDeleteTextures(6, blurTextures);

	// delete PBO 删除pbo
	glDeleteBuffers(1, pixBuffObjs);
}


//当窗口大小发生改变时,渲染视口也相应做出调整
void ChangeSize(int nWidth, int nHeight)
{
	//设置渲染视口
	glViewport(0, 0, nWidth, nHeight);
	
	//设置投影
	viewFrustum.SetPerspective(35.0f, float(nWidth) / float(nHeight), 1.0f, 100.0f);
	//加载投影矩阵
	projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
	//设置模型视图矩阵和投影矩阵给管道类进去管理
	transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
	

	// 更新屏幕尺寸
	screenWidth = nWidth;
	screenHeight = nHeight;

	// 重新去根据宽度和高度设置一个基于窗口宽度和高度正模型视图投影矩阵进行设置
	gltGenerateOrtho2DMat(screenWidth, screenHeight, orthoMatrix, screenQuad);
	//释放像素数据
	free(pixelData);
	//设置宽度和高度乘像素点所占据的字节
	pixelDataSize = screenWidth * screenHeight * 3 * sizeof(unsigned int);
	//分配空间
	pixelData = (void*)malloc(pixelDataSize);


	//绑定像素缓冲区
	glBindBuffer(GL_PIXEL_PACK_BUFFER, pixBuffObjs[0]);
	//绑定数据
	glBufferData(GL_PIXEL_PACK_BUFFER, pixelDataSize, pixelData, GL_DYNAMIC_COPY);
	//解绑缓冲区,调用以0为缓冲区
	glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
	//检查错误
	gltCheckErrors();
}


//设置模糊着色器 纹理参数
void SetupBlurProg(void)
{
	// 使用着色器程序
	glUseProgram(blurProg);

	// 传入MVP矩阵
	glUniformMatrix4fv(glGetUniformLocation(blurProg, "mvpMatrix"), 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());

	// 为模糊目标设置纹理单元
	glUniform1i(glGetUniformLocation(blurProg, "textureUnit0"), GetBlurTarget0());
	glUniform1i(glGetUniformLocation(blurProg, "textureUnit1"), GetBlurTarget1());
	glUniform1i(glGetUniformLocation(blurProg, "textureUnit2"), GetBlurTarget2());
	glUniform1i(glGetUniformLocation(blurProg, "textureUnit3"), GetBlurTarget3());
	glUniform1i(glGetUniformLocation(blurProg, "textureUnit4"), GetBlurTarget4());
	glUniform1i(glGetUniformLocation(blurProg, "textureUnit5"), GetBlurTarget5());
}


//绘制花托 应用运动模糊的物体  传入移动速度与方向
void DrawWorld(GLfloat yRot, GLfloat xPos)
{
	//获取模型视图矩阵,并将它储存在变量mCamera中
	M3DMatrix44f mCamera;
	modelViewMatrix.GetMatrix(mCamera);

	// 转置灯光到模型视图矩阵下 转置之后的位置储存在vLightTransformed内
	M3DVector4f vLightTransformed;
	m3dTransformVector4(vLightTransformed, vLightPos, mCamera);

	// 绘制花托
	modelViewMatrix.PushMatrix();
	modelViewMatrix.Translate(0.0f, 0.2f, -2.5f);  //设置位置
	modelViewMatrix.Translate(xPos, 0.0f, 0.0f);    //设置左右移动距离
	modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);  //设置旋转
	//使用着色器程序点光源着色器
	shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,modelViewMatrix.GetMatrix(),transformPipeline.GetProjectionMatrix(),vLightTransformed, vGreen, 0);
	torusBatch.Draw(); //绘制
	modelViewMatrix.PopMatrix();
}


//主渲染函数
void RenderScene(void)
{
	static CStopWatch animationTimer;//时间
	static float moveDistance = 1.5; //物体移动的距离 
	
	float seconds = animationTimer.GetElapsedSeconds() * speedFactor; //获取运行时间 乘速度因子 决定物体移动速度
	float xPos = 0;//移动位置

	xPos = sin(seconds)*moveDistance; //物体实际移动 (-1,1)*1.5  [-1.5,1.5]

	
	//绘制地面
	modelViewMatrix.PushMatrix();
	//激活纹理单元 
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, textures[0]); 
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	//默认的贴图着色器
	shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE, transformPipeline.GetModelViewProjectionMatrix(), vWhite, 0);
	floorBatch.Draw(); //绘制地面
	//绘制移动 花托物体 
	DrawWorld(xPos*120.0f, xPos);
	modelViewMatrix.PopMatrix();

	//使用PBO
	// 绑定纹理
	glBindBuffer(GL_PIXEL_PACK_BUFFER, pixBuffObjs[0]);
	//从帧缓冲区读取像素数据返回到GPU当中的PBO对象中
	glReadPixels(0, 0, screenWidth, screenHeight, GL_RGB, GL_UNSIGNED_BYTE, NULL);
	glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);//解绑缓冲区

	//绑定为解包缓冲区,直接将像素读取存放到纹理当中
	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixBuffObjs[0]);

	//为新的模糊设置纹理单元,这将在每一帧中不断增加。。
	glActiveTexture(GL_TEXTURE0 + GetBlurTarget0());
	//如果当一个像素缓冲区绑作为解包缓冲区的时候,就会让glTexture2D这个函数原本是从CPU内存读取到帧缓冲区的改为从GPU内存读取到帧缓冲区
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, screenWidth, screenHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);//解绑对象
	
	// 使用模糊着色器和所有模糊纹理绘制全屏四边形。
	projectionMatrix.PushMatrix();
	projectionMatrix.LoadIdentity();
	projectionMatrix.LoadMatrix(orthoMatrix);  //加载正投影矩阵
	modelViewMatrix.PushMatrix();
	modelViewMatrix.LoadIdentity();
	glDisable(GL_DEPTH_TEST);//关闭深度测试
	SetupBlurProg();//使用着色器
	screenQuad.Draw();//绘制屏幕后期模糊效果
	glEnable(GL_DEPTH_TEST);  //开启深度测试
	modelViewMatrix.PopMatrix();
	projectionMatrix.PopMatrix();

	// 移动到下一帧的下一个模糊纹理。
	AdvanceBlurTaget();

	// 交换缓冲区
	glutSwapBuffers();

	// 循环执行
	glutPostRedisplay();
}

//主函数 
int main(int argc, char* argv[])
{
	//基础属性设置 屏幕的宽高 运动的速度 是否使用PBO
	screenWidth = 1024;
	screenHeight = 720;
	speedFactor = 1.0f;  //速度因子

	//初始化
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);  //缓冲区设置
	glutInitWindowSize(screenWidth, screenHeight);   //窗口尺寸
	//窗口名称 
	glutCreateWindow("MoveBlur");

	//设置渲染环境
	SetupRC();

	//窗口改变时 回调函数
	glutReshapeFunc(ChangeSize);
	//渲染
	glutDisplayFunc(RenderScene);
	
	//循环
	glutMainLoop();
	//清理
	ShutdownRC();
	return 0;
}

ShaderPBOBlurVS.vs

#version 330 
 
uniform mat4 mvpMatrix;
in vec3 vVertex;
in vec2 texCoord0;
out vec2 vTexCoord;

void main(void)
{
    vTexCoord = texCoord0;
    gl_Position = mvpMatrix * vec4(vVertex, 1.0);
}

ShaderPBOBlurFS.fs

#version 330 

in vec2 vTexCoord;
uniform sampler2D textureUnit0;
uniform sampler2D textureUnit1;
uniform sampler2D textureUnit2;
uniform sampler2D textureUnit3;
uniform sampler2D textureUnit4;
uniform sampler2D textureUnit5;
void main(void)
{
	
    vec4 blur0 = texture(textureUnit0, vTexCoord);
    vec4 blur1 = texture(textureUnit1, vTexCoord);
    vec4 blur2 = texture(textureUnit2, vTexCoord);
    vec4 blur3 = texture(textureUnit3, vTexCoord);
    vec4 blur4 = texture(textureUnit4, vTexCoord);
    vec4 blur5 = texture(textureUnit5, vTexCoord);
	
    vec4 summedBlur = blur0 + blur1 + blur2 + blur3 + blur4 + blur5;
    gl_FragColor = summedBlur / 6;
}

你可能感兴趣的:(opengl,开发语言,图形渲染)