图形学基础:第1章 颜色

第1章:颜色

任何事物的颜色可以通过三基色(红red、绿green、蓝blue)混合而成,颜色的分量(也有叫做通道)即指red、green、blue的所占比例,除了这三种分量外,我们有时候还需要使用到透明度分量(alpha)


颜色的表示

使用3个float表示颜色

考虑到运算性能和内存开销,使用3个float来表示其red、green、blue分量,一般使用范围为0到1,1为100%

    struct Color3f
    {
        float   _red;
        float   _green;
        float   _blue;

    public:
        Color3f() {}
        Color3f(const Color3f& c):_red(c._red), _green(c._green), _blue(c._blue) {}
        Color3f(float r, float g, float b) : _red(r), _green(g), _blue(b) {}

        Color3f     operator +(const Color3f& c) const { return Color3f(_red + c._red, _green + c._green, _blue + c._blue); }
        Color3f     operator -(const Color3f& c) const { return Color3f(_red - c._red, _green - c._green, _blue - c._blue); }
        Color3f&    operator +=(const Color3f& c) { _red += c._red, _green += c._green, _blue += c._blue; return *this; }
        Color3f&    operator -=(const Color3f& c) { _red -= c._red, _green -= c._green, _blue -= c._blue; return *this; }
        Color3f     operator *(float k) const { return Color3f(_red * k, _green * k, _blue * k); }
        Color3f     operator /(float k) const { return Color3f(_red / k, _green / k, _blue / k); }
        Color3f&    operator *=(float k) { _red *= k, _green *= k, _blue *= k; return *this; }
        Color3f&    operator /=(float k) { _red /= k, _green /= k, _blue /= k; return *this; }
    };

使用4个float表示颜色

使用4个float表示具有透明度的颜色,其中alpha,0表示全透明,1表示不透明

    struct Color4f
    {
        float   _red;
        float   _green;
        float   _blue;
        float   _alpha;

    public:
        Color4f() {}
        Color4f(const Color3f &c) :_red(c._red), _green(c._green), _blue(c._blue), _alpha(1.0f) {}
        Color4f(float r, float g, float b, float a = 1.0f) : _red(r), _green(g), _blue(b), _alpha(a) {}

        Color4f     operator +(const Color4f& c) const { return Color4f(_red + c._red, _green + c._green, _blue + c._blue, _alpha + c._alpha); }
        Color4f     operator -(const Color4f& c) const { return Color4f(_red - c._red, _green - c._green, _blue - c._blue, _alpha - c._alpha); }
        Color4f&    operator +=(const Color4f& c) { _red += c._red, _green += c._green, _blue += c._blue, _alpha += c._alpha; return *this; }
        Color4f&    operator -=(const Color4f& c) { _red -= c._red, _green -= c._green, _blue -= c._blue, _alpha -= c._alpha; return *this; }
        Color4f     operator *(float k) const { return Color4f(_red * k, _green * k, _blue * k, _alpha * k); }
        Color4f     operator /(float k) const { return Color4f(_red / k, _green / k, _blue / k, _alpha / k); }
        Color4f&    operator *=(float k) { _red *= k, _green *= k, _blue *= k, _alpha *= k; return *this; }
        Color4f&    operator /=(float k) { _red /= k, _green /= k, _blue /= k, _alpha /= k; return *this; }
    };

使用4个字节表示颜色

很多时候我们也采用4个字节表示颜色,由于每个字节的表示范围为0-255,可以理解为它将颜色的各分量分成了256个程度,那么除去透明度,它可以表示的颜色共256*256*256种,相比上一种表示方式,它的精度远不如上一种,但节省大量内存开销,事实上目前的显示设备大多也只支持这么多的颜色种数。

    typedef unsigned char   byte;

    union Color4b {
        int     _value;
        struct
        {
            byte    _alpha;
            byte    _blue;
            byte    _green;
            byte    _red;
        };

    public:
        Color4b() {}
        Color4b(const Color4b& c) : _value(c._value) {}
        Color4b(int value) : _value(value) {}
        Color4b(byte a, byte r, byte g, byte b) : _alpha(a), _blue(b), _green(g), _red(r) {}
    };
  • 这里我们使用了联合体定义Color4b结构,这样可以方便了各分量进行访问,也方便了int赋值(形如0xAABBGGRR)。
  • 这里我们没有使用class来定义颜色,因为颜色本来就只是一种描述结构,与类的封装、继承、多态毫无关系,我们仅重载一些运算符方便计算表达。

颜色的混合

不透明颜色和半透明颜色的混合

假设一不透明的背景图像是A,另一幅透明的图象是B,那么透过B去看A,看上去的图象C就是B和A的混合图象

C = (1 - B.alpha) * A + B.alpha * B

Color3f sge::alphaBlend(const Color3f &bgcolor, const Color4f &fcolor)
{
    float pA = 1.0f - fcolor._alpha;
    Color3f out;
    out._red = pA * bgcolor._red + fcolor._alpha * fcolor._red;
    out._green = pA * bgcolor._green + fcolor._alpha * fcolor._green;
    out._blue = pA * bgcolor._blue + fcolor._alpha * fcolor._blue;
    return out;
}

半透明颜色和半透明颜色的混合

半透明玻璃A与半透明玻璃B重叠与单个半透明玻璃C的效果一样,我们可以根据A与B计算出C的颜色和透明度。

可以想象一束光透过透明度为80%的玻璃后光线强度变为80%,再透过一个透明度为60%的玻璃后光线强度剩下48%,由于我们采用不透明度,我们将A、B、C透明度简写为A.a、B.a、C.a,则:

C.a = 1 - ((1 - A.a) * (1 - B.a)) = A.a + B.a - A.a * B.a

我们已经知道,在背景色上如何叠加半透明色,我们可以分为两步,先在背景色上叠上第一个半透明颜色A,然后在叠好的结果上叠上第二个半透明颜色B,设原始背景色为R1,第一次的叠好的结果为T1,全叠好的结果为T2

T1 = (1 - A.a) * R1 + A.a * A;

T2 = (1 - B.a) * T1 + B.a * B

= (1 - B.a) * ((1 - A.a) * R1 + A.a * A) + B.a * B
= (1 - B.a) * (1 - A.a) * R1 + (1 - B.a) * A.a * A + B.a * B

而T2还应等同于R1与C的直接混合结果

T2 = (1 - C.a) * R1 + C.a * C

= (1 - (A.a + B.a - A.a * B.a)) * R1 + (A.a + B.a - A.a * B.a) * C
= (1 - A.a) * (1 - B.a) * R1 + (A.a + B.a - A.a * B.a) * C

联立上述等式,同减(1 - A.a) * (1 - B.a) * R1,可以消除R1,化简得C:

C = [(1 - B.a) * A.a * A + B.a * B] / (A.a + B.a - A.a * B.a)

Color4f sge::alphaBlend(const Color4f &c1, const Color4f &c2)
{
    Color4f out;
    out._alpha = c1._alpha + c2._alpha - c1._alpha * c2._alpha;
    float k = (1.0f - c2._alpha) * c1._alpha;
    out._red = (k * c1._red + c2._alpha * c2._red) / out._alpha;
    out._green = (k * c1._green + c2._alpha * c2._green) / out._alpha;
    out._blue = (k * c1._blue + c2._alpha * c2._blue) / out._alpha;
    return out;
}

本节不能生成可执行程序


本章代码位于sgeMath.h中,github链接https://github.com/xiangwencheng1994/sge/tree/master/lesson1.Color

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值