1. 程式人生 > >基於模板測試實現半透明多邊形運算

基於模板測試實現半透明多邊形運算

多邊形常見操作

android api sample中的region 操作如下圖:

本文基於opengl 的stencil buffer 實現這幾種多邊形運算。

OpenGL 模板測試實現

如下圖:
 如上圖, Source為原始半透明重疊多邊形繪製效果,重疊部分因為融合的緣故 有增強效果。
Union   聯合操作,需保證每個畫素 當且僅當只屬於一個多邊形,為此通過模板測試實現的具體思路:
  1. 用0x00 清除模板緩衝區,開啟模板測試
  2. 寫模板緩衝區,同時進行模板測試,同時寫顏色緩衝區:保證只有模板值為0的片源可以通過模板測試,而且通過模板測試後 模板值加1 操作。

Difference 做差操作,是兩個矩形相減效果,順序無關,具體實現:
  1. 0x00 清除模板緩衝區
  2. 禁止顏色緩衝區寫入,禁止模板測試,畫任意一個矩形,將模板值加1 操作。此時末班緩衝區中第一個矩形繪製區域為mask區域,該區域內不能繪圖。
  3. 開啟模板測試,只能在模板值為0x00的地方繪圖,畫第二個矩形,模板測試通過後模板值加1。

XOr 異或操作,兩個矩形相交的區域不顯示,不相交的區域顯示,具體思路:
  1. 0x00 清除模板緩衝區,
  2. 禁掉模板測試,禁掉顏色緩衝區寫入,將模板值加1操作,繪製兩個矩形,相交區域的模板值為2,其他矩形區域模板值為1。
  3. 開啟模板測試,允許寫入顏色緩衝區,測試條件為模板值等於1,模板值為8位,此時掩碼為0x01,二進位制為00000001。繪製兩個矩形。模板測試通過後將模板值置為0。

Intersection 相交操作,只顯示兩個矩形相交區域,具體思路:
  1. 0x00 清除模板緩衝區,
  2. 禁掉模板測試,禁掉顏色緩衝區寫入,將模板值加1操作,繪製兩個矩形,相交區域的模板值為2,其他矩形區域模板值為1。
  3. 開啟模板測試,允許寫入顏色緩衝區,測試條件為模板值等於2,模板值為8位,此時掩碼為0x03,二進位制為00000011。繪製任意一個矩形。模板測試通過後將模板值置為0。


GLUT框架下實現:

// @module:		stencil render
// @file:		opacityRectRender.cpp
// @author:		peteryfren
// @date:		2013/3/28
// @version:	1.0
// @desc:		通過模板緩衝區實現半透明矩形的常見操作。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <math.h>

#include <gl/glut.h>

const int windowWidth = 256;
const int windowHeight = 256;

GLenum checkForError(char *loc);

void opacitySourceRender(int width, int height);
void opacityRectXorRender(int width, int height);
void opacityRectUnionRender(int width, int height);
void opacityRectIntersectRender(int width, int height);
void opacityRectDifferenceRender(int width, int height);

void Redraw(void)
{
	glCullFace(GL_BACK);

	float destAlpha = 0.5;
	glClearColor(0,0,0, 0.5);	// 指定清理顏色值
	glClearStencil(0x00);		// 指定清理幀緩衝值
	glClearDepth(0.0);			// 指定清理深度緩衝值
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	// glEnable (GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	opacitySourceRender(windowWidth, windowHeight);
	
	// opacityRectDifferenceRender(windowWidth, windowHeight);
	// opacityRectIntersectRender(windowWidth, windowHeight);
	// opacityRectUnionRender(windowWidth, windowHeight);
	// opacityRectXorRender(windowWidth, windowHeight);

	glutSwapBuffers();

	checkForError("swap");
}

