9

色彩空间与带区间的色相调整

 3 years ago
source link: https://wuzhiwei.net/colorspace_and_hueshift/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

色彩空间与带区间的色相调整

在游戏的战斗中,两军对垒,需要让玩家区分敌我,颜色是一个很重要的标识。

Blue CavalryRed Cavalry

上图是一个蓝方骑士和一个红方骑士,可以看出他们除了标识颜色不一样外,其余完全一样。假使在游戏内的士兵用序列帧动画来实现,如果两个阵营用两套序列帧来实现的话。首先美术工序变得复杂,其次会导致游戏内存消耗增大,最后还会使包体积变大。

真实的情况是图片只有蓝方骑士,红色骑士只不过在运行时通过色相转换HueShiftHueShift得到的。

在了解如何进行色相转换之前,我们有必要了解什么是色彩空间。

色彩空间是描述使用一组值表示颜色方法的抽象数学模型,比如绝大多数人都知道的三原色模型就是一种色彩空间。

RGB色彩空间指的是用红(Red)绿(Green)蓝(Blue)这三种原色描述任意颜色的色彩空间。

RGB Model

为什么采用红绿蓝作为三原色?是因为人的眼睛的锥形感光细胞对红绿蓝的光线比较敏感。

通常在计算机内部用8个bit来分别描述红绿蓝的强度,即数字从0到255,0表示此颜色强度最低(完全没有),而255则表示此颜色的强度最强。

RGB采用简单的加法来描述所有颜色,比如黄色为(255, 255, 0),黑色为(0, 0, 0),白色为(255, 255, 255)。

HSV是RGB的一种变形,分别用色相(Hue)饱和度(Saturation)明度(Value)三个数值来描述颜色。

HSV相较于RGB用人类更为熟悉的方式封装了关于颜色的信息:

这是什么颜色?深浅如何?明暗如何?

  • 色相指的是色彩的基本属性,回答了所问的第一个问题:这是什么颜色?通常色相表达为一个环,称为色环,对应角度为0到360度。
  • 饱和度指的是色彩的纯度,越高则表示色彩越纯,越低则逐渐变灰,回答了所问的第二个问题:深浅如何?数值为百分比,即0到1。
  • 明度指的是颜色的亮暗程度,越高则越显得发白,越低则越显得发黑,回答了所问的第三个问题:明暗如何?数值为百分比,即0到1。

HSV可以用一个圆锥来表示,有助于建立直观的理解。

HSV Model

HSV和RGB间的转换有一套比较复杂的公式,这不在本文的讨论范围内。

除了RGB和HSV外,还有很多其他的色彩空间,比较知名的有与HSV相似的HSL,印刷常用的CMYK和电视常用的YUV

在了解完色彩空间后,现在回到最开始的问题,如何在运行时将蓝方士兵渲染成红方士兵呢?

答案很简单,对蓝方士兵进行指定范围内(蓝色)的色相调整,即对色相落入一定范围内的像素的色相进行调整。

shader

关于色相调整的shader网络上有很多开源实现,譬如hsva-unity是可以直接在Unity内使用的shader

它的核心代码很简单:

float4 frag(Fragment IN) : COLOR
    //获取该像素原图的RGBA颜色值
    float4 color = tex2D (_MainTex, IN.uv_MainTex);
    //将RGB转化为HSV格式
    float3 hsv = rgb2hsv(color.rgb);
    //影响因子乘数,只对色相处在[_HSVRangeMin, _HSVRangeMax]返回1,其余则返回0
    float affectMult = step(_HSVRangeMin, hsv.r) * step(hsv.r, _HSVRangeMax);
    //将原HSV进行修改色相,饱和度和明度的修改,仅在影响因子为1时修改生效,再将修改后的HSV转化为RGB格式
    float3 rgb = hsv2rgb(hsv + _HSVAAdjust.xyz * affectMult);
    //返回最终该像素的RGBA,其中透明度通道也加上施加调整的影响
    return float4(rgb, color.a + _HSVAAdjust.a);

其中_HSVRangeMin定义了可以被调整的色相下限,_HSVRangeMax则定义了上限。

step(edge, x)函数在x小于edge时返回0,否则返回1。

float affectMult = step(_HSVRangeMin, hsv.r) * step(hsv.r, _HSVRangeMax);

这段代码表示HSV的第一个分量(即色相)在区间[_HSVRangeMin, _HSVRangeMax]内时返回1,否则返回0,即指定了可以进行调整的色相范围。

Hue Bar

根据色相条,可以知道蓝色的色相范围大概在180到260间,如果按0-1表示,则在0.5到0.7间。

所以我们把_HSVRangeMin设置为0.5,把_HSVRangeMax设置为0.7,然后进行色相调整,可以变换出无数种颜色来。

Hue Shift in Unity

玩具Demo放在了Github,玩的开心 😎


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK