GLSL着色器实现多重纹理与帧缓冲对象(FBO)

还记得我前面几篇博客上写的东西都是将纹理直接渲染到屏幕上,就是产生一个和纹理尺寸大小相同的窗口进行渲染,那么渲染完了就正好完整的显示了纹理图案。但是在做数值计算的时候,一般是不需要输出到屏幕上的,这就是今天我们要用到的帧缓存。有了帧缓存,我们的输出不需要是屏幕了,而是直接输出到帧缓存中去。而且帧缓冲区对象的使用还会对程序的性能有一定提升

那么帧缓冲对象到底是个什么东西呢?首先帧缓冲区你可以理解为就是一个概念,下面这个图可以很形象的解释帧缓冲区的意思。这里对这个图解释一下: 

       

其实真实存放数据的缓存就是纹理缓存和render缓存,FBO就是将纹理缓存和render缓存整了一个句柄关联起来,对其进行处理,现在理解为啥FBO只是一个概念了。


本篇博客主要有以下3个技术点:
1:将一个矩阵,这个矩阵存储着我们需要的数值,有可能就是一系数矩阵或者说权值矩阵,把这个矩阵放入纹理缓冲中,当做纹理来使用。
2:这里面用到了多重纹理,多重纹理的处理还是比单重的纹理处理的过程复杂的多的。
3:也是本文的重点,就是对帧缓存(FBO)的使用。

具体的使用还是要看代码吧。

大家在把代码贴到自己电脑上会缺少一个savePicture.h这个头文件,这个头文件包含了loadBMP和saveBMP两个函数,很抱歉,这两个函数不是我写的,不方便发到网上,但是这两个函数都是很简单的,大家可以自己到网上找到相关的函数来代替。有了这两个功能,程序就可以跑起来了。


#include "stdafx.h"
#include<windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/glut.h>
#include "savePicture.h"

//纹理的编号
static GLuint texture;    //lena图像作为纹理
static GLuint texWeigArr1;	//自己生成的纹理1
static GLuint texWeigArr2;	//自己生成的纹理2
static GLuint texOutput;    //产生结果的纹理

static GLuint fb;			//FBO编号

#define printOpenGLError() printOglError(__FILE__, __LINE__)

const GLint imgHeight = 512, imgWidth = 512;
const GLint imgHeight2 = 256, imgWidth2 = 256;
static GLfloat   weight_1[imgWidth][imgHeight];    //权值矩阵1
static GLfloat   weight_2[imgHeight2][imgWidth2];   //权值矩阵2
static  GLfloat outPutFb[imgHeight * imgWidth];   //输出纹理缓冲
static GLubyte  pData[imgHeight * imgWidth];   //存储最终的图像数据

//顶点着色器
const char *vShader = {
	//"#version 110  \n "
	"void main()"
	"{"
		"gl_TexCoord[0] = gl_MultiTexCoord0;"
		"gl_TexCoord[1] = gl_MultiTexCoord1;"
		"gl_TexCoord[2] = gl_MultiTexCoord2;"
	    "gl_Position = ftransform();"
	"}"
};

//片元着色器
const char *fShader = {
	//"#version 110          \n "
	"#extension GL_ARB_texture_rectangle : enable  \n"
    "uniform sampler2DRect  LenaTexture;         \n"
	"uniform sampler2DRect  WeightTex1;         \n"
    "uniform sampler2DRect  WeightTex2;         \n"
	"void main()         \n"
	"{          \n"
	    "vec2   weig1Pos =  gl_TexCoord[1].st;					\n "
		"vec2   weig2Pos =  gl_TexCoord[0].st / 2.0;"
		"vec2     pos1 = vec2(gl_TexCoord[0].s,512.0-gl_TexCoord[0].t);"
		"vec4   texColor   =  texture2DRect(LenaTexture,pos1 );         \n"
	    "vec4   weight1    =  texture2DRect(WeightTex1, weig1Pos);          \n "
		"vec4   weight2    =  texture2DRect(WeightTex2, weig2Pos);          \n"
		"texColor.yzw = vec3(0.0,0.0,0.0);         \n"
		"weight1.yzw = vec3(0.0,0.0,0.0);         \n"
		"if ( gl_TexCoord[0].s >256.0)"
		"{ gl_FragColor = texColor;     }   \n"
		"else"
		"{gl_FragColor =   weight1 * weight2;   }     \n"
	"}        \n"
};