void opacitySourceRender(int width, int height)
{
	// one way to solve this in generic OpenGL is to 
	//		fill the framebuffer with your desired alpha channel, 
	// switch the blending mode to glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA)
	//		and draw the rectangles. 

	glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);

	float tileVertices[] = {0,height/2, width,height/2, width,height*3/4, 0,height*3/4}; 
	glColor4f(1, 0, 0, 0.5);
	glVertexPointer(2, GL_FLOAT, 0, tileVertices);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glColor4f(1, 1, 1, 1);

	float tileVertices1[] = {width/4,0, width/2,0, width/2,height, width/4,height}; 
	glColor4f(1, 0, 0, 0.5);
	glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glColor4f(1, 1, 1, 1);
}

void opacityRectDifferenceRender(int width, int height)
{
	glEnable(GL_STENCIL_TEST);

	// draw mask area
	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
	glStencilFunc(GL_ALWAYS, 0, 0xFF);			// 禁模板測試
	glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);		// 模板值加1

	glColor4f(1, 0, 0, 0.1);
	float tileVertices[] = {0,height/2, width,height/2, width,height*3/4, 0,height*3/4};
	glColor4f(1, 0, 0, 0.5);
	glVertexPointer(2, GL_FLOAT, 0, tileVertices);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

	//static unsigned char data[windowWidth*windowHeight];
	//glReadPixels(0, 0, windowWidth, windowHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, data);
	//SaveFileGrayBMP("stencil.bmp", data, windowWidth,  windowHeight);

	// draw main area
	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	glStencilFunc(GL_EQUAL, 0x00, 0x01);	// 只能在模板為0的地方繪圖
	glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);	

	glColor4f(1, 0, 0, 0.5);
	float tileVertices1[] = {width/4,0, width/2,0, width/2,height, width/4,height}; 
	glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glColor4f(1, 1, 1, 1);

	glDisable(GL_STENCIL_TEST);
}

void opacityRectXorRender(int width, int height)
{
	glEnable(GL_STENCIL_TEST);

	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

	// draw mask area 
	glStencilOp(GL_INCR, GL_INCR, GL_INCR);
	glStencilFunc(GL_ALWAYS, 0x00, 0xFF);		// 禁用模板

	float tileVertices[] = {0,height/2, width,height/2, width,height*3/4, 0,height*3/4};
	glColor4f(1, 0, 0, 0.5);
	glVertexPointer(2, GL_FLOAT, 0, tileVertices);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

	glColor4f(1, 0, 0, 0.5);
	float tileVertices1[] = {width/4,0, width/2,0, width/2,height, width/4,height}; 
	glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glColor4f(1, 1, 1, 1);

	//static unsigned char data[windowWidth*windowHeight];
	//glReadPixels(0, 0, windowWidth, windowHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, data);
	//SaveFileGrayBMP("stencil.bmp", data, windowWidth,  windowHeight);

	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	glStencilFunc(GL_EQUAL, 0x01, 0x01);		// 模板值為1的區域才繪製
	glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);		// 用0替換當前值

	glColor4f(1, 0, 0, 0.5);
	glVertexPointer(2, GL_FLOAT, 0, tileVertices);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glColor4f(1, 1, 1, 1);

	glColor4f(1, 0, 0, 0.5);
	glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glColor4f(1, 1, 1, 1);

	glDisable(GL_STENCIL_TEST);
}

void opacityRectIntersectRender(int width, int height)
{
	glEnable(GL_STENCIL_TEST);
	
	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
	
	// draw mask area 
	glStencilOp(GL_INCR, GL_INCR, GL_INCR);
	glStencilFunc(GL_ALWAYS, 0x00, 0xFF);		// 禁用模板

	float tileVertices[] = {0,height/2, width,height/2, width,height*3/4, 0,height*3/4};
	glColor4f(1, 0, 0, 0.5);
	glVertexPointer(2, GL_FLOAT, 0, tileVertices);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

	glColor4f(1, 0, 0, 0.5);
	float tileVertices1[] = {width/4,0, width/2,0, width/2,height, width/4,height}; 
	glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glColor4f(1, 1, 1, 1);

	//static unsigned char data[windowWidth*windowHeight];
	//glReadPixels(0, 0, windowWidth, windowHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, data);
	//SaveFileGrayBMP("stencil.bmp", data, windowWidth,  windowHeight);

	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	glStencilFunc(GL_EQUAL, 0x02, 0x03);		// 模板值為2的區域才繪製 掩碼0000 0011
	glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);

	glColor4f(1, 0, 0, 0.5);
	glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glColor4f(1, 1, 1, 1);

	glDisable(GL_STENCIL_TEST);
}

