本节书摘来自异步社区《点睛:ActionScript3.0游戏互动编程》一书中的第1章,第1.3节,作者:游志德 , 彭文波 更多章节内容可以访问云栖社区“异步社区”公众号查看。
1.3 HSB模式及其与RGB间的转换
从前面的讨论可知,RGB模式是一个数理性质较强的概念,对于大部分色彩来说,我们很难通过RGB的数值得知它代表什么颜色以及它的明暗和鲜艳程度如何。
《忆江南》中的景观之所以能直接用最简单的三原色来渲染,完全是因为它所描绘的江南美景足够的清澈纯净而且颜色的种类较少。
实际上,大多数情况下,颜色的种类远不止红绿蓝三种,例如北宋的苏东坡在以歌咏残秋季节景物为主题的《赠刘景文》一诗中就有这样的一句:“一年好景君须记,最是橙黄桔绿时。”
类似的诗句举不胜举,例如韩愈的《晚春》中还提到了紫色:“草树知春不久归,百般红紫斗芳菲。”
从以上的例子我们不难看出,单凭红绿蓝三种颜色远不能在感官认知上对万千世界中丰富的色彩给出较为准确的定义。此外,即使我们确定了颜色种类,颜色的明暗或者饱和程度也需要加以描述。“绿树阴浓夏日长,楼台倒影入池塘。”(高骈《山亭夏日》)中的“阴浓”就在亮度方面对绿色的深度进行了修饰。
作为21世纪的文艺程序员,我们也要在感性与理性之间寻找出属于自己的一片天。HSB模式就帮了我们很大的忙。
HSB通过H(Hue,色相)、S(Saturation,饱和度)和B(Brightless,亮度)来描述色彩的构成,与感官认知相当地吻合。同时让我们感到欣慰的是,HSB模型的数学计算并没有想象中的复杂,而且很容易就能转换到计算机可以识别的RGB模式中,让科学与艺术再次擦出闪耀的火花!
笔者不希望像其他介绍ActionScript乃至美术的书一样寥寥几句就带过HSB模式,或者直接把公式贴上来,毕竟这是“百度一下你就知道”的知识,直接搬过来也没什么意义可言了。所以,我们会简单讲下自己的理解,但与此同时,书上会贴出一些百度回来的内容,毕竟受篇幅所限,不可能面面俱到。
1.3.1 HSB模式的色彩空间模型及其分支
第一次接触HSB模式的原理,是从以下这个地址开始的:http://zh.wikipedia.org/wiki/ HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4。
图1-21所示为我从以上链接的文章复制过来的一张色彩空间示意图,可见HSB在业界至少还可以细分成HSL和HSV两种模式。它们都使用了三维坐标系来描述色彩值的分布。
第一个属性是色相,它代表颜色的种类,如红、黄、绿就分别属于不同种类的颜色。色相值的大小来源于物理学中的可见光谱(也就是我们常说的七色彩虹),其中,位于两端的红光与紫光在颜色形态上(可能与反射色光的种类有关)比较接近。所以伟大的物理学家牛顿首次把直线状的颜色光谱围成一个圆环并将色相坐标轴做成了环状。在这样的一个色相环中,0度和360度完全等价,为红色,其他颜色则根据它们在彩虹渐变中的顺序依次排列,分别为:橙30,黄60,绿120,蓝240,紫300(分布不均匀的原因跟RGB处理有关,实际上分布均为的模式更有利于指导色彩搭配)。引入亮度和饱和度以后,所有颜色便组成了一个实心圆柱体(实质上它们都可以由圆锥体展开,详情可以查阅原文)。对于这一个属性,HSV和HSL的理解完全一致。图1-21中,环形箭头所指的角度(即实线与虚线之间的夹角,顺时针为正,逆时针为负)就等于Hue的值。
![ea0beda26375b3bd33827c0e120a393214001c86](https://yqfile.alicdn.com/ea0beda26375b3bd33827c0e120a393214001c86.png?x-oss-process=image/resize,w_1400/format,webp)
Saturation值在坐标系中表现为坐标点到圆心(圆柱中轴线)的距离,距离圆心越远,饱和度越高,色彩越鲜艳,取值范围为0~100。
最后一个属性代表亮度,两种色彩模式用了不同的单词(Lightness和Value),坐标系里,它表示当前点到圆柱底部的距离,取值范围为0~100。
图1-22中,左侧圆点所表示的是H=240、S=67、L=100的白色,右侧圆点则表示H=90、S=67、V=83的绿色。
我们无法用很简洁的语句概括它们之间的异同,但我们还是可以通过图1-22看出一些特征性的差别。
1)HSL的亮度跟数学的概念非常吻合,亮度为0时必然全黑,亮度为100时必然全白;而HSV只有亮度等于0的时候为全黑,亮度为100时RGB可能只有一种颜色的光被全部反射出来。
2)两者对饱和度的理解不甚一致,HSL认为很白的颜色也能代表饱和,但HSV认为很白就一定很不鲜艳。所以HSV模式里,只有当S等于0时才有可能全白。而S=100、V=100只相当于HSL模式中L=50的位置。
事实上,这两种色彩模式都存在一定的缺陷,如黄色比粉红往往要刺眼一些,但是HSL模式中,黄色的亮度还不如粉红的高;又比如淡紫比深蓝的感觉要鲜艳,HSV模式却告诉我们,前者的饱和度比后者要低。
灰度值(1.4节会讲述)可以在一定程度上解决以上问题,但是局限性依然很大。笔者曾经幻想过用纯数学手段来弥补自己美术细胞的不足,结果都以失败告终,直至某位美工给我笔者荐了几款配色软件以后我才如梦初醒。一个看起来不太起眼的ColorPicker也得占用十几兆的存储空间,查阅安装目录后发现,光数据表就接近10MB,而且还是压缩过的。可见美学的知识还真不是区区几条数学公式就说了算。但无论如何,在此笔者还是代表全天下没有美术功底的程序员们,向设计出HSB模型的数理学家致以崇高的敬意。
一般涉及取色器的软件都会使用到HSB模式,而且HSL和HSV两者只选其一,比如Windows的系统颜色面板和Microsoft Office会使用HSL,而Adobe系列的软件如Flash、Photoshop则倾向于选择HSV。因为使用HSV的软件往往喜欢用B代替V,所以导致笔者经常搞混HSB、HSL和HSV之间的关系,也让笔者在后续的日子里走了不少弯路。在此希望笔者的前车之鉴,可以让大家不会重蹈覆辙。在碰到类似问题的时候,建议先搞清楚别人所提到的HSB具体是指HSL、HSV还是广义上的色相模型。
1.3.2 软件中的拾色器如何实现HSB的色彩空间模型
阅读本书的朋友恐怕很少有机会接触到圆柱形的三维取色器,而HSB模式却是一个立体的空间。那么,软件里的颜色拾取器是如何在平面里体现出3D坐标系的呢?让我们从Windows调色板开始研究。
图1-22是Windows系统调色板的界面。抛开左侧的颜色列表不谈,我们可以把Windows调色板分为两个部分:一个二维的平面和一个一维的滑动条。乍一看似乎跟之前的圆柱体空间扯不上任何关系,不过大家有没发现,最右侧那个一维的条是不是跟圆柱体的母线很像,自上而下呈现出逐渐变暗的趋势吗?没错,它就相当于圆柱体的高度变化。一维条左边的色彩选取平面就相当于圆柱体的横截面了,只是被拉成了矩形而已。因为我们可以看到,左边缘跟右边缘都是红色,而底部始终为灰色,这足以证明它就是圆环的一个变体。
![ea6140b957b901aa99c68a335227aa3931a82f3b](https://yqfile.alicdn.com/ea6140b957b901aa99c68a335227aa3931a82f3b.png?x-oss-process=image/resize,w_1400/format,webp)
小提示
在配套源码里,笔者给出了从圆形色盘展开为矩形的一个演示动画,有兴趣的读者可以自行查阅。
我们观察一下滑块拖动时HSL 3个值的变化,RGB部分在这里就先忽略了,因为取色面板的构建原理都基于HSL模式。注意,这3个值的取值范围都被调整为0~240。
我们只拖动右侧的滑块,HSL 3个值当中只有L在变化,这跟色彩空间示意图相当吻合;小心翼翼地拖动二维面板上的十字准星,让它只在水平方向移动,则只有H值在变化(Windows面板用了E这个字母);而只作竖直方向移动的话,就只更改S的值了。
笔者不是美术专业人士,但有阅读过跟网页色彩搭配有关的书籍,里面曾提到过,要使整个页面的色彩比较协调,其中的一个办法就是让色相、饱和度跟亮度这三者之中至少有一项保持基本一致。所以这样的面板对于调出一套比较合理的配色方案还是会有一定帮助的。
当圆柱体被平面化以后,这3条坐标轴就可以随意组合了,不一定再是亮度占着一维空间。下面我们再来看看Flash CS 5.5软件里的颜色面板。
启动Flash CS6/ 5.5,在确保有fla文档打开的前提下,选择菜单项【窗口】|【颜色】(Alt+Shift+F9),就能打开如图1-23所示的颜色面板。
我们确保下拉框选定的是纯色,在其下方的单选按钮选中的是HSB中的B选项,将会看到跟Windows系统调色板非常相似的界面。不同的是,一维滑动条最上方不再显示白色,因为Flash CS6/5.5使用的是HSV模式,即使把B拖到100%,也还是呈现出紫色来。
拖动一维滑动条上的滑块,我们还可以看到,二维调色面的色彩也会随之更改,即圆柱截面实时更新。与Windows系统调色板相比,这绝对是个优势。它会根据当前点的高度实时截取圆柱的横截面,使我们可以第一时间观察到真实的颜色值,非常直观,如图1-24所示。
![7a362e1e6c5bf52e457a130c761852d3ef0c57ac](https://yqfile.alicdn.com/7a362e1e6c5bf52e457a130c761852d3ef0c57ac.png?x-oss-process=image/resize,w_1400/format,webp)
![5d4c67d6e0284bc600bbfb8fcd04d0701772414b](https://yqfile.alicdn.com/5d4c67d6e0284bc600bbfb8fcd04d0701772414b.png?x-oss-process=image/resize,w_1400/format,webp)
与此同时,一维轴的属性还可以由用户指定。我们可以把单选按钮选定到H或者S上,然后左侧的平面就将由另外两个坐标轴进行构建,如图1-25、图1-26所示。
我们甚至可以将一维轴设置为R、G或者B,但个人认为此举没有太实在的美学意义。
Flash CS6新增的Kuler面板(实际上它只是个网站,安装完成以后在CS5.5的【扩展菜单项】里也能找到)比较准确地还原了色彩空间的示意图,用圆来表示H和S坐标轴所构成的平面,从而更有助于我们理解HSV模式的原型尤其是H的概念,如图1-27所示。
![47c89c70b6487aa90d42c4de7cc91878d0d75e47](https://yqfile.alicdn.com/47c89c70b6487aa90d42c4de7cc91878d0d75e47.png?x-oss-process=image/resize,w_1400/format,webp)
![341897250c24c7de62030cc69a19c5e11b3d650d](https://yqfile.alicdn.com/341897250c24c7de62030cc69a19c5e11b3d650d.png?x-oss-process=image/resize,w_1400/format,webp)
![db2d7115f02cf8a6a242ce1a441db211d49d87c6](https://yqfile.alicdn.com/db2d7115f02cf8a6a242ce1a441db211d49d87c6.png?x-oss-process=image/resize,w_1400/format,webp)
但需要注意的一点是,Kuler的色环将红和黄的角度拉开了,跟HSB模型中的色环有所不同。因为这样的分布更符合美术上的色彩对比概念,对色彩搭配更有帮助。
与RGB模式相比,HSB更直观地描述了人类对色彩的真实感知,为色彩的视觉变化提供了一套非常有指导意义的解决方案。诸如变色、明暗调整、对比度更改这一类的事儿,HSB都能很完美地实现。虽说RGB也能通过色光反射量实现一些变换,但它对肉眼来说太不友好了,出来的效果也颇为生硬。
下面我们来研究下HSB和RGB之间的转换公式。
1.3.3 HSB与RGB之间的互转公式及HSL和HSV对色彩属性理解的异同
首先,HSL和HSV对H的定义一致,公式如下。
![a5c5257e18c58715d39f14c85abb20a096a8d336](https://yqfile.alicdn.com/a5c5257e18c58715d39f14c85abb20a096a8d336.png?x-oss-process=image/resize,w_1400/format,webp)
max=min意味着R、G、B 3个值始终相等,只能呈现出黑白灰3种颜色。色相值在这种情况下没有任何意义,所以干脆让它等于0。
然后,HSB模式将色相环等分成3个长度均为120°的区间,如图1-28所示。
![359b3608091059e30bd2da097f2e379f879cd5fd](https://yqfile.alicdn.com/359b3608091059e30bd2da097f2e379f879cd5fd.png?x-oss-process=image/resize,w_1400/format,webp)
[0,60]U[300,360]为红色区间,[60,180]为绿色区间,[180,300]为蓝色区间。判断RGB颜色的H值位于哪个颜色区间也非常简单,我们只要找出色光反射量最大的那个通道即可。
红色被划分到了两侧,所以当色相位于红色区间时要多加一个条件,但它并不复杂,因为H轴本身是环状结构,左侧与右侧实际上是重合的。所以这一步的判断仅仅是为了确保计算出来的角度始终落在[0, 360]的区间内而不至于出现负数。因此当max=r时,公式可以合并如下。
(60*(g−b)/(max−min)+360)%360
但是我们写程序的时候,一般都还是用逻辑判断来处理,因为取余数的执行效率比较差。
然后我们看看HSL里面L和S的计算方法。
![ed6a1ec568d0b8fbf5d289119f67707cab5cab28](https://yqfile.alicdn.com/ed6a1ec568d0b8fbf5d289119f67707cab5cab28.png?x-oss-process=image/resize,w_1400/format,webp)
可见,只有当max和min都取到通道的最大值,即R=G=B=255时,亮度才能达到100%,换而言之,HSL模式里,亮度最大的只有白色,这跟HSL的色彩空间示意图不谋而合。
max=min or l=0意味着R=G=B,这时候的颜色必然是黑白灰的其中一种,自然就没有饱和度了。
而HSV中,V和S的计算公式如下。
![211f31f542e389b228691dfe1871038abe806b0d](https://yqfile.alicdn.com/211f31f542e389b228691dfe1871038abe806b0d.png?x-oss-process=image/resize,w_1400/format,webp)
作为物理意义是亮度的V,它达到最大值的要求比HSL模式低,RGB 3个通道中只要有一个等于255,亮度就达到最大值。这样的差异,可能导致两种颜色的明暗比较在不同的模式描述下出现不同的结果,即颜色A和颜色B之间存在L(A)>L(B)但V(A)
饱和度为0的定义,两种模式没有差别,而饱和度的计算规则也都以R、G、B通道值的差作为主要的决定因素,再除以亮度从而得到标准化的结果。然后,因为亮度计算的差异,L的色彩变化范围要比V大一倍,所以HSL模式的除数必须等于亮度的2倍才可以使被扩大的范围得以抵消,加上较明亮的颜色,HSV和HSL在饱和度方面没有达成共识,HSL模式的S公式就要写成分段函数了。
而HSV/HSL转RGB则是RGB转HSV/HSL的逆运算,虽然在一些极端条件下,这种运算并不可逆,但请放心,这些问题不会影响到我们对色彩的使用。
比如H=250,S=60,L=100和H=300,S=50,L=100,它们的RGB数值都等于255,这时候再转回HSL,就是H=0,S=0,L=100,值是变了,不过结果都还是白色。这跟地球极点处的经纬度算法非常相似。
1.3.4 HSB与RGB互转公式的ActionScript实现
为了让HSB转RGB这个通用的算法在后续的项目里可以重用,我们先把这个转换公式做成一个工具类并且放入到公共类库里。当然了,这样的类,网上也有很多现成的可以直接拿过来用(个人推荐frocessing工具包)。
类里的算法看起来跟上述的公式略有出入,一方面是因为我们借用了一些现成的代码,另一方面是因为我们把S和L/V的取值范围从0~1改成了0~100。
受篇幅所限,我们就不在这里贴出实现的代码了,读者可自行查阅PublicClasses中的ConvertColor类。
代码清单1-11
package com.gemei.geom {
public class ConvertColor{
//返回一个包含h,s和b3个属性的对象(色相0~360,饱和度0~100,亮度0~100)
public static function RGBToHSB(r:int,g:int,b:int):Object{
}
//返回一个包含h,s和b 3个属性的对象(色相0~360,饱和度0~100,亮度0~100)
public static function HSBToRGB(h:int,s:int,b:int):Object{
}
public static function RGBToHSL(r:int,g:int,b:int):Object{
}
public static function HSLToRGB(h:int, s:int, l:int):Object{
}
}
}