Shader 中的顏色計算

Hahnn發表於2018-09-20

下面介紹 Shader 中 gl_FragColor 的計算與轉換:

一、顏色計算

1. 加

這裡要講講三原色和三基色:三原色一般指的是紅、綠、藍三種,簡稱 RGB,這是加色系。就是光源只含有特定的波段,本身就是色光,將不同顏色的光加在一起形成新的顏色。典型的例子是螢幕,關係如下:

Shader 中的顏色計算

顯然,shader 中顏色的加法運算子合加色系規律。當我們把顏色相加時,會形成新的顏色,並且顏色會往白色靠攏。顏色的混合規律符合三原色規律。

Shader 中的顏色計算

2. 乘

講完三原色,再講講三基色:一般指的是顏料三原色,在純白光照射下顏色為絳紅、黃、青,簡稱 CMYK,屬於減色系它們本身不發光,靠反光被看見。由於材料吸收特定波段的光,所以只有不被吸收的部分反射了回來。加上的顏色越多吸收的光也越多。

Shader 中的顏色計算

當我們使用乘法來做顏色混合時,其規律符合三基色的混合規律,這個時候又是減色系。

Shader 中的顏色計算

兩個顏色相乘,會算出兩個顏色中 RGB 值的乘積併合成一個新的顏色。而且顏色總會越來越暗,回不到原來的白色。通常將顏色和一個值相乘,來弱化這個顏色。

3. 減

單純的顏色相減似乎沒有意義,不過通過1.0-color可以實現顏色的反相。

Shader 中的顏色計算

Shader 中的顏色計算

4. 真正的顏色混合

在圖片或視訊濾鏡中,一般不會直接使用加減乘除來做顏色混合。而是使用 mix() 函式,它的公式是:x*(1−a)+y*a,其實也是顏色相加,但是算上了一定的比重。這樣不會因為一個白色的顏色和其他顏色相加後只有白色,現實世界中也不是這樣的。

Shader 中的顏色計算

mix()可以做單通道或多通道的融合:

Shader 中的顏色計算

①. 簡單的顏色漸變

回到上面的案例,通過加法來表示重疊區域:

Shader 中的顏色計算

通過 mix() 來混合兩個顏色的過渡:

Shader 中的顏色計算

為什麼加法和mix()得到的過渡顏色不一樣?各位可以思考一下。

②. 複雜的顏色漸變

為 rgb 三個通道賦以不同的函式變化曲線。plot 是封裝好的畫線函式,以xy二維笛卡爾座標系做曲線的繪製,pct 表示x軸的變化速率,當x是線性變化時,曲線為直線。當x是非線性變化時,會有不一樣的曲線,從而導致漸變色的多樣變化:

Shader 中的顏色計算

二、顏色轉換

1. 基於笛卡爾座標系

RGB 是對機器很友好的色彩模式,但並不夠人性化,因為我們對色彩的認識往往是”什麼顏色?鮮豔不鮮豔?亮還是暗?”。HSL 模式和 HSV(HSB) 都是基於 RGB 的,是作為一個更方便友好的方法建立出來的 —— refer

  • HSL 為 色相,飽和度,亮度
  • HSV 為色相,飽和度,明度
  • HSB 為 色相,飽和度,明度

下圖表達了兩種顏色模型對人類來說的易理解程度:

Shader 中的顏色計算

HSL 和 HSB/HSV 又有一些區別:

Shader 中的顏色計算

這裡提供轉換公式:

// RGB 轉 HSB
vec3 rgb2hsb( in vec3 c ){
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz),
                 vec4(c.gb, K.xy),
                 step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r),
                 vec4(c.r, p.yzx),
                 step(p.x, c.r));
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),
                d / (q.x + e),
                q.x);
}

// HSB 轉 RGB
//  Function from Iñigo Quiles
//  https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ){
    vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
                             6.0)-3.0)-1.0,
                     0.0,
                     1.0 );
    rgb = rgb*rgb*(3.0-2.0*rgb);
    return c.z * mix(vec3(1.0), rgb, c.y);
}
複製程式碼

那我們可以怎麼應用 HSB 顏色呢?

當我們讓色相 Hue 從0~1 遞增時,你會發現所有顏色都一一取到了(這裡的飽和度和亮度都設定為 1):

Shader 中的顏色計算

假設讓亮度也一樣從0~1,看看效果會如何:

Shader 中的顏色計算

你會發現水平方向的亮度變化不好看,如果是垂直方向的呢?

Shader 中的顏色計算

再改一下垂直方向的飽和度,你會發現有了 HSB,一切顏色變化都更好理解了:

Shader 中的顏色計算

2. 極座標系

HSB 原本是在極座標下產生的(以半徑和角度定義)而並非在笛卡爾座標系(基於xy定義)下。將 HSB 對映到極座標我們需要取得角度和到畫素屏中點的距離。由此我們運用 length() 函式和 atan(y,x) 函式。

當用到向量和三角學函式時,vec2, vec3 和 vec4 被當做向量對待,即使有時候他們代表顏色。我們開始把顏色和向量同等的對待,事實上你會慢慢發現這種理念的靈活性有著相當強大的用途。—— refer

Shader 中的顏色計算

相關文章