天天看點

OpenGL(十四) 模闆測試

啟用模闆測試時,OpenGL會在記憶體中開辟一塊空間作為模闆緩沖區,裡邊儲存了每個像素的“模闆值”,模闆測試的過程就是把每一個像素的模闆值與一個設定的模闆參考值進行比較,符合設定條件的通過測試,不符合條件的則不會繪制。

glClearStencil函數用來指定模闆值複位後的初始值,跟使用glClearColor函數指定複位後畫闆的顔色一樣。

glStencilFunc函數用來設定模闆測試條件。第一個是模闆測試條件,第二個是指定的目前模闆的測試值,程式會使用改值與參考模闆值進行比較,第三個是mask位,設定後,隻會比較mask中二進制位位1的位。

glStencilOp (GLenum fail, GLenum zfail, GLenum zpass)用來設定模闆值根據模闆測試結果和深度測試結果的結果如何變化。

第一個參數表示模闆測試未通過時該如何變化,第二個參數表示模闆測試通過,但深度測試未通過時該如何變化,第三個參數表示模闆測試和深度測試均通過時該如何變化。這種模闆的變換可以是:

  • GL_KEEP(不改變,這也是預設值)
  • GL_ZERO(回零)
  • GL_REPLACE(使用測試條件中的設定值來代替目前模闆值)
  • GL_INCR(增加1,但如果已經是最大值,則保持不變)
  • GL_INCR_WRAP(增加1,但如果已經是最大值,則從零重新開始)
  • GL_DECR(減少1,但如果已經是零,則保持不變)
  • GL_DECR_WRAP(減少1,但如果已經是零,則重新設定為最大值)
  • GL_INVERT(按位取反)

另外程式中用到了glPushMatrix和glPopMatrix兩個函數,glPushMatrix函數将目前矩陣壓入堆棧,使得之後的操作不受之前矩陣變換的影響,都是相對于原始的世界坐标系進行操作。

glPopMatrix将與之最近的一個glPushMatrix壓入堆棧的矩陣取出,使得之前的矩陣變換對其後的運算有效。這兩個函數的配合使用可以使得兩者之間的矩陣操作不受之前生成的變換矩陣的影響,并且兩者之間的矩陣操作也不會對主體的矩陣變換産生影響。

以下是使用模闆測試功能,實作平面鏡功能,鏡子之外的物體不會繪制:

#define WindowWidth   400
#define WindowHeight 400
#define WindowTitle "OpenGL模闆測試"
#include <gl/glut.h>

GLfloat angle = 0.0f; 

void draw_sphere()
{
// 設定光源
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
  {
GLfloat
pos[]     = {5.0f, 5.0f, 0.0f, 1.0f},
ambient[] = {0.0f, 0.0f, 1.0f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
  }

// 繪制一個球體
glPushMatrix();
glTranslatef(0, 0, 2);
glutSolidSphere(0.8, 20, 20);
glPopMatrix();
}

void display(void)
{
// 清除螢幕
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// 設定觀察點
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, 1, 5, 25);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(5, 0, 6.5, 0, 0, 0, 0, 1, 0);

glRotatef(angle,0,1,0);

glEnable(GL_DEPTH_TEST);

// 繪制球體
glDisable(GL_STENCIL_TEST);
draw_sphere();

// 繪制一個平面鏡。在繪制的同時注意設定模闆緩沖。
// 另外,為了保證平面鏡之後的鏡像能夠正确繪制,在繪制平面鏡時需要将深度緩沖區設定為隻讀的。
// 在繪制時暫時關閉光照效果
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glEnable(GL_STENCIL_TEST);

glDisable(GL_LIGHTING);
glColor3f(0.5f, 0.5f, 0.5f);
glDepthMask(GL_FALSE);
glRectf(-2.0f, -2.0f, 1.6f, 1.6f);
glDepthMask(GL_TRUE);

// 繪制一個與先前球體關于平面鏡對稱的球體,注意光源的位置也要發生對稱改變
// 因為平面鏡是在X軸和Y軸所确定的平面,是以隻要Z坐标取反即可實作對稱
// 為了保證球體的繪制範圍被限制在平面鏡内部,使用模闆測試
glStencilFunc(GL_EQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glScalef(1.0f, 1.0f, -1.0f);
draw_sphere();

// 交換緩沖
glutSwapBuffers();
}

void myIdle(void)   
{      
angle+=0.1f;   
if( angle >= 360.0f )      
angle = 0.0f;      
display();  
}  

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);
glutIdleFunc(&myIdle); 
// 開始顯示
glutMainLoop();
return 0;
}
      

運作截圖:

OpenGL(十四) 模闆測試
OpenGL(十四) 模闆測試