Alpha顏色混合的魔法 上篇
摘要:本系列文章介紹了一種在影象處理、2D遊戲、3D遊戲中經常使用的圖片混合模型:Alpha顏色混合;
它就像神奇的魔法一樣,在電腦螢幕上給我們展現出一個個絢麗多彩的世界!
全文 分為: 上篇 各種Alpha顏色混合方式
下篇 其他一些顏色混合方案、補充
tag:Alpha,Blend,透明,顏色混合,顏色混合公式
正文:
為了便於討論,這裡只處理32bit的ARGB顏色;
程式碼使用C++,編譯器:VC2005
(文章中的效果圖片都是用給出的例子程式碼實際生成)
A: 一些顏色和圖片的資料定義:
#define asm __asmtypedef unsigned
TUInt8 b,g,r,a; //a is alpha};
struct TPicRegion //一塊顏色資料區的描述,便於引數傳遞{
TARGB32* pdata; //顏色資料首地址long byte_width; //一行資料的物理寬度(位元組寬度);
//abs(byte_width)有可能大於等於width*sizeof(TARGB32);long width;
//那麼訪問一個點的函式可以寫為:inline TARGB32& Pixels(const TPicRegion& pic,constlong x,constlong y)
{
return ( (TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*y) )[x];
}
B: 混合兩幅圖片
這裡簡單的按照50%的比例混合兩幅圖片;演算法也很簡單,就是將顏色分量直接
相加,然後取平均值;函式如下:
{
long width =min(picDst.width ,picSrc.width );
long height=min(picDst.height,picSrc.height);
for (long y=0;y<height;++y)
{
for (long x=0;x<width;++x)
{
TARGB32& DstColor=Pixels(picDst,x,y);
TARGB32 SrcColor=Pixels(picSrc,x,y);
DstColor.b=(DstColor.b + SrcColor.b)/2;
DstColor.g=(DstColor.g + SrcColor.g)/2;
DstColor.r=(DstColor.r + SrcColor.r)/2;
DstColor.a=(DstColor.a + SrcColor.a)/2;
}
}
}
函式效果:
混合前源圖片0 混合前源圖片1
PicBlend_half混合後的結果圖片
C.按比例混合兩幅圖片
我們來增強PicBlend_half的混合能力,允許指定兩幅圖片的混合比例(引數Alpha);
Alpha屬於[0..255],當Alpha=127的時候與PicBlend_half等價(小的誤差不算:)
Alpha顏色混合公式:Dst=( Src0*(255-Alpha) + Src1*Alpha ) / 255;
{
long width =min(picDst.width ,picSrc.width );
long height=min(picDst.height,picSrc.height);
for (long y=0;y<height;++y)
{
for (long x=0;x<width;++x)
{
TARGB32& DstColor=Pixels(picDst,x,y);
TARGB32 SrcColor=Pixels(picSrc,x,y);
DstColor.b=(DstColor.b*(255-Alpha) + SrcColor.b*Alpha)/255;
DstColor.g=(DstColor.g*(255-Alpha) + SrcColor.g*Alpha)/255;
DstColor.r=(DstColor.r*(255-Alpha) + SrcColor.r*Alpha)/255;
DstColor.a=(DstColor.a*(255-Alpha) + SrcColor.a*Alpha)/255;
}
}
}
提示: 利用兩幅圖片然後不斷的調整Alpha混合引數就可以得到漂亮的
動畫效果(過渡/切換螢幕等);
函式效果:
Alpha=64時混合後的結果圖片 Alpha=192時混合後的結果圖片
D.帶關鍵色的圖片合成
圖片中用一種特殊的顏色來代表圖片透明的部分,這個關鍵色一般會選擇圖片中實際沒
有用到的顏色;程式在顯示圖片的時候跳過這些特殊畫素,從而形成透明效果;
GIF的透明、很多遊戲中的透明貼圖都應用了這種原理;
比如一幅帶有透明關鍵色的圖片:
該圖片中的關鍵色顏色為純紅色,R=255;G=0;B=0;
函式實現:
void PicBlend_KeyColor(const TPicRegion& picDst,const TPicRegion& picSrc,const TARGB32& KeyColor){
long width =min(picDst.width ,picSrc.width );
long height=min(picDst.height,picSrc.height);
unsigned long KeyColorValue=(*(unsigned long*)&KeyColor) &0x00FFFFFF;
for (long y=0;y<height;++y)
{
for (long x=0;x<width;++x)
{
TARGB32 SrcColor=Pixels(picSrc,x,y);
if ( ( (*(unsigned long*)&SrcColor) &0x00FFFFFF )!=KeyColorValue )
Pixels(picDst,x,y)=SrcColor;
}
}
}
函式效果:
PicBlend_KeyColor函式效果圖
提示:有時為了方便也可以將一個顏色範圍內的顏色都作為透明關鍵色;
一個支援換裝人物系統簡單示例:
身體 頭 髮型
按 底、身體、頭、髮型 的順序混合後的效果圖
E.帶Alpha通道的圖片的混合
PicBlend_KeyColor的實現方式有一些缺點,比如美工做圖片的時候需要"摳邊"
(將透明區域和不透明區域分離),增加了工作量;合成的圖片在“精靈”的邊界區域
有鋸齒感(如果有縮放的話,鋸齒感會更強);我們需要一種更加自由的定義方式,
每個顏色增加一個專門的通道Alpha通道來描述該畫素的透明資訊;
Alpha屬於[0..255],
帶Alpha通道的顏色混合公式:Dst=( Dst*(255-Src.Alpha) + Src*Src.Alpha ) / 255;
(提示: 當Alpha==0時, 公式化簡為: Dst=Dst; //Src完全透明
當Alpha==255時,公式化簡為: Dst=Src; //Src完全不透明 )
{
long width =min(picDst.width ,picSrc.width );
long height=min(picDst.height,picSrc.height);
for (long y=0;y<height;++y)
{
for (long x=0;x<width;++x)
{
TARGB32& DstColor=Pixels(picDst,x,y);
TARGB32 SrcColor=Pixels(picSrc,x,y);
unsigned long Alpha=SrcColor.a;
DstColor.b=(DstColor.b*(255-Alpha) + SrcColor.b*Alpha)/255;
DstColor.g=(DstColor.g*(255-Alpha) + SrcColor.g*Alpha)/255;
DstColor.r=(DstColor.r*(255-Alpha) + SrcColor.r*Alpha)/255;
DstColor.a=(DstColor.a*(255-Alpha) + SrcColor.a*Alpha)/255;
}
}
}
函式效果:
混合前源圖片0 混合前源圖片1(32bit ARGB顏色) 其中透明通道展示
PicBlend混合後的結果圖片(注意精靈的輪廓線,與背景完美的融合在了一起)
F.顏色混合方案:加
有時候,直接把兩幅圖片顏色值相加也能得到很不錯的效果;
比如在實現一些光照特效、太陽引起的鏡頭光暈等效果的時候就很不錯;
(顏色相加時可能會超出255的值域,需要把結果飽和到255)
{
if (color>=255)
return255;
elsereturn color;
}
void PicBlend_Add(const TPicRegion& picDst,const TPicRegion& picSrc)
{
long width =min(picDst.width ,picSrc.width );
long height=min(picDst.height,picSrc.height);
for (long y=0;y<height;++y)
{
for (long x=0;x<width;++x)
{
TARGB32& DstColor=Pixels(picDst,x,y);
TARGB32 SrcColor=Pixels(picSrc,x,y);
DstColor.b=border_color_up(DstColor.b + SrcColor.b);
DstColor.g=border_color_up(DstColor.g + SrcColor.g);
DstColor.r=border_color_up(DstColor.r + SrcColor.r);
DstColor.a=border_color_up(DstColor.a + SrcColor.a);
}
}
}
函式效果:
混合前源圖片0 混合前源圖片1
PicBlend_Add混合後的結果圖