OpenGL(九) 三維混色和深度快取設定
顏色的混合在現實世界中非常常見,例如隔著有色玻璃觀看物體,此時在觀察者嚴重呈現出來物體的顏色就是玻璃的顏色和物體的顏色的混合。
OpenGL在RGBA顏色模式下使用函式glenable(GL_BLEND)開啟混色功能,使用glDisable(GL_BLEDN)關閉混色功能。混色功能開啟之後,最終顯示的顏色的RGBA分量是兩個顏色各自的RGBA分量共同決定的。
源因子和目標因子
需要混合的兩個顏色一個稱為源顏色,一個稱為目標顏色,而定義一個顏色是源顏色還是目標顏色的唯一依據是:先繪製的顏色是“目標顏色”,後繪製的是“源顏色”,跟物體的深度無關。
混色運算的實現是源顏色和目標顏色分別乘上一個“源因子”和“目標因子”後經過算術運算實現的。使用函式glBlenFunc來設定源因子和目標因子。函式原型是:
void glBlendFunc (GLenum sfactor, GLenum dfactor);
第一個引數用於設定源因子,第二個引數用於設定目標因子,這兩個引數可以是多種值,下邊是一些比較常用的設定:
- GL_ZERO:表示使用0.0作為因子,實際上相當於不使用這種顏色參與混合運算。
- GL_ONE: 表示使用1.0作為因子,實際上相當於完全的使用了這種顏色參與混合運算。
- GL_SRC_ALPHA:表示使用源顏色的alpha值來作為因子。
- GL_DST_ALPHA:表示使用目標顏色的alpha值來作為因子。
- GL_ONE_MINUS_SRC_ALPHA:表示用1.0減去源顏色的alpha值來作為因子。
- GL_ONE_MINUS_DST_ALPHA:表示用1.0減去目標顏色的alpha值來作為因子。
如果不對源因子和目標因子進行設定,預設情況是採用設定glBlendFunc(GL_ONE,GL_ZERO),即完全使用源顏色,覆蓋目標顏色。
最常用的設定是glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);這種情況表示源顏色乘以自身的Alpha值,目標顏色乘以1.0減去源顏色的Alpha值,這樣一來,源顏色的Alpha值越大,則產生的新顏色中源顏色所佔比例就越大,而目標顏色所佔比例則減少。這種情況相當於使用源顏色Alpha的數值大小簡單模擬玻璃(源顏色)的不透明度。
深度快取
深度快取記錄了每一個畫素距離觀察點的距離,啟用深度快取功能之後,程式會忠實的執行只繪製最上層畫素點,覆蓋比最上層畫素點深度更深的畫素點,而顏色透明度則被忽略。
要解決這一問題,需要在繪製半透明模型之前“凍結”深度快取區,將深度快取區設定為只讀的,這樣一來,雖然半透明物體被繪製上去了,深度快取區還保持在原來的狀態,即當前繪製的模型的深度不會影響到深度緩衝區的狀態。等繪製完所有需要混色的模型之後,再把深度快取區設定為可讀可寫狀態。
呼叫glDepthMask(GL_FALSE),可以把深度快取區設定為只讀形式,使用glDepthMask(GL_TRUE)將深度快取區設定為可讀可寫形式。
示例
以下程式的功能是在三維空間中繪製3個平面,顏色分別是RGB,R在最下邊,G在中間,B在最上層,實現3個顏色的混色,可以看到,RGB三者重合的區域混合出了白色:
程式程式碼:
#include <glut.h>
GLfloat angle = 0.0f;
void myDisplay(void)
{
//List 1 定義模型檢視矩陣
if(!glIsList((GLuint)1))
{
glNewList(1,GL_COMPILE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60,1,2,50);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0,-10,10,0,0,0,0,1,0);
glEndList();
}
glEnable(GL_DEPTH_TEST); //深度快取
glEnable(GL_BLEND); //開啟混色
//設定源因子和目標因子
glBlendFunc(GL_ONE,GL_ONE);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glCallList(1);
glDepthMask(GL_FALSE); //設定深度緩衝區為只讀模式
glRotatef(angle,1,0,0);
glRotatef(angle,0,1,0);
glRotatef(angle,1,0,1);
glBegin(GL_QUADS);
//最底層的面 先繪製的是目標,紅色框
glColor4f(1,0,0,0.5);
glVertex3f(-2,-5,-5);
glVertex3f(8,-5,-5);
glVertex3f(8,5,-5);
glVertex3f(-2,5,-5);
//中間的面,第二繪製的源1 綠色框
glColor4f(0,1,0,0.5);
glVertex3f(-4.5,-5,-2);
glVertex3f(5.5,-5,-2);
glVertex3f(5.5,5,-2);
glVertex3f(-4.5,5,-2);
//頂層的面,第三繪製的源2 藍色框
glColor4f(0,0,1,0.5);
glVertex3f(-7,-5,1);
glVertex3f(3,-5,1);
glVertex3f(3,5,1);
glVertex3f(-7,5,1);
glEnd();
glDepthMask(GL_TRUE); //恢復深度緩衝區讀寫
glutSwapBuffers();
}
void myIdle(void)
{
angle+=0.2f;
if( angle >= 360.0f )
angle = 0.0f;
myDisplay();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(200, 200);
glutInitWindowSize(400, 400);
glutCreateWindow("OpenGL");
glutDisplayFunc(&myDisplay);
glutIdleFunc(&myIdle);
glutMainLoop();
return 0;
}