//输出错误相关信息
int printOglError(char *file, int line)
{
	GLenum glErr;
	int retCode = 0;

	glErr = glGetError();
	while (glErr != GL_NO_ERROR)
	{
		printf("glError in file %s @ line %d: %s\n", file, line, gluErrorString(glErr));
		retCode = 1;
		glErr = glGetError();
	}
	return retCode;
}

//输出opengl错误
void printInfoLog(GLhandleARB obj)
{
	int infologLength = 0;
	int charsWritten = 0;
	GLcharARB *infoLog;
	printOpenGLError();

	glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength);
	printOpenGLError();

	if(infologLength > 0)
	{
		infoLog = (GLcharARB*)malloc(infologLength);
		if(infoLog == NULL)
		{
			printf("ERROR: Could not allocate InfoLog buffer\n");
			exit(1);
		}
		glGetInfoLogARB(obj,infologLength,&charsWritten,infoLog);
		printf("InfoLog:\n%s\n\n",infoLog);
		free(infoLog);
	}
	printOpenGLError();
}



/*************************************************************
function name:   initShaders
input:   1.    const char *vShaderCode, 
            2.     const char *fShaderCode,
output:  1.    -1   compile error
             2.    -2    link  error
			 3.	     progHandle    
description:
                  
*****************************************************************/
GLhandleARB initShaders( const char *vShaderCode, const char *fShaderCode )   
{
	GLhandleARB vertHandle, fragHandle, progHandle;   //对象句柄
	GLint vertCompiled, fragCompiled;				//状态值
	GLint linked;

	//创建顶点着色器对象和片元着色器对象
	vertHandle = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
	fragHandle = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

	//将源代码字符串加载到着色器中
	glShaderSource( vertHandle, 1, &vShaderCode, NULL );
	glShaderSource( fragHandle, 1, &fShaderCode, NULL );

	printf("编译码块顶点着色器并打印编译器日志文件:\n");
	//编译码块顶点着色器并打印编译器日志文件
	glCompileShaderARB(vertHandle);
	printOpenGLError();				//检查opengl错误
	glGetObjectParameterivARB(vertHandle,GL_OBJECT_COMPILE_STATUS_ARB, &vertCompiled);
	printInfoLog(vertHandle);

	printf("编译码块片元着色器并打印编译器日志文件:\n");
	//编译码块片元着色器并打印编译器日志文件
	glCompileShaderARB(fragHandle);
	printOpenGLError();             //检查opengl错误
	glGetObjectParameterivARB(fragHandle,GL_OBJECT_COMPILE_STATUS_ARB, &fragCompiled);
	printInfoLog(fragHandle);


	if(!vertCompiled || !fragCompiled)
		return -1;

	//创建一个程序对象并附加两个编译好的着色器
	progHandle = glCreateProgramObjectARB();
	glAttachObjectARB(progHandle, vertHandle);
	glAttachObjectARB(progHandle, fragHandle);

	printf("链接程序对象并打印信息日志:\n");
	//链接程序对象并打印信息日志
	glLinkProgramARB(progHandle);
	printOpenGLError();             //检查opengl错误
	glGetObjectParameterivARB(progHandle, GL_OBJECT_LINK_STATUS_ARB, &linked);
	printInfoLog(progHandle);

	if(!linked)
		return -2;

	//将程序对象安装为当前状态的一部分
	glUseProgramObjectARB(progHandle);     //改为运行的函数,用于测试该算法的时间

    return progHandle;	
}

//装载一个bmp图像使之成为纹理,其中貌似包含了 glTexImage2D这个函数的功能

int LoadGLTextures(char *textureFilePath)
{
	unsigned char *pTexData = NULL;

	long bitCnt =  0;
	long iw =0;
	long ih = 0;   //以上三个参数其实是没有的,~_~
	long status = LoadBMP( textureFilePath, &pTexData,&iw, &ih, &bitCnt );

     glGenTextures( 1, &texture );
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB,texture);

	//当卷积内核超过了图像边界时使用图像边缘的像素值
	glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
	glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );

	//纹理过滤的方式不应该设置为线性插值
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

	glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, imgWidth, imgHeight, 0,GL_RGB,GL_UNSIGNED_BYTE,pTexData );

    glTexEnvi( GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE );

	return 0;
}

