游戏开发论坛

 找回密码
 立即注册
搜索
楼主: zozo

请教:如何具体实现类似“Alpha混合”的“光影效果”

[复制链接]

27

主题

295

帖子

305

积分

中级会员

Rank: 3Rank: 3

积分
305
发表于 2005-3-21 17:43:00 | 显示全部楼层

Re: 请教:如何具体实现类似“Alpha混合”的“光影效果”

2??rar解?悍旁谕?荒夸??A下

sf_2005321174339.rar

710.54 KB, 下载次数:

6

主题

52

帖子

52

积分

注册会员

Rank: 2

积分
52
 楼主| 发表于 2005-3-21 19:17:00 | 显示全部楼层

Re:请教:如何具体实现类似“Alpha混合”的“光影效果”

多谢 DraculaLin

20

主题

398

帖子

398

积分

中级会员

Rank: 3Rank: 3

积分
398
发表于 2005-3-22 12:58:00 | 显示全部楼层

Re:请教:如何具体实现类似“Alpha混合”的“光影效果”

最近一直在学习D3D,没什么时间研究DDraw了。在网上找了几篇文章,给没看过的朋友参考参考!看过的就不要浪费时间啦!^_^

[快速Alpha混合 ]

这是一个快速的Alph函数,它简单快速的作到了50%的混合,适用与一些要求不是很高的场合。
unsigned short Alph50(unsigned short souCol,unsigned short desCol);

#pragma aux Alph50=\
".586"\
".387"\
"and ax,0xf7de"\
"shr ax,1"\
"and bx,0xf7de"\
"shr bx,1"\
"add ax,bx"\
parm[ax][bx]\
modify[ax bx]\
value[ax];
上边的那个仅仅是对单独点的处理,在实际应用中我们经常用到的是对局部位图的操作,所以下边的这个函数就显得很重要了。这个函数的功能是先判断mask中对应的点是否为透明色,如不是则将该点与bmp2的x,y点的颜色做Alph,并复制到bmp的相应的位置中。
void Mix_blit(BITMAP *bmp,BITMAP *mask,BITMAP *bmp2,int x,int y)
{
register int i,j;
register unsigned short col1;
for(j=0;j<mask->h;j++)
for(i=0;i<mask->w;i++)
if((col1=*(*(mask->line+j)+i))!=mask->MskCol)
*(*(bmp->line+j+y1)+x1+i)=Alph50(*(*(bmp2->line+j+y)+i+x),col1);
}
这个函数所做的工作和上一个相似,唯一不同的就是这个不会做Alph运算。
void Mask_blit(BITMAP *bmp,BITMAP *mask,BITMAP *bmp2,int x,int y)
{

register int i,j;unsigned char col1;
for(j=0;j<mask->h;j++)
for(i=0;i<mask->w;i++)
if((col1=*(*(mask->line+j)+i))!=mask->MskCol)
*(*(screen->line+j+y)+x+i)=*(*(bmp2->line+j+y)+i+x);
}
以上两个函数在使用中需要注意的是bmp与bmp2位图的大小一定要完全相同或bmp>bmp2,从程序中就能看出这一点。

20

主题

398

帖子

398

积分

中级会员

Rank: 3Rank: 3

积分
398
发表于 2005-3-22 12:59:00 | 显示全部楼层

Re:请教:如何具体实现类似“Alpha混合”的“光影效果”

