• OpenGL(十三) Alpha测试、剪裁测试



    Alpha测试测试就是测试每一个像素的Alpha值是否满足某一个特定的条件,如果满足,则该像素会被绘制,如果不满足则不绘制,跟深度测试的机制是一样的,只不过深度测试考察的是像素的“深度”属性,Alpha测试考察的是像素的“Alpha”属性。


    利用Alpha测试的这一特性,可以模拟两幅图像叠加,但是又要求前景图像有一部分是透明的场景,这时候可以把要求透明的区域内的每一个像素的Alpha值设置为0,在之后的Alpha测试的通过条件设置为大于0.5才通过,这样Alpha值为0的像素就不会被绘制,达到透明的效果。


    通过glEnable(GL_ALPHA_TEST)启用Alpha测试,通过GLDisable(GL_ALPHA_TEST)禁用Alpha测试。

    设置Alpha测试的通过条件的函数是glAlphaFunc(GLenum_func,GLclampf ref),func是参数的比较方式,ref是参数,可以取的参数以及含义如下:

    • GL_ALWAYS(始终通过)
    • GL_NEVER(始终不通过)
    • GL_LESS(小于则通过)
    • GL_LEQUAL(小于等于则通过)
    • GL_EQUAL(等于则通过)
    • GL_GEQUAL(大于等于则通过)
    • GL_NOTEQUAL(不等于则通过)

    例如有如下两幅图像需要叠加,第一个是前景图像,第二个是背景图像,场景要求是叠加之后的效果是从窗户里看过去的效果。


    背景:



    前景:



    可以利用Alpha测试,设置前景图像中白色部分的Alpah值为0,其他部分的Alpha值为1,在测试条件中选用GL_GREATER(大于则通过):


    #define WindowWidth   400
    #define WindowHeight 400
    #define WindowTitle "OpenGLAlpha测试"
    #define BMP_Header_Length 54
    #include <gl/glut.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    GLuint texGround;
    GLuint texWall;
    int power_of_two(int n)
    {
    	if( n <= 0 )
    		return 0;
    	return (n & (n-1)) == 0;
    }
    /* 函数load_texture
    * 读取一个BMP文件作为纹理
    * 如果失败,返回0,如果成功,返回纹理编号
    */
    GLuint load_texture(const char* file_name)
    {
    	GLint width, height, total_bytes;
    	GLubyte* pixels = 0;
    	GLint last_texture_ID;
    	GLuint texture_ID = 0;
    	// 打开文件,如果失败,返回
    	FILE* pFile = fopen(file_name, "rb");
    	if( pFile == 0 )
    		return 0;
    	// 读取文件中图象的宽度和高度
    	fseek(pFile, 0x0012, SEEK_SET);
    	fread(&width, 4, 1, pFile);
    	fread(&height, 4, 1, pFile);
    	fseek(pFile, BMP_Header_Length, SEEK_SET);
    	// 计算每行像素所占字节数,并根据此数据计算总像素字节数
    	{
    		GLint line_bytes = width * 3;
    		while( line_bytes % 4 != 0 )
    			++line_bytes;
    		total_bytes = line_bytes * height;
    	}
    	// 根据总像素字节数分配内存
    	pixels = (GLubyte*)malloc(total_bytes);
    	if( pixels == 0 )
    	{
    		fclose(pFile);
    		return 0;
    	}
    	// 读取像素数据
    	if( fread(pixels, total_bytes, 1, pFile) <= 0 )
    	{
    		free(pixels);
    		fclose(pFile);
    		return 0;
    	} 
    	{
    		GLint max;
    		glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
    		if( !power_of_two(width)
    			|| !power_of_two(height)
    			|| width > max
    			|| height > max )
    		{
    			const GLint new_width = 1024;   //修改为2的整数次幂
    			const GLint new_height = 1024; // 规定缩放后新的大小为边长的正方形
    			GLint new_line_bytes, new_total_bytes;
    			GLubyte* new_pixels = 0;
    			// 计算每行需要的字节数和总字节数
    			new_line_bytes = new_width * 3;
    			while( new_line_bytes % 4 != 0 )
    				++new_line_bytes;
    			new_total_bytes = new_line_bytes * new_height;
    			// 分配内存
    			new_pixels = (GLubyte*)malloc(new_total_bytes);
    			if( new_pixels == 0 )
    			{
    				free(pixels);
    				fclose(pFile);
    				return 0;
    			}
    			// 进行像素缩放
    			gluScaleImage(GL_RGB,
    				width, height, GL_UNSIGNED_BYTE, pixels,
    				new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);
    			// 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height
    			free(pixels);
    			pixels = new_pixels;
    			width = new_width;
    			height = new_height;
    		}
    	}
    	// 分配一个新的纹理编号
    	glGenTextures(1, &texture_ID);
    	if( texture_ID == 0 )
    	{
    		free(pixels);
    		fclose(pFile);
    		return 0;
    	}
    	// 绑定新的纹理,载入纹理并设置纹理参数
    	// 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复
    	glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture_ID);
    	glBindTexture(GL_TEXTURE_2D, texture_ID);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
    		GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    	glBindTexture(GL_TEXTURE_2D, last_texture_ID);
    	// 之前为pixels分配的内存可在使用glTexImage2D以后释放
    	// 因为此时像素数据已经被OpenGL另行保存了一份(可能被保存到专门的图形硬件中)
    	free(pixels);
    	return texture_ID;
    }
    void texture_colorkey(GLubyte r, GLubyte g, GLubyte b, GLubyte absolute)
    {
    	GLint width, height;
    	GLubyte* pixels = 0;
    	// 获得纹理的大小信息
    	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
    	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
    	// 分配空间并获得纹理像素
    	pixels = (GLubyte*)malloc(width*height*4);
    	if( pixels == 0 )
    		return;
    	glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
    	// 修改像素中的Alpha值
    	// 其中pixels[i*4], pixels[i*4+1], pixels[i*4+2], pixels[i*4+3]
    	//   分别表示第i个像素的蓝、绿、红、Alpha四种分量,0表示最小,255表示最大
    	{
    		GLint i;
    		GLint count = width * height;
    		for(i=0; i<count; ++i)
    		{
    			if( abs(pixels[i*4] - b) <= absolute
    				&& abs(pixels[i*4+1] - g) <= absolute
    				&& abs(pixels[i*4+2] - r) <= absolute )
    				pixels[i*4+3] = 0;
    			else
    				pixels[i*4+3] = 255;
    		}
    	}
    	// 将修改后的像素重新设置到纹理中,释放内存
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
    		GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
    	free(pixels);
    }
    void display(void)
    {
    	static int initialized   = 0;
    	static GLuint texWindow = 0;
    	static GLuint texPicture = 0;
    	// 执行初始化操作,包括:读取背景,读取前景,将相框由BGR颜色转换为BGRA,启用二维纹理
    	if( !initialized )
    	{
    		texPicture = load_texture("backImage.bmp");
    		texWindow = load_texture("frontImage.bmp");
    		glBindTexture(GL_TEXTURE_2D, texWindow);
    		texture_colorkey(255, 255, 255, 10);
    		glEnable(GL_TEXTURE_2D);
    		initialized = 1;
    	}
    	// 清除屏幕
    	glClear(GL_COLOR_BUFFER_BIT);
    	// 绘制背景,此时不需要进行Alpha测试,所有的像素都进行绘制
    
    	glMatrixMode(GL_PROJECTION);  
    	glLoadIdentity();  
    	gluPerspective(65,1,2,50);  
    
    	glMatrixMode(GL_MODELVIEW);  
    	glLoadIdentity();  
    	gluLookAt(0,0,4.5,0,0,0,0,1,0);  
    	glBindTexture(GL_TEXTURE_2D, texPicture);
    	glDisable(GL_ALPHA_TEST);
    	glBegin(GL_QUADS);
    	glTexCoord2f(0, 0);     glVertex3f(-6.0f, -6.0f,-3);
    	glTexCoord2f(0, 1);     glVertex3f(-6.0f, 6.0f,-3);
    	glTexCoord2f(1, 1);     glVertex3f( 6.0f, 6.0f,-3);
    	glTexCoord2f(1, 0);     glVertex3f( 6.0f, -6.0f,-3);
    	glEnd();
    
    	// 绘制前景,此时进行Alpha测试,只绘制不透明部分的像素
    	glBindTexture(GL_TEXTURE_2D, texWindow);
    	glEnable(GL_ALPHA_TEST);
    	glAlphaFunc(GL_GREATER, 0.5f);
    	glBegin(GL_QUADS);
    	glTexCoord2f(0, 0);     glVertex3f(-3.0f, -3.0f,0);
    	glTexCoord2f(0, 1);     glVertex3f(-3.0f, 3.0f,0);
    	glTexCoord2f(1, 1);     glVertex3f( 3.0f, 3.0f,0);
    	glTexCoord2f(1, 0);     glVertex3f( 3.0f, -3.0f,0);
    	glEnd();
    	// 交换缓冲
    	glutSwapBuffers();
    }
    int main(int argc, char* argv[])
    {
    	// GLUT初始化
    	glutInit(&argc, argv);
    	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    	glutInitWindowPosition(100, 100);
    	glutInitWindowSize(WindowWidth, WindowHeight);
    	glutCreateWindow(WindowTitle);
    	glutDisplayFunc(&display);
    	// 开始显示
    	glutMainLoop();
    	return 0;
    }
    


    实现效果,左侧视角:



    正视视角:



    右侧视角:




    剪裁测试:


    所谓剪裁测试就是限定一个矩形绘制窗口,只有在这个窗口范围内的像素才会被绘制,窗口外的像素被忽略。使用glEnable(GL_SCISSOR_TEST)开启剪裁测试,使用GLDisable(GL_SCISSOR_TEST)关闭。使用

    glScissor (GLint x, GLint y, GLsizei width, GLsizei height)设定剪裁窗口。


    在上例代码的基础上,只需要在显示部分函数display清屏之后,加入以下两句:

            glEnable(GL_SCISSOR_TEST);
    	glScissor(0,0,300,300);


    可以实现把显示画面划定在以左下角为起点的300*300的剪裁窗口中,显示效果如下:



  • 相关阅读:
    Ubuntu中VisualBox无法识别USB设备
    SpaceVim的基本安装和常见问题
    Linux下配置ssh免密远程登录
    PhantomJS在Selenium中被标记为过时的应对措施
    Linux下Vim编辑器访问系统剪切板
    Linux下将使用rm删除的文件显示在回收站中
    Java基础之创建窗口——使用卡片布局管理器(TryCardLayout)
    Java基础之创建窗口——使用边界布局管理器(TryBorderLayout)
    Java基础之创建窗口——使用流布局管理器(TryFlowLayout)
    Java基础之创建窗口——颜色和光标(TryWindow4)
  • 原文地址:https://www.cnblogs.com/mtcnn/p/9411914.html
Copyright © 2020-2023  润新知