首页 | 互联网 | IT动态 | IT培训 | Cisco | Windows | Linux | Java | .Net | Oracle | 软件测试 | C/C++ | 嵌入式开发 | 存储世界 | 服务器
网络设备 | IDC | 安全 | 求职招聘 | 数字网校 | 网页设计 | 平面设计 | 技术专题 | 电子书下载 | 教学视频 | 源码下载 | 搜索 | 博客 | 论坛
 您现在的位置: 中国IT实验室 >> 游戏开发 >> 游戏开发文档 >> 正文
用色彩饱和法实现近似亮度调节(32位 888)
来源:ChinaItLab 作者:佚名 时间:2007-4-10

  如果我们需要动态的增加一个位图的亮度,我们自然会想到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?255:(src[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了。至于光源之间叠加怎么计算这里就不讨论了。

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

【责编:Kittoy】

中国IT教育热线咨询

相关文章
游戏开发工程实现方法理念
一个MIDP俄罗斯方块游戏的设计和实现
利用Java Swing 实现游戏开发
利用Visual C#实现Reversi游戏开发
利用 Java Swing 实现游戏开发
推荐文章

 精彩友情推荐
·Asp源码 PHP源码
·CGI源码 JSP源码
·建站书籍教程
·服务器软件 .net源码
·建站工具软件
·IDC资讯大全
·机房品质万里行
·IDC托管必备知识
·全国IDC报价
·网站推广优化
 基础入门  开发文档
 最新推荐
  多数的Windows程序都需要Windows.h和Windowsx.h这两个头文件,要确保使用它们。当然,你还需要其它......
游戏引擎演化史
在Windows上安装OGRE的方法
关于滤镜遮罩概念,Sobel 遮罩
游戏开发新手入门之Windows编程
游戏开发新手入门之位图化图形
教你实现卡通渲染的另类勾边方法
游戏设计大师谈如何成为一名游戏设
Visual C#编写 3D游戏框架示例
真正的 Java 学习从入门到精通
游戏开发经验——游戏开发的基本常
  为什么要研究攻击行为在人类有记载的5600年的历史中,共计发生了14,400次战争;今天,平均一天要发生............
游戏开发中显示对话的特殊句法
游戏原型设计的介绍
网络游戏中的攻击行为
谈动作类游戏的必要条件
规则的多元分析模式
载入位图文件到DirectDraw
Archer Game Suite 是什么?
浅谈游戏企划-新手入门篇
暴雪称霸游戏业界的六大秘密绝招
骨骼动画及示例Skinned Mesh的解析
  培训中心