カラーモデル

6と7の節では、2つのワークフローをまとめました。そうすればシェーダーに入力されたカラーデータは期待どおりであることを確保できます。シェーダーは入力された色データを処理します。この節では、いくつかの色表現と一般的な処理方法について説明します。


RGBカラーモデル

人間には主に3種類の錐体細胞があり、色認識を行うときに赤(波長約600nm)、緑(波長約550nm)、青(波長約450nm)に最も敏感です。これは、私たちが知覚できる色の領域を決定します。この理論に基づいて、RGBカラーモデルが作成されました。RGBカラーモデルは、色を表すために3つのチャネルの線形結合を使用して、私たちが最もよく目にするカラーモデルです。3つのカラーチャネルはそれぞれ、赤(Red)、緑(Green)、青(Blue)です。

(Unity RGB色調整のインターフェース)

これらの3つの変数にはどの色も関連付けられているため、色を継続的に変換するのは直感的ではありません。これらの3つの変数はすべて明るさに関連しているため、明るさが変更されると、それに応じて3つの変数すべてが変更されます。さらに、人間の目は3色の変数に対してさまざまな感度を持っており、人間の目は赤に対して最も感度が低く、青に対して最も感度が高くなっています。これにより、RGBモデルで色の特定の値をより正確に推測することが困難になります。この欠陥に基づいて、アーティストが開発するとき、RGBカラーモデルの代わりに、より直感的なHSVカラーモデルを使用します。


HSVカラーモデル

HSVカラーモデルを説明する前に、まず、スペクトルカラーという概念を理解する必要があります。

多色光が分散系(プリズムやグレーティングなど)で分割された後、分散された単色光は波長順に並べています。波長が約400nm〜770nmの可視光波長範囲の単色光に対応する色をスペクトル色と呼びます。

RGBと比較して、HSVはより直感的なカラーモデルです。各部分の意味は次のとおりです。

H:色相、色合い(Hue)、角度で測定、値の範囲は0°〜360°で、赤から反時計回りに計算されます。対応するスペクトル色は波長に応じて選択されていると理解できます。

S:彩度、飽和度、色の純度(Saturation)、値の範囲は0%〜100%で、スペクトル色に近い度合いを示します。任意の色を特定のスペクトル色を白と混合した結果として見ることができます。スペクトル色の白色光成分が0で、彩度は最も高いのです。

V:明度、色の明るさ(Value)、色のもつ明るさの度合いを示します。値の範囲は0%(黒)〜100%です。

上図に示されているモデルでHSVカラーモデルを示す。円柱の断面は極座標として見ることができます。Hパラメータは極座標の極角で表され、Sパラメータは極座標の極軸の長さで表され、Vパラメータは円柱の中心軸の高さで表されます。

RGBカラーモデルでは、赤はRGB =(255、0、0)によって決定されます。HSVモデルでは、パラメータH=0を表すことができます。

パラメータH=0の場合の半辺断面は次のとおりです。

この図から、次のことがはっきりとわかります。

水平方向は左から右です。パラメータSの値が大きくなると、彩度が高くなるほど色が暗くなり、スペクトル色に近づきます。逆に、パラメータSの値が小さくなると、彩度が小さいほど色が薄くなり、色が白に近くなります。彩度0は真っ白です。

垂直方向は下から上です。パラメータVの値が大きくなるほど、明るさが高くなるほど色が明るくなります。逆に、パラメータVの値が小さくなると、明るさが低くなるほど色が濃くなります。明度0は、純粋な黒を意味します。

HSVカラーモデルを使用すると、単一の色を簡単に取得して、指定したスペクトル色の明るさと彩度を調整できます。


相互変換

基礎的な内容

RGBカラーモデルとHSVカラーモデルの間には特定の変換式があります。

Unityには、変換用の対応するAPIがあります。

public static Color HSVToRGB(float H, float S, float V);
public static void RGBToHSV(Color rgbColor, out float H, out float S, out float V);

Unrealには対応するブループリントノードがあります。

ただし、Shaderにはこのような変換を直接実行できる関連関数はなく、2つのカラーモデル間の変換関係を理解する必要があります。