void opacityRectUnionRender(int width, int height)
{
	// 模板緩衝區就一個位元組

	glEnable (GL_STENCIL_TEST);		

	// glStencilFunc(GL_ALWAYS,0,0xFF);	// 禁用模板測試

	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);	// 開啟寫入顏色緩衝區

	// fail = KEEP  模版測試失敗後保持模板緩衝區中值 不變
	// zfail =  KEEP 深度測試失敗後保持模版緩衝區中值 不變
	// zpass = INCR 深度測試通過後對模版緩衝區中值 加1操作
	glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);		
	
	// glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

	// func = EQUAL 只有與參考值相等才能通過模版測試
	// ref = 0x00	參考值
	// mask = 0x01	只比較最低位
	glStencilFunc(GL_EQUAL, 0x0, 0x1);	// 開啟模板測試

	glEnable (GL_BLEND);		// 開啟混合
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	// sfactor = 1 - dstA
	// dfactor = dstA
	// glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);

	float tileVertices[] = {0,height/2, width,height/2, width,height*3/4, 0,height*3/4};
	glColor4f(1, 0, 0, 0.5);
	glVertexPointer(2, GL_FLOAT, 0, tileVertices);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

	glColor4f(1, 0, 0, 0.5);
	float tileVertices1[] = {width/4,0, width/2,0, width/2,height, width/4,height}; 
 	glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	glColor4f(1, 1, 1, 1);

	//static unsigned char data[windowWidth*windowHeight];
	//glReadPixels(0, 0, windowWidth, windowHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, data);
	//SaveFileGrayBMP("stencil.bmp", data, windowWidth,  windowHeight);

	glDisable (GL_STENCIL_TEST);
}

void Button(int button, int state, int x, int y)
{
	if (state != GLUT_UP)
		return;

	switch (button) 
	{
	case GLUT_LEFT_BUTTON:

		glutPostRedisplay();
		break;
	
	case GLUT_RIGHT_BUTTON:

		glutPostRedisplay();
		break;
	}
}

void Keyboard(unsigned char key, int x, int y)
{

	glutPostRedisplay();
}

void Reshape(int width, int height)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0.0f, (GLfloat) windowWidth, 0.0f, 
		(GLfloat) windowHeight, -1.0f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glViewport(0, 0, windowWidth, windowHeight);
}

int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH|GLUT_STENCIL);

	glutInitWindowSize(windowWidth, windowHeight);
	glutCreateWindow("Region");

	// set up world space to screen mapping
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0.0f, (GLfloat) windowWidth, 0.0f, 
		(GLfloat) windowHeight, -1.0f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glViewport(0, 0, windowWidth, windowHeight);

	glClearColor(1.0, 1.0, 1.0, 1.0);
	glutDisplayFunc(Redraw);
	glutReshapeFunc(Reshape);
	glutMouseFunc(Button);
	glutKeyboardFunc(Keyboard);

	glEnable(GL_CULL_FACE);

	glutMainLoop();

	return 0;
}

GLenum checkForError(char *loc)
{
	GLenum errCode;
	const GLubyte *errString;

	if ((errCode = glGetError()) != GL_NO_ERROR)
	{
		errString = gluErrorString(errCode);
		printf("OpenGL error: %s",errString);

		if (loc != NULL)
			printf("(%s)",loc);

		printf("\n");
	}

	return errCode;
}


參考

1. opengl 模板測試理論: http://blog.csdn.net/xiajun07061225/article/details/7388417 2. glStencilFunc函式講解: http://blog.csdn.net/doing5552/article/details/2297280 3. opencsg   http://www.opencsg.org/