void init()
{
	
	glShadeModel( GL_FLAT );
	glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
	glViewport(0,0, imgWidth, imgHeight );
	glEnable ( GL_DEPTH_TEST );

	//LoadGLTextures("texture.bmp");
	if ( LoadGLTextures("texture.bmp") == 1 )
		printf( " Load Faild!   \n");
	
    //初始化权值矩阵1
	for ( int i = 0; i < imgHeight; ++i )
		for ( int j = 0; j < imgWidth; ++j )
		{
              weight_1[i][j]=1.0;
		}
	glGenTextures( 1, &texWeigArr1 );
	glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texWeigArr1 );
	//当卷积内核超过了图像边界时使用图像边缘的像素值
	glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
	glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );

	//纹理过滤的方式不应该设置为线性插值
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

	glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, imgWidth, imgHeight, 0,GL_LUMINANCE,GL_FLOAT,weight_1 );

	glTexEnvi( GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE );

	//初始化权值矩阵2
	for ( int i = 0; i <imgHeight2 / 2; ++i )
		for ( int j = 0; j <imgWidth2 /2; ++j )
		{
			weight_2[i][j] = 0.8;
		}

		for ( int i = 0; i <imgHeight2/ 2; ++i )
			for ( int j = imgWidth2 /2; j <imgWidth2; ++j )
			{
				weight_2[i][j] = 0.0;
			}

			for ( int i = imgHeight2 / 2; i <imgHeight2; ++i )
				for ( int j = 0; j <imgWidth2 /2; ++j )
				{
					weight_2[i][j] = 1.0;
				}		
				for ( int i = imgHeight2 / 2; i <imgHeight2; ++i )
					for ( int j =imgWidth2 /2; j <imgWidth2 ; ++j )
					{
						weight_2[i][j] = 0.2;
					}		
		glGenTextures( 1, &texWeigArr2 );
		glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texWeigArr2 );
		//当卷积内核超过了图像边界时使用图像边缘的像素值
		glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
		glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );

		//纹理过滤的方式不应该设置为线性插值
		glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
		glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
		glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, imgWidth2,
			           imgHeight2, 0,GL_LUMINANCE,GL_FLOAT,weight_2 );

		//设置输出纹理的参数
	glGenTextures( 1, &texOutput );
	glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texOutput );

	glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
	glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );

	//纹理过滤的方式不应该设置为线性插值
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

	//没有给输出的纹理数据,等待程序进行赋值
	glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, imgWidth, imgHeight, 0,GL_RGBA,GL_FLOAT,0 ); 
	glTexEnvi( GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE );

	//激活各个纹理
	glActiveTexture( GL_TEXTURE0 );
    glBindTexture(GL_TEXTURE_RECTANGLE_ARB,texture);
	glActiveTexture ( GL_TEXTURE1 );
	glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texWeigArr1 );
	glActiveTexture ( GL_TEXTURE2 );
	glBindTexture( GL_TEXTURE_RECTANGLE_ARB, texWeigArr2 );
}
void initFBO( unsigned unWidth, unsigned unHeight )
{
	//创建FBO,准备屏幕外帧缓存
	glGenFramebuffersEXT( 1, &fb );

	//绑定屏幕外帧缓存,即避开了窗口系统默认的渲染目标
	glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, fb );


}
void readFromTexture( GLfloat *data )
{
	glReadBuffer( GL_COLOR_ATTACHMENT0_EXT );
	glReadPixels( 0, 0, imgWidth, imgHeight,
		                       GL_LUMINANCE, GL_FLOAT, data );

}
void display( void )
{
	glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	关联输出缓存至FBO
	glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, 
		                       GL_COLOR_ATTACHMENT0_EXT, 
							    GL_TEXTURE_RECTANGLE_ARB,texOutput,0 );

	glDrawBuffer( GL_COLOR_ATTACHMENT0_EXT );

	glBegin( GL_QUADS );  

	glMultiTexCoord2f( GL_TEXTURE0, 0.0, 0.0);  
	glMultiTexCoord2f( GL_TEXTURE1, 0.0, 0.0); 
	glMultiTexCoord2f( GL_TEXTURE2, 0.0, 0.0); 
	glVertex2f( -1.0, -1.0);   

	glMultiTexCoord2f(GL_TEXTURE0, 0.0, 512.0 );  
	glMultiTexCoord2f(GL_TEXTURE1, 0.0, 512.0 );  
	glMultiTexCoord2f( GL_TEXTURE2, 0.0, 512.0); 
	glVertex2f( -1.0, 1.0 );

	glMultiTexCoord2f(GL_TEXTURE0, 512.0, 512.0 );  
	glMultiTexCoord2f(GL_TEXTURE1, 512.0, 512.0 );  
	glMultiTexCoord2f( GL_TEXTURE2, 512.0,512.0); 
	glVertex2f( 1.0, 1.0 );

	glMultiTexCoord2f(GL_TEXTURE0, 512.0, 0.0 ); 
	glMultiTexCoord2f(GL_TEXTURE1, 512.0, 0.0 ); 
	glMultiTexCoord2f( GL_TEXTURE2, 512.0, 0.0); 
	glVertex2f ( 1.0, -1.0 );

	glEnd( );
	glFlush();
}




