OpenGL系统设计-雾与混合(2)

 

在高级三维建模中都少不了Alpha,它作为RGBA颜色模型中的一个分量,表征了物体的透明度。和RGB取值范围一样,Alpha取值从0.01.00.0表示完全透明,1.0表示完全不透明。

鉴于Alpha的性质,它可以用于模拟玻璃、水。也可以混合合成图像,对几何图元进行反走样处理。

混合是OpenGL提供的基于像素级的颜色操作,它只支持RGBA颜色模式。在颜色索引表模式下,混合是无效的。

混合是将像素和片元融合起来产生一个新的像素颜色。如果片元是处在混合状态的,那么OpenGL先从帧缓冲中读出像素的颜色,然后和片元的颜色混合,再写回帧缓冲中。

启动和禁用混合效果需要使用glEnable(GL_BLEND)glDisable(GL_BLEND)OpenGL缺省状态为禁用混合。

针对混合,片元和像素均有一个因子来控制各自对最终像素颜色的贡献。可以使用glBlendFunc来设置这些因子。glBlendFunc指定了像素混合计算方法,其原型如下:

 

void glBlendFunc(
  GLenum sfactor,  
  GLenum dfactor   
);
 

sfactor参数表示红、绿、蓝、Alpha混合源值的计算方式,共有9种方式

GL_ZERO

GL_ONE

GL_DST_COLOR

GL_ONE_MINUS_DST_COLOR

GL_SRC_ALPHA

GL_ONE_MINUS_SRC_ALPHA

GL_DST_ALPHA

GL_ONE_MINUS_DST_ALPHA

GL_SRC_ALPHA_SATURATE

dfactor参数表示红、绿、蓝、Alpha混合目标值的计算方式,共有8种方式

GL_ZERO

GL_ONE

GL_SRC_COLOR

GL_ONE_MINUS_SRC_COLOR

GL_SRC_ALPHA

GL_ONE_MINUS_SRC_ALPHA

GL_DST_ALPHA

GL_ONE_MINUS_DST_ALPHA

 

glBlendFunc()定义了混合操作的方式,sfactor 参数指定了用于缩放源颜色值的方法,dfactor 参数指定了用于缩放目标颜色值的方法,共有11种可能,每一种方式都定义了4个缩放因子,分别对应红色、绿色、蓝色和Alpha分量。

假设颜色的源值和目标值分别是(RS,GS,BS,AS)(Rd,Gd,Bd,Ad),其颜色数目是一个整数,为(kR,kG,kR,kA),其中

kR = 2mR – 1

kG = 2mG – 1

kB = 2mB – 1

kA = 2mA – 1

 

其中(mR ,mG ,mB ,mA) 是红、绿、蓝、Alpha的颜色位数,如256色的8位、16M色的16位,真彩色的24位和32位等。

如果用(sR ,sG ,sB ,sA) (dR ,dG ,dB ,dA)分别表示源因子和目标因子,则下表9-2列出了源因子或目标因子的取值,所有的因子取值范围均是[0,1]

 

9-2     混合方式

源因子/目标因子

(f (R) ,f (G) ,f (B) ,f (A) )

GL_ZERO

(0,0,0,0)

GL_ONE

(1,1,1,1)

GL_SRC_COLOR

(RS/kR,GS/kG,BS/kB,AS/kA)

GL_ONE_MINUS_SRC_COLOR

(1,1,1,1) - (RS/kR,GS/kG,BS/kB,AS/kA)

GL_DST_COLOR

(Rd/kR,Gd/kG,Bd/kB,Ad/kA)

GL_ONE_MINUS_DST_COLOR

(1,1,1,1)

GL_SRC_ALPHA

(Rd/kR,Gd/kG,Bd/kB,Ad/kA) - (AS/kA,AS/kA,AS/kA,AS/kA)

GL_ONE_MINUS_SRC_ALPHA

(1,1,1,1) - (AS/kA,AS/kA,AS/kA,AS/kA)

GL_DST_ALPHA

(AD/kA,AD/kA,AD/kA,AD/kA)

GL_ONE_MINUS_DST_ALPHA

