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

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

一,缓冲区概念

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

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

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

二,使用缓冲区

1,创建缓冲区

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

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

2,绑定缓冲区

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

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

使用绑定点缓冲区,可以拿绑定点作为参数调用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如何使用缓冲区。

缓冲区使用方式:

在不确定缓冲区用途时,对于通常的缓冲区使用方式或条件来说,使用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控制的PBO中。CPU仍涉及将源加载到PBO,但不涉及将像素数据从PBO传输到纹理对象。相反,GPU(OpenGL驱动程序)管理将数据从PBO复制到纹理对象。这意味着OpenGL执行DMA传输操作时不会浪费CPU周期。此外,OpenGL可以安排异步DMA传输以供以后执行。因此,glTexImage2D()立即返回,CPU可以执行其他操作,而无需等待像素传输完成。

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

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

效果预览:

代码参考:

Render.CPP

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

#include <gltools/GLTools.h>
#include <gltools/GLShaderManager.h>           //着色器管理类
#include <gltools/GLMatrixStack.h>            //矩阵堆栈
#include <gltools/GLFrame.h>                 //矩阵
#include <gltools/GLFrustum.h>              //投影矩阵
#include <gltools/GLBatch.h>               //三角形批次类
#include <gltools/GLGeometryTransform.h>  //几何变换管道
#include <gltools/StopWatch.h>           //时间管理
#include <freeglut/freeglut.h>

#include <math.h>                     //数学库  
#include <iostream>
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;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: OpenGL ES(Open Graphics Library for Embedded Systems)是一种软件接口,用于在嵌入式系统(如手机,平板电脑)上进行2D和3D图形绘制。 帧缓冲区(framebuffer)是OpenGL ES中的一个图形缓冲区,用于存储图形绘制的输出。它由多个颜色缓冲区(color buffer)和深度缓冲区(depth buffer)组成。当图形绘制完成后,帧缓冲区中的图像将被显示在屏幕上。 在OpenGL ES中,帧缓冲区可以用来实现许多不同的效果,包括双缓冲(double buffering)、多重采样(multisampling)和屏幕空间反射(screen space reflections)。 ### 回答2: OpenGL ES是一种在移动设备和嵌入式系统上使用的图形库,它提供了一种可编程的方式来渲染2D和3D图形。在OpenGL ES中,帧缓冲区是一个用于存储渲染输出的内存区域。 帧缓冲区是一个用于存储图像数据的固定大小的缓冲区。在渲染过程中,OpenGL ES会将渲染输出存储到帧缓冲区中。帧缓冲区可以看作是一个像素数组,每个像素都包含了颜色值和其他可能的信息,如深度、模板等。通过使用帧缓冲区,我们可以进行离屏渲染、后期处理和图像效果的操作。 在OpenGL ES中,我们可以创建多个帧缓冲区并切换它们来实现不同的渲染效果。通常情况下,我们会创建一个前向渲染的帧缓冲区,用于将渲染结果直接显示在屏幕上。同时,我们也可以创建一个后向渲染的帧缓冲区,用于存储渲染结果以便后续处理。 使用帧缓冲区可以实现一些常见的图像效果,如屏幕后处理、多重采样抗锯齿(MSAA)、阴影渲染等。通过在帧缓冲区上执行多个渲染通道,我们可以将不同的渲染效果叠加在一起,创建出更加复杂和逼真的图像效果。 需要注意的是,帧缓冲区的大小和格式应该与设备的显示屏幕大小和格式保持一致,以确保渲染结果可以正确地显示出来。同时,由于帧缓冲区占用了系统内存,我们在使用完之后需要及时释放它,以避免内存泄漏和性能问题。 总之,帧缓冲区OpenGL ES中扮演着重要的角色,它提供了一种存储渲染输出的机制,并且可以通过多个渲染通道实现各种图像效果。通过合理使用帧缓冲区,我们可以创建出更加逼真和吸引人的图形效果。 ### 回答3: OpenGL ES的帧缓冲区是用于渲染图形的重要概念。它是一个内存区域,用于存储渲染的结果,并将其发送到显示设备以显示在屏幕上。 帧缓冲区由颜色附件和可选的深度和模板附件组成。颜色附件用于存储渲染的颜色值,而深度和模板附件分别用于存储深度和模板信息。 在渲染过程中,我们可以在帧缓冲区中绘制图形。首先,我们将帧缓冲区设置为当前绘制目标,然后使用OpenGL ES提供的各种绘制命令来绘制图形。在绘制完成后,渲染的结果将存储在帧缓冲区中。 一旦渲染完成,我们可以选择将帧缓冲区中的内容发送到显示设备并显示在屏幕上。这通常通过将帧缓冲区绑定到纹理对象或渲染缓冲对象来完成。然后,我们可以将纹理对象或渲染缓冲对象作为图形渲染的输入来进行后续处理或者直接在屏幕上显示。 帧缓冲区提供了一个灵活和高效的方式来进行图形渲染。通过使用帧缓冲区,我们可以实现各种视觉效果,例如离屏渲染、后期处理和屏幕抖动等。此外,帧缓冲区还允许我们进行多重渲染目标,即同时将结果存储在多个颜色附件中,从而实现更复杂的渲染效果。 总而言之,OpenGL ES的帧缓冲区是一个用于存储渲染结果的内存区域,它提供了灵活和高效的方式来进行图形渲染,并可以将结果发送到显示设备以显示在屏幕上。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值