雾也是大自然中最常见的现象之一,有了雾的效果,场景看起来会更加真实。在OpenGL中,很容易实现雾的效果。
在OpenGL中,雾的工作模式有两种:线性模式和指数模式。这两种模式是根据雾的浓度变化来区分的。
在线性模式下,只需要提供一个距离视点的开始位置和结束位置。从开始位置到结束位置之间,雾的浓度越来越高,浓度的变化和距离成正比。
在指数模式下,雾的浓度随着距离的增加呈指数增长。这种模式通常用来用于烟雾、烟幕等效果。
启用雾化的效果必须使用glEnable(GL_FOG),同样,取消雾的效果就是glDisable(GL_FOG)。启用雾化后,还需要设置雾的参数才能够真正实现雾的效果。设置雾的参数的命令是glFog,其原型如下。
void glFogf(
GLenum pname,
GLfloat param
);
void glFogi(
GLenum pname,
GLint param
);
void glFogfv(
GLenum pname,
const GLfloat *params
);
void glFogiv(
GLenum pname,
const GLint * params
);
pname 表示需要设置的雾的参数,取值及含义说明如下表9-1所示。
表9-1 glFog参数取值含义
pname取值 | 对应params含义 |
GL_FOG_MODE | params参数表示雾的工作模式。可以取三个值:GL_LINEAR, GL_EXP, and GL_EXP2,前者表示线性模式,后两种表示指数模式。缺省取值为GL_EXP。 |
GL_FOG_DENSITY | params参数表示雾的密度,用于指数模式的指数。如果是负值,则被忽略,缺省值为1.0。 |
GL_FOG_START | params参数表示雾化效果的开始距离,用于线性模式中。缺省值为0.0。 |
GL_FOG_END | params参数表示雾化效果的结束距离,用于线性模式中,缺省距离为1.0。 |
GL_FOG_INDEX | params参数表示在颜色索引表的颜色模式下,颜色的索引值if,缺省的索引值为0.0。 |
GL_FOG_COLOR | 仅用于glFogfv 和 glFogiv 函数,此时params参数表示雾的颜色Cf的向量RGBA值。不论取值是浮点数还是整数,最终的颜色值都映射到[0,1]范围。缺省的值为(0,0,0,0)。 |
经过雾化后,从观察者的角度来看,物体的颜色和其本身的颜色已经不同,这种差别是由混合因子来确定的,而混合因子又来自于雾的各参数。
假设z是从原点到在物体的距离,start是雾化的开始距离,end是雾化的结束距离,density是雾的密度指数,则在线性模式下,混合因子f为
在指数模式下有两种情况,当pname取值为GL_FOG_MODE,param取值为GL_EXP时,混合因子f为
当pname取值为GL_FOG_MODE,param取值为GL_EXP2时,混合因子f为
不论采取哪种雾化模式,f 最后都会被映射到[0, 1]之间。计算出f 之后,在RGBA颜色模式下,原来某像素的颜色是 ,雾的颜色为 , 则雾化后该像素的颜色就变成
在颜色索引表模式下,原来某像素的颜色索引为 ,雾的颜色索引为 ,则雾化后
该像素的颜色索引就变成
下面我们来在场景中增加雾的效果。
//global variant
GLuint fogMode[]={GL_LINEAR, GL_EXP, GL_EXP2};
GLuint fog=0; //缺省模式为线性模式
BOOL bFog = TRUE; //缺省为启动雾化效果
int glInit()
{
// 启用纹理映射
glEnable(GL_TEXTURE_2D);
//启用阴影平滑(Smooth Shading)。
glShadeModel(GL_SMOOTH);
//使用深灰色背景
glClearColor(0.5f,0.5f,0.5f,0.0f);
//设置深度缓冲
glClearDepth(1.0f);
//启动深度测试
glEnable(GL_DEPTH_TEST);
//深度测试的类型
glDepthFunc(GL_LEQUAL);
//进行透视修正
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
//以baby.bmp文件创建一个纹理
g_Texture[0] = CreateTexture("baby.bmp");
if(!g_Texture[0]) //创建失败则返回
{
MessageBox(g_hWnd, "创建纹理失败!", "提示", MB_OK);
return FALSE;
}
//启动雾化效果
glEnable(GL_FOG);
//雾的颜色
float fogColor[4] = {0.5f,0.5f,0.5f,1.0f};
//雾化效果使用线性模式
glFogi(GL_FOG_MODE, GL_LINEAR);
//RGBA模式,设置雾的颜色
glFogfv(GL_FOG_COLOR, fogColor);
//雾的浓度,用于指数模式中
glFogf(GL_FOG_DENSITY, 0.1f);
// 雾的计算精度,使用GL_DONT_CARE或GL_FASTEST可以针对每个像素
// 的雾的计算进行调整,提高计算速度。
glHint(GL_FOG_HINT, GL_DONT_CARE);
// 雾化开始距离
glFogf(GL_FOG_START, 0);
// 雾化结束距离
glFogf(GL_FOG_END, 20.0f);
return TRUE;
}
void glMain()
{
static int angle =0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //加载单位矩阵
glTranslatef(0.0f, 0.0f, -25.0f);
glRotated(angle, 1, 1, 0);
//设置雾化模式
glFogi(GL_FOG_MODE, fogMode[fog]);
//根据bFog的值确定是否启动雾化效果
if(!bFog)
glDisable(GL_FOG);
else
glEnable(GL_FOG);
glBindTexture(GL_TEXTURE_2D, g_Texture[0]); // 选择纹理
//10个带纹理贴图的立方体
for(int i=0; i<10; i++)
{
glPushMatrix() ;
glRotatef(36*i,0.0,1.0,0.0) ;
glTranslatef(10.0,0.0,0.0) ;
DrawCube();
glPopMatrix();
}
angle++;
SwapBuffers(g_hDC);//交换前后缓冲区
}
对于雾化模式的控制和启动通过按键来实现,这一部分代码放在WindProc中。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_ACTIVATE:
{
if (!HIWORD(wParam))
{
g_bActive=TRUE;
}
else
{
g_bActive=FALSE;
}
return 0;
} // 监视窗口激活消息
case WM_SIZE:
{
glSceneResize(LOWORD(lParam),HIWORD(lParam));
return 0;
}
//通过按键改变雾的工作模式
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE: //如果按下了ESC键就退出
PostQuitMessage(0);
return 0;
case VK_SPACE: //空格键进行雾化模式的切换
if(fog==2)
fog=0;
else
fog++;
break;
case VK_F1: //F1键确定是否启动雾化效果
bFog= !bFog;
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
程序运行后可以看到雾的效果非常明显,距离越远的立方体越模糊,最远的一个已经全然隐没在雾中。F1键可以就是否进行雾化进行切换,空格键则在三种雾化模式间进行变换,ESC键退出。同时也可以看到,在同样的密度下,GL_EXP2比GL_EXP的雾要更加浓厚一些。运行效果如图9-1所示。