每個(gè)像素都有自己對(duì)應(yīng)的 Buffer,其實(shí)就是一個(gè) 32bit 的數(shù),如 Color Buffer, Depth Buffer, Stencil Buffer. Stencil Buffer 與 Depth Buffer 有點(diǎn)特別,因?yàn)樗麄児灿猛粋€(gè) Buffer, Depth Buffer 占用 Buffer 前面的 24Bit, Stencil Buffer 占用后面的 8Bit. Stencil Buffer 可以使用從 1Bit-8Bit. 如在繪制反射時(shí),就像照鏡子一樣,因?yàn)橹恍枰诜瓷淦矫嫔侠L制物體的鏡像,即要么在反射平面上繪制,要不就不繪制,所以只需要用到 1Bit 的 Stencil Buffer.
什么叫 Stencil Buffer ?
即是一個(gè)模板,也就是說(shuō),他可以是一個(gè)平面,也可以是一個(gè)立體幾何圖形,如一個(gè)四邊形,一個(gè)Teapot. 在模板所占據(jù)的空間中,他的值為 1(values stored in the stencil buffer), 在啟用 Stencil Buffer 時(shí),我們所畫(huà)的圖形只有在這個(gè)空間中的部分才能顯示出來(lái),所以我們可以創(chuàng)建一個(gè)模板,他是一個(gè)字,然后以后畫(huà)的圖形最多只能把這個(gè)字給顯示出來(lái),這個(gè)圖形有其他部分都沒(méi)有被寫(xiě)進(jìn) Color Buffer.
Stencil Buffer 最簡(jiǎn)單的運(yùn)用,用來(lái)生成鏡面反射。
1. 先要使編程環(huán)境支持 Stencil Buffer
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
2. 設(shè)置清除 Stencil Buffer 使用的函數(shù)
glClearStencil(0);
3. 在我們創(chuàng)建模板的時(shí)候,要先關(guān)掉 Depth Test und Color Mask,
因?yàn)槲覀儾⒉幌氚涯0瀹?huà)到屏幕上
glDisable(GL_DEPTH_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
// 為了把我們的模板圖形不顯示到屏幕上,但又要寫(xiě)入 Stencil Buffer 中。
我們什么時(shí)候創(chuàng)建模板的?就是在啟用模板緩存后進(jìn)行的第一次進(jìn)繪制的圖形就是模板。
OpenGL會(huì)根據(jù)我們所設(shè)定的 glStencilFunc 的值和 glStencilOp 來(lái)比較,
然后在 plane 中(即視口所對(duì)應(yīng)的那個(gè)二維數(shù)組)寫(xiě)入比較的結(jié)果值。
4. 開(kāi)始創(chuàng)建模板
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 0x1, 0x1);
// 把模板圖形所在的區(qū)域的 Buffer 值設(shè)置成 1, 其余的還是 0.
// 這時(shí)用的就是給 glStencilFunc 指定的 ref 的值,現(xiàn)在是 1
// 當(dāng)然可以有其他的操作,如 GL_INCR, GL_INVERT(bitwise invert)
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
// 開(kāi)始創(chuàng)建模板的圖形
drawFloor();
// 模板創(chuàng)建好后,我們就要設(shè)置下一次進(jìn)行繪制時(shí)的模板函數(shù)
// 只有通過(guò)條件的像素才能被顯示到屏幕上,否則就被丟棄
// 但要注意,現(xiàn)在我們要進(jìn)行繪制的就是鏡像了,所以是要被顯示到屏幕上的,
// 所以在繪制之前,要把顏色屏蔽關(guān)掉和啟用深度測(cè)試
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_DEPTH_TEST);
// Stencil buffer 值等于 1 的地方才繪制到屏幕上
glStencilFunc(GL_EQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
5. 繪制鏡像圖像
// 現(xiàn)在繪制我們的鏡像圖像
// 鏡像是跟原來(lái)的物體對(duì)稱(chēng)的, 所以用 glScalef 來(lái)進(jìn)行反轉(zhuǎn),實(shí)現(xiàn)對(duì)稱(chēng)
// 在繪制鏡像物體的時(shí)候,燈光也要相應(yīng)的反轉(zhuǎn)
glPushMatrix();
glScalef(1, -1, 1);
glutSolidTeapot(1.0f);
glPopMatrix();
6. 在模板中顯示的鏡像圖像已經(jīng)創(chuàng)建好,不再需要模板了,所以我們關(guān)掉 stencil buffer
glDisable(GL_STENCIL_TEST);
7. 繪制鏡像所在的平面,就如鏡子
// 使用 Blend 與鏡像圖像混合起來(lái)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawFloor();
glDisable(GL_BLEND);
8. 繪制產(chǎn)生鏡像的物體
glutSolidTeapot(1.0f);
至此,真正的鏡面反射已經(jīng)創(chuàng)建完成。
非真正的反射可以如下實(shí)現(xiàn):
先畫(huà)對(duì)稱(chēng)物體,畫(huà)出鏡面(使用 Blend), 然后畫(huà)出原物體,但這時(shí)如果旋轉(zhuǎn)Camera,就會(huì)發(fā)現(xiàn),那個(gè)對(duì)稱(chēng)的物體并不是平面的,還是原來(lái)的空間立體物體。但用 Stencil Buffer 實(shí)現(xiàn)的鏡面反射是真正的鏡面反射,鏡像是只在鏡面上顯示,即是平面的。
下面的代碼可以很好的工作
//****************************************************************//
if (useStencil) {
glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
} else {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
if (useStencil) {
glDisable(GL_DEPTH_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
/* Draw 1 into the stencil buffer. */
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
/* Now render floor; floor pixels just get their stencil set to 1. */
drawFloor();
/* Re-enable update of color and depth. */
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_DEPTH_TEST);
/* Now, only render where stencil is set to 1. */
glStencilFunc(GL_EQUAL, 1, 0xffffffff); /* draw if ==1 */
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
glPushMatrix();
glScalef(1, -1, 1);
glTranslatef(0.0, 0.8, 0);
glColor3f(0, 1, 0);
glutSolidTeapot(1);
glPopMatrix();
if (useStencil) {
glDisable(GL_STENCIL_TEST);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(0.7, 0.0, 0.0, 0.3);
drawFloor();
glDisable(GL_BLEND);
glTranslatef(0, -0.0001, 0);
glFrontFace(GL_CW);
glColor3f(1, 1, 1);
drawFloor();
glTranslatef(0.0, 0.8, 0);
glColor3f(0, 1, 0);
glutSolidTeapot(1);
//****************************************************************//