游戏开发论坛

 找回密码
 立即注册
搜索
查看: 1992|回复: 0

用色彩饱和法实现近似亮度调节(32位 888)

[复制链接]

13

主题

46

帖子

51

积分

注册会员

Rank: 2

积分
51
发表于 2007-3-21 23:12:00 | 显示全部楼层 |阅读模式
如果我们需要动态的增加一个位图的亮度,我们自然会想到HSL编码,把RGB编码转换成HSL,再增加L,再转回rgb。那么就来看看怎么转换吧。这是从别处拷贝的代码。

void   CDibImage::RGBtoHSL(COLORREF   rgb,   double*   H,   double*   S,   double*   L)   
  {   
      double   delta;   
      double   r   =   (double)GetRValue(rgb)/255;   
      double   g   =   (double)GetGValue(rgb)/255;   
      double   b   =   (double)GetBValue(rgb)/255;   
      double   cmax   =   max(r,max(g,b));   
      double   cmin   =   min(r,min(g,b));   
      *L   =   (cmax+cmin)/2.0;   
      if   (cmax   ==   cmin)     
      {   
          *S   =   0;   
          *H   =   0;   //   it's   really   undefined   
      }     
      else     
      {   
          if   (*L   <   0.5)     
                *S   =   (cmax-cmin)/(cmax+cmin);   
          else   
                *S   =   (cmax-cmin)/(2.0-cmax-cmin);   
          delta   =   cmax   -   cmin;   
          if   (r==cmax)   
                *H   =   (g-b)/delta;   
          else   if   (g==cmax)   
                *H   =   2.0   +(b-r)/delta;   
          else   
                *H   =   4.0+(r-g)/delta;   
          *H   /=   6.0;   
          if   (*H   <   0.0)   
                *H   +=   1;   
      }   
  }   

这个转换过程,基本上不要想了用到。不过参考运算过程,我们至少知道L是怎么来的。

它等于RGB中的最大值加最小值的平均值。

所以我们一般就用直接增加RGB值的方法来调节亮度,一个近似的但便宜很多的方法更好,不是吗?

比如我们要增加20的亮度值,那么

R+=20;
G+=20;
B+=20;

这么做唯一的不妥是颜色的饱和度和色相出现些微偏差,不过这个你在意么?

如果颜色越接近黑色,那么色相的改变就越大,如果一个场景中的背景是纯黑色0x000000,那么改变后就变成0x141414,这显然不符合大多数应用的要求,所以后面我们会对此做出一些调整

但这里还有一个问题,如果增量超出了色彩范围怎么办?

如果值245+20 = 265 = 9;

最终结果差不多给颜色来了个反转,这不是我们想要的,所以我们就要用到色彩饱和方法。

色彩饱和的算法很简单,就是检查相加值如果超过了255,则设回255。

你可以想到这种方法的某些应用,比如你有个很酷的动画技能效果图片(光效果),它的底色是黑色,如何把效果从图片中抠出来画到显示表面上,如果用蒙板,比如黑色值做key,程序遇到这个key就不拷贝,不过有些问题就是边缘会带有黑边,因为边缘不是纯黑色,这里我们可以把颜色值加上一个值n,过滤掉过暗的色,小于255-n的值就不会被拷贝了,你可以用这个方法建立一张黑白色蒙板图,便于反复使用。

回到原来的主题上来,怎么写这个运算

unsigned char *dst,*src;

while(NULL!=src){
dst[0]=(src[0]+n)>255?255src[0]+n);
dst[1]=(src[1]+n)>255?255:(src[1]+n);
dst[2]=(src[2]+n)>255?255:(src[2]+n);
//increase 4 for 32bit long
dst+=4;
src+=4;
}

有个语句判断,真闹心,而且这么写计算值超过了uchar的字长。可以想象对速度的影响。

解决的办法还是用查表法。这样可以快速定位。
比如unsigned char additive[256][256].

第一个256为我们要增加的亮度,第二个256为源色彩的子通道值。现在我们还要解决前面留下来的一个问题,我们把亮度值乘以一个权变系数再相加,这个系数为正弦函数sin(a),a的取值范围从0到180度,再分成256个刻度,这样从0到255的亮度增加值就会形成一个正态分布的式样,正好符合我们的要求:两头的颜色(纯黑和白色)对光照敏感度低,而中间的颜色对光照敏感度高。

static double PI = 3.1415926;
unsigned char additivetable[256][256];
void makeAdditivetable{
for(int l = 0;l<255;l++){
  for(int c = 0;c<255;c++){
   short cl = l*sin((PI)/256*c);
   additivetable[l][c]=(cl&0x100)?255:cl;
  }
}
}

这样原来的代码可以改写成
unsigned char *dst,*src;
unsigned char (*light)[3] = {{n,n,n}...};//r,g,b assume we have multi lights here
while(NULL!=src){
//b,g,r
dst[0]=additivetable[light[0][2]][src[0]];
dst[1]=additivetable[light[0][1]][src[1]];
dst[2]=additivetable[light[0][0]][src[2]];
//increase 4 for 32bit long
dst+=4;
src+=4;
//next light at next point
light++;

}

light可以任意的设定,不仅有白光,还可以设置其他光,比如带有暖意的橙光*light = {168,80,0};

也许到这里有人已经发现一些问题,比如如果一个物体是纯红色,那么在纯蓝色灯光照射下岂不是不会有任何变化?嗯,的确是这样,这个问题可以这么来解释:如果一个物体在自然光照射下只返回纯红,那么这说明该物体表面只会反射红色光,而其他光的波长都被吸收掉了,所以打出的蓝色光不会起到任何变化。事实上纯色光在自然界太罕见,基本上都是混合光,也就是说,如果你要实现蓝色光的效果,应该这么设定*light={40,40,168};

由此我们想到的一个问题是:不同的材质对不同波长的光是有不同的反射率的,你也许想到使用一个材质反光率表来解决这个问题,在画不同材质是加入这些参数,但因为材质本身的色彩已经反映了自身的吸收率,再加上我们已经设定了调节系数,所以我们就不大为这个问题操心了。

再来就是一个光照范围的问题,光照随中心距离而逐渐减弱,比如路过一盏路灯,从进入范围到走出,人物上的光彩应该出现动态的变化,怎么处理?

根据人物坐标,可以动态的设置不同的light值。
缺点:整个人物块出现固定的光照表现,如果人物处在光圈的边缘轮廓上,那么整个人物会显得不太自然。

采用调色板
在256色模式下会使用这种方法。当然32位下也可以用,这么做速度快,可以控制很多细节,但麻烦的是你要重写很多处理过程,这显然不是32位主流的方法。

设定一个和屏幕一样大小的亮度缓存
这个亮度缓存只储存和光亮有关信息,也就是前面我们设定的unsigned char (*light)[3] = {{n,n,n}...};比如某个位置打开一盏灯,或关掉一盏灯,我们就重新计算在这个light能够影响到的所有点上的light,增加或去掉它的叠加效果,更新buffer,更新表面。那么对于所有的游戏场景,都应该有一个预置的亮度buffer图,在我们需要整体效果时,比如夜晚,黄昏,清晨等,就在图上所有的点叠加这个光源就ok了。至于光源之间叠加怎么计算这里就不讨论了。

采用色彩饱和的逆方法,类似我们可以实现阴影效果。通常,阴影实现我们就不用处理得那么细腻,因为一般用户不会太注意阴影,所以使用的阴影表刻度要低很多。


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2026-1-26 19:14

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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