int _tmain(int argc, char* argv[])
{
	GLhandleARB progHandle = 0;

	glutInit( &argc, argv );
	glutInitDisplayMode( GLUT_SINGLE| GLUT_LUMINANCE);
	glutInitWindowSize ( imgWidth, imgHeight);
	glutInitWindowPosition( 100, 100 );
	glutCreateWindow(" gningh多重纹理&FBO ");
	
	glewInit();
	initFBO(imgWidth, imgHeight );
	init();

	progHandle = initShaders(vShader, fShader); 
	if ( progHandle <= 0 )
		printf("Failed to run shader.\n");
	else{
		//设置初始一致变量
		glUniform1i( glGetUniformLocation( progHandle, "LenaTexture" ), 0 );   //0 是纹理的句柄
		glUniform1i( glGetUniformLocation( progHandle, "WeightTex1" ), 1 );   
		glUniform1i( glGetUniformLocation(progHandle, "WeightTex2" ), 2 );

	}

	display();

	readFromTexture( outPutFb );

    //因为outputFb中保存的图像灰度都是0~1之间的数值表示的
	//这里我们要将0~1之间的数值乘以255变为0~255之间的灰度
	for ( int i =0; i < imgHeight * imgWidth; ++i )
	{
		pData[i] = (char)(outPutFb[i] * 255);
	}

	//将pData中数据保存为result.bmp。8代表是8位灰度
	asrSaveBMP("result.bmp",pData,imgWidth,imgHeight,8);
 
	return 0;
}

原始图像:



经过处理后的图像为:




reference:

http://www.songho.ca/opengl/gl_fbo.html

http://blog.csdn.net/xiajun07061225/article/details/7283929

http://stackoverflow.com/questions/11236991/minimal-example-for-creating-fbo-using-opengl-es-2-0-on-ios

http://hankjin.blog.163.com/blog/static/3373193720103892121336/

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
不使用缓冲纹理图片实现环形波纹效果,可以使用顶点着色器中的顶点位置信息和片段着色器中的时间信息来计算出每个像素点的颜色值。具体的实现步骤如下: 1.定义顶点着色器程序,将顶点坐标和纹理坐标传递给片段着色器。 ``` #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoord; out vec2 TexCoord; void main() { gl_Position = vec4(aPos, 1.0); TexCoord = aTexCoord; } ``` 2.定义片段着色器程序,根据顶点坐标和时间计算出每个像素点的颜色值。 ``` #version 330 core out vec4 FragColor; uniform float time; uniform vec2 center; // 波纹中心 uniform float radius; // 波纹半径 uniform float strength; // 波纹强度 void main() { vec2 uv = gl_FragCoord.xy / vec2(800, 600); // 屏幕坐标系转换到纹理坐标系 float dist = length(uv - center); if (dist < radius) { float alpha = (radius - dist) * strength; float offset = time * 2.0; float angle = atan(uv.y - center.y, uv.x - center.x) + offset; uv.x += alpha * cos(angle); uv.y += alpha * sin(angle); } FragColor = vec4(uv, 0.0, 1.0); } ``` 在片段着色器中,我们定义了一些常量和变量来控制波纹的中心、半径和强度。根据像素点到波纹中心的距离,可以计算出波纹的强度,然后通过一个偏移量来控制波纹的运动方向和速度。最后,将波纹的偏移量应用到纹理坐标上,从而实现波纹效果。 3.在主程序中,创建一个矩形并渲染到屏幕上。 ``` // 创建矩形 float vertices[] = { -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f }; unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); // 渲染循环 while (!glfwWindowShouldClose(window)) { float time = glfwGetTime(); // 渲染矩形 glViewport(0, 0, 800, 600); glClear(GL_COLOR_BUFFER_BIT); shader.use(); shader.setFloat("time", time); shader.setVec2("center", vec2(0.5, 0.5)); shader.setFloat("radius", 0.3); shader.setFloat("strength", 0.1); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 6); glfwSwapBuffers(window); glfwPollEvents(); } ``` 在主程序中,我们创建了一个矩形,并将顶点坐标和纹理坐标传递给片段着色器。然后,将时间、波纹中心、半径和强度等信息传递给片段着色器,从而实现环形波纹效果。最后,将矩形渲染到屏幕上。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值