RGBカラーモデルの場合、デカルト座標系に基づいて表すことができます。3つのチャネルR、G、およびBの量は同じで、デカルト座標系を介して、上の図に示されている幾何学的モデルを構築します。座標の原点で表される色は黒、座標(1、1、1)で表される色は白です。ベクトル(1、1、1)は、明度の向上を表すことができ、その意味は、HSVカラーモデルのパラメーターVの座標軸に相当します。

HSVカラーモデルでは、デカルト座標系のRGBカラーモデルの点が円筒座標系で表されていると考えることができます。2つの座標系には多くの変換方法があり、異なるカラーモデルも生成します。

上の図に示すように、最初に、RGBカラーモデルの原点(0,0,0)から白色点(1,1,1)までのベクトルの延長線を円筒座標系のZ軸と見なされ、原点(黒)は円筒座標系の原点と見なされます。次に、さまざまなルールに従ってさまざまなマッピングを作成します。その中で、HSVモデルは、Red(赤)、Green(緑)、Blue(青)、Cyan(青緑)、Magenta(赤紫)、Yellow(黄)、White(白)の7色をRGBで構成される平面に投影し、六角錐という幾何学的モデルを取得します。そしてこの六角錐を六角柱に拡張し、さらにそれを円柱に拡張すると、得られたモデルはHSV幾何学的モデルです。その結果、RGBカラーモデルとHSVカラーモデルの色の間に1対1のマッピング関係が形成されます。

 

RGBカラーモデルからHSVカラーモデルへの変換

(r、g、b)をRGBカラーモデルの幾何学的モデルの色のRGB座標とし、それらの値は(0、1)の間の実数です。 maxをr、g、bの最大値、minをr、g、bの最小値とします。これをHSVカラーモデルのポイントに変換します。次のように計算されます。

ここで、hの値は0°から360°の間にあるように正規化されています。

上記の数学モデルに基づいて、Unityでshaderコードをビルドします。まず、数式のデータを0から1の範囲に正規化します。これは、すべてを360°で割ることに相当します。数式を並べ替えて、各関数に必要なデータを取得します。

必要なデータは、lerp関数とstep関数の組み合わせによって選択されます。step関数は、r、g、bの3つの量のサイズを比較するために使用され、対応するベクトルは、サイズの結果に従ってlerp関数によって選択されます。除数が0になるのを防ぐために、1.0e-10を使用して計算に参加します。残りのsとvの計算は比較的簡単です。

 

シェーダーコードを取得します。

float3 RGBToHSV(float3 c)
{
	float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
	float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
	float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));
	float d = q.x - min(q.w, q.y);
	float e = 1.0e-10;
	return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

HSVカラーモデルからRGBカラーモデルへの変換

(h、s、v)をHSVカラーモデルの円筒座標系の色の座標とします。 hの値は0°から360°の間にあるように正規化されます;sの値は0から1の間に正規化され、彩度を表します;vの値は0から1の間に正規化され、明度を表します。これをRGBカラーモデルのポイントに変換します。次のように計算されます。

同じく、変数Hの値を(0,1)の間に正規化します。

上記の数学モデルによれば、UnityでShaderコードを構築します:3つの変数r、g、bの計算式は分解し、異なるhiに応じた計算式の表は次のとおりです。

3つの変数の計算は、オフセットを使用して同じ式に分類できます。変数Rの式の計算に基づくと、2つの変数GとBのオフセットはそれぞれ2/3と1/3です。つまり、コード内のk.xyzです。

観察から、各計算方法はlerp関数の形式で表すことができることがわかります。

変数pの場合、v * lerp(1、0、s)に変換できます。

変数qの場合、v * lerp(1、1-f、s)に変換できます。

変数tの場合、v * lerp(1、f、s)に変換できます。

変数vの場合、v * lerp(1、1、s)に変換できます。

このことから、shaderコードがわかりやすくなります。変数から3を引いて絶対値を求めることで、変数qと変数tの演算を区別することができます。変数pと変数vの計算は、saturate関数によって区別できます。

コードを取得します。

float3 HSVToRGB(float3 c)
{
	float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
	float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
	return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y);
}

UWA公式サイト:https://jp.uwa4d.com

UWA公式ブログ:https://blog.jp.uwa4d.com

UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com