15.1、融合
15.1.1 Alpha值与融合(Blending)
Alpha值在前面几章中已经提到过,但是几乎所有例程都将它设置为1.0,没有详细讨论它为其它值时的情况。融合,是本章的重点,它是透明技术、数字合成和计算机绘画技术的核心。固名思义,融合就是指两种颜色各分量依据一定的比例混在一起合二为一。而这种比例就来源于Alpha值,即RGBA中的A或(r、g、b、a)中的a值,通常称a为不透明性,称(1-a)为透明性。因为在颜色表方式下不能说明a值,因此融合操作不能在颜色表方式下进行。
为了更好地理解这些概念,我们可以举出一个例子来简要说明。例如,坐在汽车内透过车窗的茶色玻璃看车外的绿树,这些树木的颜色并不是它们本来的绿色,而是透明的茶色与不透明的绿色的混合颜色。也就是说,最终的颜色来源于两部分,一部分来自于玻璃的茶色,另一部分来自于树木的绿色。两部分所占的百分比依据玻璃的透射变化,若玻璃透射率为80%(即一束光照在其上有80%的透射过去),其不透明度为20%,即这里的a值就等于0.2,这样进入眼中的树木颜色是20%的玻璃颜色与80%的树木本身颜色的合成。
15.1.2 融合因子(Blending Factor)
在OpenGL的融合操作中,实际上包含了两个因子的操作,这两个因子就是源因子(Source Factor)和目的因子(Destination Factor)。从数学的角度来看,设源因子和目的因子分别为(Sr、Sg、Sb、Sa)与(Dr、Dg、Db、Da),则融合的最终结果是:
(Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)
|
并且其中每个元素值都约简到[0, 1]之间。在OpenGL中,由函数gjBlendFunc()来产生这两个融合因子的值。其函数形式为:
void glBlendFunc(GLenum sfactor,GLenum dfactor)
控制源因子和目的因子的结合。参数sfactor指明怎样计算源因子,dfactor指明怎样计算目的因子。这些参数的可能值见表15-1所示。注意:融合因子的值在[0,1]范围内,且两因子结合后也要约简到[0,1]内,源与目的的RGBA值分别带有s和d下标。
常数 |
相关因子 |
计算后得到的融合因子 |
GL_ZERO |
源因子或目的因子 |
(0,0,0,0) |
GL_ONE |
源因子或目的因子 |
(1,1,1,1) |
GL_DST_COLOR |
源因子 |
(Rd,Gd,Bd,Ad) |
GL_SRC_COLOR |
目的因子 |
(Rs,Gs,Bs,As) |
GL_ONE_MINUS_DST_COLOR |
源因子 |
(1,1,1,1)-(Rd,Gd,Bd,Ad) |
GL_ONE_MINUS_SRC_COLOR |
目的因子 |
(1,1,1,1)-(Rs,Gs,Bs,As) |
GL_SRC_ALPHA |
源因子或目的因子 |
(As,As,As,As) |
GL_ONE_MINUS_SRC_ALPHA |
源因子或目的因子 |
(1,1,1,1)-(As,As,As,As) |
GL_DST_ALPHA |
源因子或目的因子 |
(Ad,Ad,Ad,Ad) |
GL_ONE_MINUS_DST_ALPHA |
源因子或目的因子 |
(1,1,1,1)-(Ad,Ad,Ad,Ad) |
GL_SRC_ALPHA_SATURATE |
源因子 |
(f,f,f,1); f=min(As,1-Ad) |
表15-1 源因子和目的因子
|
当用函数glBlendFunc()说明了融合因子的产生后,需调用函数glEnable(GL_BLEND)来启动融合操作,不用时可调用glDisable(GL_BLEND)来关闭它。若源因子的参数为常数GL_ONE,目的因子的参数为常数GL_ZERO,则相当于关闭融合操作,这些值为缺省值。
15.1.3 融合实例
首先来看一个融合运用的简单例子:
例15-1 Alpha二维融合例程(blend2d.c)
#include #include #include #pragma comment(lib, "OpenGL32.lib") #pragma comment(lib, "GLU32.lib") #pragma comment(lib, "GlAux.lib") void myinit(void); void CALLBACK myReshape(GLsizei w, GLsizei h); void CALLBACK display(void); /* 初始化 alpha 融合的参数 */ void myinit(void) { glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glShadeModel (GL_FLAT); glClearColor (0.0, 0.0, 0.0, 0.0); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT); glColor4f (1.0, 0.0, 0.0, 0.7); glRectf (0.25, 0.4, 0.75, 0.9); glColor4f (0.0, 1.0, 0.0, 0.5); glRectf (0.1, 0.1, 0.6, 0.6); glColor4f (0.0, 0.0, 1.0, 0.3); glRectf (0.4, 0.1, 0.9, 0.6); glFlush(); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) gluOrtho2D (0.0, 1.0, 0.0, 1.0*(GLfloat)h/(GLfloat)w); else gluOrtho2D (0.0, 1.0*(GLfloat)w/(GLfloat)h, 0.0, 1.0); glMatrixMode(GL_MODELVIEW); } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA); auxInitPosition (0, 0, 500, 500); auxInitWindow (TEXT("Alpha 2D Blending")); myinit(); auxReshapeFunc (myReshape); auxMainLoop(display); }
以上程序运行结果是显示三个不同颜色(红、绿、蓝)的方块部分融合的效果。
|
图15-1 红绿蓝方块融合效果 |
15.2、反走样
反走样(Antialiasing),又叫反混淆,是计算机图形学中的一个重要概念。由于计算机生成的图形是由离散点组成的数字化图像,因而生成的图形必然与真实景物之间存在一定误差。这种误差表现为图形上的直线或光滑的曲线呈现锯齿状、彩色花纹失去原有的形态和色彩、细小物体在画面上得不到反映等等。这种锯齿就叫做走样。见图15-2所示,左边为走样线,右边为反走样线。
|
图15-2 走样线与反走样线 |
15.2.1 行为控制函数
在OpenGL中,许多细节的实现算法有所不同。这样,可以调用函数glHint()对图像质量和绘制速度之间的权衡作一些控制,但并非所有的实现都采用它。其函数形式为:
void glHint(GLenum target,GLenum hint);
控制OpenGL行为的某些方面。参数target说明控制什么行为,其可能值见表15-2所示。参数hint可以是:GL_FASTEST(即给出最有效的选择)、GL_NICEST(即给出最高质量的选择)、GL_DONT_CARE(即没有选择)。
参数 |
意义 |
GL_POINT_SMOOTH_HINT |
指定点、线、多边形的采样质量 |
GL_LINE_SMOOTH_HINT |
GL_POLYGON_SMOOTH_HINT |
GL_FOG_HINT |
指出雾的计算是按每个象素进行(GL_NICEST) 还是按每个顶点进行(GL_FASTEST) |
GL_PERSPECTIVE_CORRECTION_HINT |
指定颜色和纹理插值的质量 |
表15-2 函数glHint()参数及其意义
|
实际上,对于提示的解释依赖于OpenGL的具体实现,有时可以忽略。参数GL_PERSPECTIVE_CORRECTION_HINT用于指定一个图元中的颜色值和纹理值怎样进行插值,要么在屏幕空间进行插值,要么按照透视投影纠正方式进行插值(这种方式需更多计算)。通常,系统对颜色进行线性插值,虽然从原理和技术的角度来讲未必正确,但是人眼可以接受,且实现速度很快。然而纹理在大多数情况下,为了看起来可以接受,需要对其进行透视纠正插值。因此,可以用这个参数控制插值方法。同样,在进行反走样计算时,也要用到这个函数来控制图形实现的行为。
15.2.2 点和线的反走样
在OpenGL中虽然颜色表方式可以实现反走样,但建议最好用RGBA方式进行。不管何种方式,对图元进行反走样,首先要用glEnable()启动反走样(参数为GL_POINT、GL_LINE_SMOOTH或GL_POLYGON_SMOOTH),同时,也可以调用glHint()提供一个图像质量提示。但需注意的是,在RGBA方式下,必须启动混合,最可能用的混合因子是GL_SRC_ALPHA(源)和GL_ONE_MINUS_SRC_ALPHA(目的)。另外,可以使目的因子为GL_ONE,则线的交点处要亮一些。下面举出一个例子:
例15-2 反走样线例程(antiline.c)
#include #include #include #pragma comment(lib, "OpenGL32.lib") #pragma comment(lib, "GLU32.lib") #pragma comment(lib, "GlAux.lib") void myinit(void); void CALLBACK myReshape(GLsizei w, GLsizei h); void CALLBACK display(void); /* 初始化反走样为 RGBA 模式,同时包括alpha混合、提示、线宽等的设置。 */ void myinit(void) { glEnable (GL_LINE_SMOOTH); glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE); glLineWidth (5.0); glShadeModel(GL_FLAT); glClearColor(0.0, 0.0, 0.0, 0.0); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor4f (0.0, 0.6, 1.0, 1.0); auxWireOctahedron(1.0); glFlush(); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective (45.0, (GLfloat) w/(GLfloat) h, 3.0, 5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity (); glTranslatef (0.0, 0.0, -4.0); /* 将物体移至视见区内 */ glRotatef(15.0,1.0,1.0,0.0); } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA); auxInitPosition (0, 0, 400, 400); auxInitWindow (TEXT("Antialiased Lines Using RGBA")); myinit(); auxReshapeFunc (myReshape); auxMainLoop(display); }
以上程序运行结果是显示一个反走样的网状八面体。
|
图15-3 反走样线 |
15.2.3 多边形的反走样
填充多边形的反走样类似于点线的反走样,不同的只是将所有POINT或LINE的地方改为POLYGON而已。如下面的一个在RGBA模式下的多边形反走样例子:
例15-3 多边形反走样例程(antipoly.c)
#include #include #include #pragma comment(lib, "OpenGL32.lib") #pragma comment(lib, "GLU32.lib") #pragma comment(lib, "GlAux.lib") void myinit(void); void CALLBACK myReshape(GLsizei w, GLsizei h); void CALLBACK display(void); void myinit(void) { GLfloat mat_ambient[] = { 0.5, 0.5, 0.0, 1.00 }; GLfloat mat_diffuse[] = { 1.0, 0.8, 0.1, 1.0 }; GLfloat position[] = { 1.0, 0.0, 1.0, 0.0 }; glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE,mat_diffuse); glLightfv (GL_LIGHT0, GL_POSITION, position); glEnable (GL_LIGHTING); glEnable (GL_LIGHT0); glEnable (GL_BLEND); glCullFace (GL_BACK); glEnable (GL_CULL_FACE); glEnable (GL_POLYGON_SMOOTH); glDisable (GL_DEPTH_TEST); glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE); glClearColor (0.0, 0.0, 0.0, 0.0); } void CALLBACK display(void) { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glTranslatef (0.0, 0.0, -8.0); glRotatef (-45.0, 1.0, 0.0, 0.0); glRotatef (45.0, 0.0, 1.0, 0.0); auxSolidIcosahedron (1.0); glFlush (); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(30.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_ALPHA ); auxInitPosition (0, 0, 400, 400); auxInitWindow (TEXT("Antialiased Polygons")); myinit(); auxReshapeFunc (myReshape); auxMainLoop(display); }
以上程序运行结果是显示一个黄色的填充反走样二十面体。
|
图15-4 反走样多边形 |
15.3、雾
15.3.1 雾的概论和例程
雾化效果在当今的计算机图形学中应用极广,它不仅可以使场景中的物体看起来更加真实,而且还可提高绘制速度。在很多情况下,计算机图像有时会出现不符合实际的精细和棱角分明,上一节的反走样技术可以通过光顺着物体的边界反走样,使其看起来更真实,这一节的雾化处理可以使物体看起来更加自然,即在雾中,离视点远的物体会变得模糊。
雾,是一个描述类似于大气效果的一般术语,在视景仿真中应用很广。它可以模拟烟雾(haze)、薄雾(mist)、浓烟(smoke)和污染(pollution)等效果。当启动雾后,离视点较远的物体开始淡化成雾的颜色,同时,雾的浓度可以控制,即随着距离的增加物体变淡的速率可控,雾的颜色也可以任意设定。雾在两种颜色方式下都能使用。下面举出一个在RGBA方式下使用雾的例程:
例15-4 RGBA方式下雾化例程(fogrgb.c)
#include #include #include #include #include #pragma comment(lib, "OpenGL32.lib") #pragma comment(lib, "GLU32.lib") #pragma comment(lib, "GlAux.lib") void myinit(void); void CALLBACK myReshape(GLsizei w, GLsizei h); void CALLBACK display(void); void myinit(void) { GLfloat mat_ambient[] = { 0.7, 0.6, 0.4, 1.00 }; GLfloat mat_diffuse[] = {0.7,0.0,0.99,1.0}; GLfloat mat_specular[] = { 1.0, 0.0, 1.0, 1.00 }; GLfloat mat_shininess[] = { 15.0 }; GLfloat position[] = { 5.0, 5.0, 5.0, 1.0 }; GLfloat fogColor[4] = {0.6, 0.6, 0.6, 1.0}; glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glLightfv(GL_LIGHT0, GL_POSITION, position); glFrontFace (GL_CW); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_FOG); { glFogi (GL_FOG_MODE, GL_LINEAR); glFogfv (GL_FOG_COLOR, fogColor); glFogf (GL_FOG_START, 3.0); glFogf (GL_FOG_END,15.0); glHint (GL_FOG_HINT, GL_DONT_CARE); glClearColor(0.3, 0.3, 0.3, 1.0); } } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glTranslatef(-3.0,-1.5,-2.0); auxSolidTorus(0.6,1.5); glPopMatrix(); glPushMatrix(); glTranslatef(-0.5,-0.5,-7.0); auxSolidTorus(0.6,1.5); glPopMatrix(); glPushMatrix(); glTranslatef(2.0,0.8,-10.0); auxSolidTorus(0.6,1.5); glPopMatrix(); glFlush(); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= (h*3)) glOrtho (-6.0, 6.0, -2.0*((GLfloat) h*3)/(GLfloat) w, 2.0*((GLfloat) h*3)/(GLfloat) w, 0.0, 10.0); else glOrtho (-6.0*(GLfloat) w/((GLfloat) h*3), 6.0*(GLfloat) w/((GLfloat) h*3), -2.0, 2.0, 0.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity (); } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA); auxInitPosition (0, 0, 500, 400); auxInitWindow (TEXT("Fog_RGB_mode")); myinit(); auxReshapeFunc (myReshape); auxMainLoop(display); }
以上程序运行结果是显示三个不同远近的蓝紫色环形圈在雾中的效果,这里的雾化计算采用线性方式(GL_LINEAR)。
|
图15-5 雾化效果 |
15.3.2 雾化步骤
在OpenGL程序中使用雾化效果非常容易。其步骤有三,如下:
1)启动雾。函数调用为glEnable(GL_FOG);
2)控制雾。函数调用为glFog*(),用它选择控制浓度和颜色的雾方程,其具体形式为:
void glFog{if}[v](GLenum pname,TYPE param);
设置计算雾的参数和函数。若pname为GL_FOG_MODE,则param可以是GL_EXP(缺省)、GL_EXP2或GL_LINEAR,分别代表三个雾因子。若pname为GL_FOG_DENSITY、GL_FOG_START或GL_FOG_END,则param为雾方程中对应的density、start和end的值。缺省值为1、0、1。在RGBA方式下,pname可以是GL_FOG_COLOR,此时参数param为指向含有RGBA值的向量指针。同样,在颜色表方式下,pname相应值为GL_FOG_INDEX,其对应的param是雾的颜色索引值。
3)若有必要,可用函数glHint(GL_FOG_HINT)提供一个值。