半透明在游戏中通常用来呈现若隐若现的特殊效果。事实上这种效果的运用相当频繁,比如薄雾、鬼魂或隐形任务等,有时会以半透明的手法来表现。本篇随笔就来介绍半透明效果的制作方法,下图23是一张位图经过半透明处理后显示在背景上的效果。
1、半透明的制作原理 简单地说,半透明效果就是前景图案与背景图案像素颜色的混合。从图23中观察半透明效果呈现的区域,可以看到背景图案,也可看到前景的人物图案。什么是前景图案与背景图案像素颜色的混合呢?这就要从位图的基本结构开始谈起了。
一张位图是由许多的像素所组成的,每一个像素中都包含红(R)、绿(G)、蓝(B)三原色的色彩值,由这三种原色值来决定该像素的色彩。而要呈现半透明效果,必须将前景图与背景图彼此对应像素的颜色依某一比例来进行调配,这个比例就叫做“不透明度”。
以没有进行半透明处理,单纯地将一张前景图贴到背景图上的一块区域来说,前景图的不透明度是100%,而背景图在这一块区域上的不透明度则是0%(完全透明,所以看不见背景),也就是说在这块区域上,背景图的色彩完全派不上用场。
可是如果想要有半透明的效果,让前景图看起来稍微透明一点,那就需要确定不透明度的值。假设确定不透明度是70%,也就是说前景图像素颜色按照一定的不透明度比例进行合成,那么最后整个区域所呈现出来的就是所要的半透明效果了。综合上面的说明,可以整理出一个建档的公式如下:
半透明图色彩 = 前景图色彩 × 不透明度 + 背景图色彩 ×(1 - 不透明度)2、半透明的操作步骤 清楚了半透明制作的原理后,接下来说明程序产生半透明效果的实际步骤。
步骤一:取得位图结构
位图结构包含了一些位图的基本信息,由于我们在制作半透明效果时会用到,因此在从文件加载位图后,必须先取得该位图的结构,而取的位图结构的函数如下:
int GetObject( HGDIOBJ GDI对象, //取得GDI对象结构
int 结构大小,
LPVOID 结构变量,);
上面这个函数用于取得GDI对象的信息,包含这里所谈的位图,其中第3个参数是一个结构变量,如果是用于取得位图的信息,则输入一个位图结构的地址,而Windows API所定义的位图结构(BITMAP)如下:
typedef struct tagBITMAP{
LONG bmType; // 位图类型,必须设为0
LONG bmWidth; // 位图宽度
LONG bmHeight; //位图长度
LONG bmWidthBytes; //每一列像素所占Byte数
WORD bmPlanes; //颜色平面数
WORD bmBitsPixel; //像素的位数
LPVOID bmBits; //位图内存指针
}BITMAP;
后面将会用到bmWidth、bmHeight、bmWidthBytes及bmBitsPixel这几个结构成员的信息。
在此举个例子来说明取得位图结构的方法,假设现在有一个位图名称为“bitmap”,位图结构变量名称为“bm”,则使用GetObject()函数取得BITMAP结构的程序代码如下:
GetObject(bitmap,sizeof(BITMAP),&bm);
这样,位图结构bm中的各个结构成员便包含了位图bitmap的基本信息。
步骤二:建立暂存数组 取得位图的结构,接下来必须先建立一个暂存数组准备存储位图中所有像素的颜色值。这个暂存数组的大小是由前一个步骤中所取得位图的bmHeight与bmWidthBytes信息来决定的,因此,必须利用指针来动态建立。延续前一个例子,若要建立一个可存储bitmap所有像素颜色值的暂存数组,程序代码如下:
unsigned char *px = new unsigned char [bm.bmHeight * bm.bmWidthBytes];
这里,因为unsigned char变量类型大小是1Byte,所以这个数组的每个元素大小也就是1Byte(8bits)。以一张24bits色彩的位图来说,它的每个像素是以24bits来表示颜色的,其中B(蓝)、G(绿)、R(红)三原色各占8个bits。
因此,在下面的步骤中,当取出位图的所有颜色并存储在这个数组中时,每一个像素会占用3个数组元素来存储B、G、R的颜色值。
步骤三:取得位图位置 建立了暂存数组之后,要取出位图的所有颜色值存储到数组中就简单多了,有一个现成的API函数可以使用。
LONG GetBitmapBits( HBITMAP 位图, // 取得位图位值
LONG 要取得的Byte数,
LPVOID 存储的数组指针);
使用此函数取得位图位值的程序代码如下。
GetBitmapBits(bitmap, bm.bmHeight * bm.bmWidthBytes, px);
下面以图来说明像素颜色值存储在数组中的对应关系,如下图所示:
步骤四:合成像素颜色值
取得了位图的所有像素颜色值之后,接下来的工作就是按照不透明度来设定半透明区域内每个像素的颜色了。
此时应该会有两个像素颜色数组,一个是前景图的,一个则是背景图的。根据实际要显示半透明区域的坐标,将这两个数组算出对应的元素进行签名讲过的颜色合成运算,再存回暂存数组中,anemia数组中所存储的便是已经完成半透明的颜色值了。这个步骤的实际处理过程,将在代码中做详细说明。
步骤五:重设位图颜色 处理完暂存数组中半透明的颜色值之后,最后一个操作就是根据数组的内容来重设位图的颜色。这个操作同样可以使用一个API的函数来完成。
LONG SetBitmapBits( HBITMAP 位图, //设定位图位值
DWORD 颜色数组大小,
CONST VOID 数组指针 );//为什么是CONST VOID?
以上5个步骤都完成之后,所要的半透明图也就完成了,剩下的就只有贴图操作了。
范例ch2_7:取得前景图与背景图的颜色值,以前景图的不透明度30%和背景图的不透明度70%进行半透明处理,制作半透明效果。
下载地址:
ch2_7(上传到windows live空间,可能需要MSN账号登陆)
说明:程序源代码中有相关的注释。
程序运行结果如下图:
PS:如想获得更多关于Visual C++游戏开发的内容,可点击随笔
:《Visual C++游戏编程基础》学习笔记——索引随笔。