(1,1,1,1) - (AD/kA,AD/kA,AD/kA,AD/kA)

GL_SRC_ALPHA_SATURATE

(i,i,i,1)

 

在表9-2

i = min (AS,kAAD) / /kA

 

RGB模式下,OpenGL使用下面的公式计算像素的混合RGBA值。

 

R (d) = min(kR,RssR+RddR)

G (d) = min(kG,GssG+GddG)

B (d) = min(kB,BssB+BddB)

A (d) = min(kA,AssA+AddA)

 

事实上,混合算法并不是很精确,因为混合操作使用的是整数颜色值。为了简便起见,通常使用(RssR+RddR, GssG+GddG, BssB+BddB, AssA+AddA)来作为混合后的颜色值。例如,针对由远及近绘制的的物体,实现透明效果可以使用glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)需要注意的是,透明度的计算不需要用到帧缓冲中的Alpha分量。

如果需要实现点线任意方向的反走样,可以使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 。使用glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE)还可以对多边形进行反走样处理。在进行多边形反走样处理前还必须使用glEnable(GL_POLYGON_SMOOTH)启用多边形的光滑绘制。

为了达到正确混合,目标Alpha位必须有效,否则混合无法实现。源Alpha还可以看作是材质的不透明性,范围从1.00.0,表示完全不透明到完全透明。

下面我们来对纹理贴图的立方体进行混合处理。

 

//global variant

GLfloat z = -10.0f;         //立方体在z轴的距离

BOOL bBlend = TRUE;         //是否使用混合的标志

 

int glInit()

{

   

    // 启用纹理映射

    glEnable(GL_TEXTURE_2D);

   

    //启用阴影平滑(Smooth Shading)

    glShadeModel(GL_SMOOTH);

   

    //背景色,alpha为半透明

    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);

   

    //设置深度缓冲

    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_EXP2);                  

   

    glFogfv(GL_FOG_COLOR, fogColor);               

   

    glFogf(GL_FOG_DENSITY, 0.1f);      

 

    // 雾的计算精度

    glHint(GL_FOG_HINT, GL_DONT_CARE);                 

 

    glFogf(GL_FOG_START, 0);                           

 

    glFogf(GL_FOG_END, 30.0f);                     

   

    //启动混合

    glEnable(GL_BLEND);

   

    //设置混合颜色

    glColor4f(1.0f, 1.0f, 1.0f, 0.5f);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

 

    return TRUE;

}

 

void glMain()

{

   

    static int angle =0;

   

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();   //加载单位矩阵

    glTranslatef(0.0f, 0.0f, z);

 

    glRotated(angle, 1, 1, 1);

 

    glFogi(GL_FOG_MODE, fogMode[fog]);

   

    if(!bFog)

        glDisable(GL_FOG);

    else

        glEnable(GL_FOG);

   

    //检查混合标志

    if(!bBlend)

    {

        glDisable(GL_BLEND);

        glEnable(GL_DEPTH_TEST);

    }

    else

    {

        glEnable(GL_BLEND);

        glDisable(GL_DEPTH_TEST);

    }

 

    glBindTexture(GL_TEXTURE_2D, g_Texture[0]); // 选择纹理

   

    DrawCube();

 

    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:

            PostQuitMessage(0);

            return 0;

        case VK_SPACE:

            if(fog==2)

                fog=0;

            else

                fog++;

            break;

        case VK_F1:

            bFog= !bFog;

            break;

 

        case VK_F2:         //F2键切换是否使用混合

            bBlend=!bBlend;

            break;

 

        case VK_UP:         //向上方向键拉近立方体和观察者的距离

            z+=0.2;

            break;

        case VK_DOWN:       //向下方向键拉远立方体和观察者的距离

            z-=0.2;

            break;

 

        }

        break;

       

    case WM_DESTROY:

        PostQuitMessage(0);

        break;

 

        default:

        return DefWindowProc(hWnd, message, wParam, lParam);

    }

    return 0;

}

 

程序运行后,可以使用F2键来切换是否使用混合,按向上的方向键和向下的方向键则改变立方体和观察者的距离,雾的效果仍存在有效,如果9-2所示。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值