[利用MMX优化64K色Alpha混合算法] 云风
从今年 3 月云风开始使用 Pentium 200MMX CPU 后, 一直在考虑如何用 MMX 技术加快 Alpha 混合的操作, 尤其是针对目前常用的高彩模式. 而早先在国外一个有关游戏编程的 MailList 讨论的结果是 MMX 不利于对 16位色进行 Alpha 混合操作. 让我们先来看看 MMX 技术相对于普通指令集的更新,来了解一下这个论点的立论.

  MMX 技术的优势在于, 它的寄存器是 64 位的, 而提供了分组模式, 可以将寄存器内的数据按 8 个字节, 或 4 个字, 或 2 个双字同时进行同一操作, 方便了大数据量的数据处理; 可以成组数据同时作比较操作, 这为透明色点的批量判断带来好处; MMX 的 CPU 拥有 8 个 MMX 寄存器, 在一定程度上缓解了 80x86 CPU 寄存器数量不足的缺陷.

  但是它也有诸多不足, 比如算术指令不能对四字节字操作; 指令结构都不影响标志位; 不能对常数立即寻址; MMX 系统指令集的指令相当贫乏(连 NOT 操作也不能直接实现);

  当颜色深度是 24/32 位时, RGB 都占 8 位, 这样可以巧妙的利用 MMX 里的分组乘法指令达到做 Alpha 混合运算的效果(MMX 的乘法相关指令只有对字操作的 PMULHW/PMULLW 两条, 分别是成组数据的乘后取高位和取低位) 本文旨在探讨 16bit 色的快速 Alpha 混合运算, 所以此处略去不提.

  而 16bit 色, 红绿蓝各占 5 或 6 位, 难以被分组分开, 所以不利于运用 MMX 的这些特性. 当然另外的解决方法是采用 aRGB 4444 的结构, 其中 4 位是 Alpha 通道, 每个色素占半个字节, 再采用类似的方法.

  看过云风去年提出的16bit Alpha 混合优化算法的朋友, 应该会联想到这个算法向 MMX 的引申, OK, 也许你已经明白了大概, 本文的理论基本点就在此, 唯一的问题是, 我们需要面对的是 MMX 指令集的种种缺陷, 这些在实际的程序设计中会逐步的体现出来, 下面, 云风将在介绍算法的同时, 附带的提出一些运用 MMX 的技巧(随后将有专文介绍 MMX 编程技术)

  先来看看上次的算法有无可进一步优化的可能:

  16bit 下 Alpha 混合的关键在于如何将 RGB 分离, 让随后的乘法结果不至于相互干扰.

我提出的是将 16bit 的 rrrrrggggggbbbbb 扩展到 32bit 变形成 00000gggggg00000rrrrr000000bbbbb, 即将中间的绿色提到高 16 位, 而使色素间隔都有 5 到 6 位, 而 对于 5 位的颜色, 超过 5 位的 Alpha 级别是没有意义的, 所以只要设定 Alpha 值在 0~31 间, 同时算这 3 个色素的乘法是不会因为进位造成干扰的. 而这里需要多操作一次移位扩展 16 位到 32 位, 然后需要一次与操作, 将中间间隔位置0, 而且结果需要同样复杂的逆操作从 32 位还原到 16 位.

  改进的思路是直接将两个点交错分离, 即 rrrrrggggggbbbbbRRRRRGGGGGGBBBBB 分离成 rrrrr000000bbbbb00000GGGGGG00000 和 00000gggggg00000RRRRR000000BBBBB 两部分, 前一部分右移 5 位后变成 00000rrrrr000000bbbbb00000GGGGGG, 两个数字就都可以同时运算 3 个色素, 其结果后一组右移 5 位后可以与前一组合并. 这样就省去了好几次移位操作, 并且数据可以 4 字节读入, 和四字节写, 粗看真的效率很高. 但是在传统的 80x86 上却有两点制约了它的运用:

CPU 的寄存器不够用, 这个方法光保存数据就需要 4 个 32 位的寄存器, 虽然 EAX,EBX,ECX,EDX 刚够用, 但是这就使得 Alpha 混合函数不能直接写在 Blit 操作里面. 必须单写个子程序调用. (不过也值得写尝试一下, 不是吗? 如果有朋友写好了, 希望能给我拜读一下,我在风魂游戏程序库里留了接口, 并在注释里提到了函数的具体写法)
2D 游戏中, 一般都是利用 Alpha 混合绘制精灵而不是规则的矩形位图, 所以这里面还存在着透明色的判断, 如果是双点处理, 这一步不易实现. (不过也不是没有好的方法, 就是代码的长度就长而复杂了:-( )
而 MMX 却提供了 8 个寄存器, 同时有分组比较的指令, 正好弥补了这两点不足, 而且利用寄存器有 64 位的优势可以同时运算 4 个点. 所以我们暂且只用 MMX 来实现新的想法.(如果你对这个方法用在传统指令集上有兴趣, 希望同时操作 2 个点进行 Alpha 混合, 并写出实际的代码, 请和我联系, 我非常希望看到风魂的非 MMX Alpha 混合版本能够进一步优化)
  用 MMX 来做这项工作, 原理差不多(相当简单不是?), 也是读入源点和目标点后分离成 4 个数据放在 4 个寄存器中. 两对间进行 Alpha 混合, (这样一对数据间就同时运算了 6 个色素) 最后就两对数据混合的结果合并。不过从现在开始我们就要面对 MMX 8 个寄存器不够用的困境了 :-( MMX 指令不能和 64 位立即常数一起使用, 所以在进行分裂操作的时候用到的掩码要常驻在寄存器内. 如果寄存器主够多的话, 可以连掩码的反值也放一个, 可惜现在不能这么浪费 :-( 处理透明色问题方面, 可以先将点和透明色比较得到一个掩码, 我们再将混合后的点,及原来的目标图上的点 (这个点应当保留一个备份, 哎, 又去了一个寄存器) 分别与掩码逻辑运算合并得到最终的数据写入目标图. 这里, 需要大量运用的 NOT 操作, Intel 竟然没有在 MMX 指令集中提供 @#$%^&! 我们只好用 PANDN (取反再与操作) 间接完成. (例:可以先用 PCMPEQW mm0,mm0 (自己和自己比较当然全相等了 ;-) 生成常数 FFFFFFFFFFFFFFFF, 用 PANDN mm1,mm0 就可以将 mm1 取反.) 这里, 不再可以利用 MMX 的分组乘法, (MMX 不能对 32 位数进行乘法操作) 所以我们应该用移位和加减法来实现. 这样, 如果有几级 Alpha 值, 就应该写几个混合函数. 最后建立一个函数指针数组, 将每级 Alpha 混合函数依次放入数组. 我们在调用时就可以根据需要的 Alpha 值来调用相应的函数了 :-)

  在风魂 0.07 里, Alpha 混合又一次修改了算法, (0.06 使用的上述算法, 0.07 则没有) 这里要感谢网友 T&amp (tapu@371.net) 的新思路. 针对分级数比较少的 Alpha 混合, 比如 8 级, 可以用更简单的方法. 大家可以注意到, 50% 的 Alpha 时, R=(r1+r2)/2, 也可以近似的等于 r1/2+r2/2. 那么 RGB 可以方便的同时运算. 只需要在移位后做一次简单的与操作即可 (0RRRRRGGGGGGBBBB & 011110111101111=0RRRR0GGGGG0BBBB) 然后, 将两个移位后的数据相加就完成了 Alpha=50% 的混合. 这个方法避免了切分和还原数据, 所以速度更快. 风魂的早期版本, 对 50% 的 Alpha 度就做了此种特殊处理. 但是, 它是有误差的, 误差在于移位造成的每色素上 1/32 或 1/64 的偏差.

  下一步我们可以将 50% 的 Alpha 值推广到 25% 12.5% 甚至更小. 现在来看一下完成 R1*25%+R2*75%, 它等于 R2+R1*25%-R2*25%=R2+R1/4+R2/4. 这里除 4 的操作和除 2 原理是一样的即: (RRRRRGGGGGGBBBBB >> 2) & 0011100111100111. 依次类推, X * 37.5% + Y * 62.5% = (X+Y)/2 + Y/8 - X/8 等等. 我们就只需要利用移位和加减法就可以同时完成 N 个色素的混合了.

  再来看看这个方法的缺陷. 首先是误差问题, 每一组移位取与都会造成最大为 1/32 的误差, 而多次运算有可能使误差累计, 所以 alpha 级别不能分的太多. 而且 alpha 级别分的太细后, 使得运算步骤变的很多, 不切分直接运算的优势有可能损失掉. 而且更致命的一点是, 如果想用 MMX 加速, 那么通常 AND 运算用的掩码应该放在寄存器中 (如果放在内存, 而 MMX 不能立即寻址, 间接寻址取内存可能不能命中 CACHE 速度变慢, 大规模的混合运算速度损失太多) MMX 的寄存器却只有 8 个. 那么多个掩码会使明显的感觉寄存器不够用, 但这不失为一种好的方法. 风魂 0.07 中新的 alpha 精灵, 这一步的算法更改带来了 10% 左右的速度提升, 而画质的损失却几乎没有体现 :-)

  最后对关于带 Alpha 通道的位图的做一点探讨, 这里每一个点将带有不同的 Alpha 值, 我们应该合理的协调位图的结构. 将 Alpha 值和颜色信息放在一起是不合算的. 这样不利于高速处理。我们可以将所有点的 Alpha 值提出来放在一起, 对于 16bit 的颜色, 合理的 Alpha 级别应该在 16级以下。这样可以每一个字节存放两个 Alpha 值. 用一个寄存器作为指向 Alpha 值区域的指针, 读入对应点的 Alpha 值, 调用相应的混合函数运算。但是, 这种位图每个点都有可能是不同的 alpha 值, 如此就不能多点同时运算, 云风找到了另外的加速方法, 要知详情, 且看下文分解 ^_^
 

20

主题

398

帖子

398

积分

中级会员

Rank: 3Rank: 3

积分
398
发表于 2005-3-22 13:02:00 | 显示全部楼层

Re:请教:如何具体实现类似“Alpha混合”的“光影效果”

[16位Alpha混合的简单算法] moonet

游戏中,我们常常可以看到一些半透明的图像,那么这些图像是怎么生成的呢?是简单的贴位图做的吗?当然不是,要不美工不就惨死了。这其实是利用程序将两张图片进行混合后的效果,也就是图形的Alpha混合,要得到混合图片,就必须对每个点分别进行处理。让我们来看看如何进行Alpha混合。这里以16位色彩位例子:
  16位色彩下,每个像素都用一个WORD来表示,有16位二进制。这16位,分成三段分别表示像素的R、G、B值。现在的显示卡有两种:555格式的RGB分别各占5位最高位空、565格式G的值占6位,其他两个分量各占5位。所以首先,我们要得到像素的RGB。
N R R R R R G G G G G B B B B B  
图1 555的RGB分量
R R R R R G G G G G G B B B B B
图2 565的RGB分量

   在DirectDraw下,可以通过锁定表面,得到表面的像素,将他保存到一个WORD*的变量里。当我们得到资源表面和目的表面的像素后,我们就可以开始进行Alpha混合了。16bit的Alpha混合首先必须分别对原和目的表面的像素分色。这可以通过位操作来实现,用像素值分别与RGB对应的特定值进行与操作将多余的其他色去掉,最后通过移位去掉后面的0。这里要注意555显示卡和565显示卡的区别。分色后,混合正式开始,我们将分色得到的R、G、B的各分色分别混合。下面先看一个公式:A=目的颜色、B=资源颜色、Alpha=混色深度(0 < alpha < 1)

混合后的颜色 = A * Alpha + B * ( 1-Alpha )

  当Alpha=0,混合后的颜色=A。当Alpha=1,混合后的颜色=B。当Alpha介于0与1之间,混合后的颜色包含了资源和目的表面的两种颜色。调整Alpha的值,混合后的颜色就会跟着有规律的变化。但是,颜色是整数,所以,我们必须对公式进行一点简单的变化:

混合后的颜色 = ( A * Alpha + B * ( 32-Alpha ) ) / 32   1 < 32

  这样的等价变化后所有的元素都变成了整数,混合颜色的调整级数为32(更高当然也是可以的,把公式中的32换成其他数,Alpha区值范围也变化),但是计算机处理乘法和除法是很慢的,而Alpha混合是对很多的点进行计算,上面的公式显然是不适合的,我们再来点变化:

混合后的颜色=( ( A-B ) * Alpha ) >> 5 + B

  除以32等于右移5位,这就是调整级数取32的原因。计算机处理移位是很快的。至于那个乘以Alpha,我也不知道有什么简单的方法改成效率更高的算法。分别混合好R、G、B的色后,最后还需要将他们合成,这是个简单的步骤,与分色相反的移位,然后将三个色值按位或操作就可以了。处理了所有需要混色的像素后,记得将表面解锁。

20

主题

398

帖子

398

积分

中级会员

Rank: 3Rank: 3

积分
398
发表于 2005-3-22 13:03:00 | 显示全部楼层

Re:请教:如何具体实现类似“Alpha混合”的“光影效果”

[16位Alpha混合的MMX优化]

  最近,我开始学习汇编程序,用来优化我的引擎,断断续续的学习了20多天,也有了一些心得体会,希望和他家一起分享:) 游戏中图形操作就是吃系统资源老虎,大量的cpu时钟都被他吃掉了,为了加速我的游戏引擎的图形处理速度,我对一些图形操作进行了汇编优化,并使用了威力惊人的MMX指令。为什么说威力惊人呢?看下去就明白了。

  MMX指令适合用来做大量的串操作,在MMX处理器中,提供了8个64位的寄存器,并且,MMX指令能够使用数据组的操作。所以,用来处理逐个点操作的图形处理最合适不过了。下面,我就用我的Alpha混合的代码来说明一下,MMX的强大处理能力:) 以前,我在主页上面写过一篇16位Alpha操作的文章,那个时候,我不会汇编,用C写的例子程序,在一个912X720的区域内Alpha混合的时候,FPS值只有3-4,实在是没有实用价值,现在,我手握MMX这个利剑,经过一番努力后,FPS值直线飙升,达到了14,整整有4倍的擦别啊!如果在640X480下面,全屏幕的alpha也能达到20多帧! 为什么有这么大的性能提升呢?开始我说过,mmx寄存器是64位的,分别是mm0--mm7一共八个。MMX的指令可以让mmx寄存器之间的运算按照byte,word,dword,qd分组运算。这就是问题的关键!让我们看看,我们的16位Alpha混合是一个点占用2个byte,在一个MMX寄存器中间,我们可以容纳4个这样的点,所以一次混合计算,将能够让四个点混合,这就是4倍速度提升的原因!!! 好,让我们看看实际的代码: 其中wdDest,wdRes分别是目的和源图形数据指针,__Depth使Alpha混合深度,值为0x0001000100010001 * nDepth,nMMXCount是我用来纪录一行进行多少次MMX操作的,Not_MMX_Point是拷贝矩形宽度除4的余数,因为一旦出现宽度不是4字节对齐,余下的多余点就必须按照普通的汇编代码处理了,这里,我值不过是留下了接口,没有去实现,因为寄存器不够用,需要push和pop。__Mask64为__int64类型的掩码,值为0x0001000100010001 * (short)m_nColorKey,__Mask是用来过滤多余颜色的奄码0x001f001f001f001f(因为图形为555格式)。 大家注意,每次都是用movq来移动4个点到mmx寄存器,然后实用word对齐分组计算的。 这里要说明,使用了mmx后,原来透明色是可以不处理的,现在必须处理,为了让透明部分不影响目的数据,我是用了一点小技巧,具体做法看我的代码注释。这可是我自己想出来的哦:) 要看完整的代码,请下载我的完整的JAppLib。

__asm
{
    //pusha;
    movq mm6,__Depth;
    mov eax,dword ptr wdDest;
    mov ebx,dword ptr wdRes;
    mov cx,nUseH;//纪录操作矩形高

Add_Next_Row:
    cmp cx,0;
    je All_End;
    xor dx,dx;

Next_MMX_Point:
    cmp dx,nMMXCount;
    je Not_MMX_Point;
    movq mm0,[eax];
    movq mm1,[ebx];
    movq mm7,mm1;
    pcmpeqw mm7,__Mask64;//这里用来处理透明色的,我的思路就是透明色就用目的颜色填充源
    psubusw mm1,mm7; //这样,透明部分目的和源一样,不管怎么去alpha,只要公式是正确的,
    pand mm7,mm0; //他们混合后的颜色就是不会变,add color特效也是同样的道理
    por mm1,mm7; //

    movq mm2,mm0;//g
    psrlw mm2,5;
    pand mm2,__Mask;
    movq mm3,mm1;
    psrlw mm3,5;
    pand mm3,__Mask;

    movq mm4,mm0;//r //wdDR=(((wdDR-wdRR)*nDepth+(wdRR<<5))>>5);
    psrlw mm4,10;
    pand mm4,__Mask;
    movq mm5,mm1;
    psrlw mm5,10;
    pand mm5,__Mask;

    //psllw mm0,1;//b
    pand mm0,__Mask;
    //psllw mm1,1;
    pand mm1,__Mask;

    psubsw mm0,mm1;
    pmullw mm0,mm6;
    psllw mm1,5;
    paddsw mm0,mm1;
    psrlw mm0,5;
    //psrlw mm0,5;
    //paddusw mm0,mm1;

    psubsw mm2,mm3;
    pmullw mm2,mm6;
    psllw mm3,5;
    paddsw mm2,mm3;
    psrlw mm2,5;
    //psrlw mm2,5;
    //paddusw mm2,mm3;

    psubsw mm4,mm5;
    pmullw mm4,mm6;
    psllw mm5,5;
    paddsw mm4,mm5;
    psrlw mm4,5;
    //psrlw mm4,5;
    //paddusw mm4,mm5;

    //psllw mm0,10;
    psllw mm2,5;
    psllw mm4,10;

    por mm0,mm2;
    por mm0,mm4;

    movq [eax],mm0;

    add eax,8;
    add ebx,8;
    inc dx;
    jmp Next_MMX_Point;

Not_MMX_Point:
    xor dx,dx;

Not_MMX_Next:
    cmp dx,nNotMMX;
    je Row_End;
    sub eax,2;
    sub ebx,2;
    inc dx;
    jmp Not_MMX_Next;

Row_End:
    sub eax,nUnUsed2;
    sub ebx,nUnUsed1;
    dec cx;
    jmp Add_Next_Row;
    //loop Add_Next_Row;

All_End:
    //popa;
    emms;
}




20

主题

398

帖子

398

积分

中级会员

Rank: 3Rank: 3

积分
398
发表于 2005-3-22 13:04:00 | 显示全部楼层

Re:请教:如何具体实现类似“Alpha混合”的“光影效果”

这几篇文章我没太细看,应该都是讲Alpha混合运算优化的,希望对楼主能有所帮助!

6

主题

52

帖子

52

积分

注册会员

Rank: 2

积分
52
 楼主| 发表于 2005-3-22 13:19:00 | 显示全部楼层

Re:请教:如何具体实现类似“Alpha混合”的“光影效果”

太谢谢了:)
我会仔细阅读的!

14

主题

149

帖子

149

积分

注册会员

Rank: 2

积分
149
发表于 2005-3-25 20:26:00 | 显示全部楼层

Re:请教:如何具体实现类似“Alpha混合”的“光影效果”

请教zozo兄弟!
在PS中如何做到?
顺便请兄弟加我QQ
3860040

6

主题

52

帖子

52

积分

注册会员

Rank: 2

积分
52
 楼主| 发表于 2005-3-27 15:48:00 | 显示全部楼层

Re:请教:如何具体实现类似“Alpha混合”的“光影效果”

PS里面实现比较复杂,可以利用通道+蒙板来做,如要抠去一个带黑背景的图,主图带边缘柔化渐变,大概的步骤:
首先在目标图的图层下方建立一个色彩填充层填充的颜色选择标志色彩最浓的区域。
然后将目标图层的混合方式改为“变暗(Darken)”
之后背景将变为深色,用信息面板(Info)察看背景颜色,还不是纯黑色(纯黑的RGB值均为0)
然后建立一个色阶(Level)调整层,使用右下角三个吸管的第一个,在背景上点一下这代表将原先的背景色强行改变为纯黑。
然后将蓝色通道作为选区载入(按住Ctrl点击蓝色通道即可)
在利用这个选区建立一个色彩填充层就完成了
-------------------------------------
最简单的方法可以利用Fireworks MX 2004的位图逻辑运算命令来做:
点击目标黑色背景位图->选择“screen”运算命令,
点击目标白色背景位图->选择"Multply”运算命令。完成!
然后可以调整位图的色彩饱和度!
-------------------------------------
我在程序设计方面很差的,呵呵,+我QQ聊聊天把 :)
QQ记下了,我上后再+你  :)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-12-25 00:58

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表