光の波長をRGBに変換 [Blender Wavelength node]

スポンサーリンク

我々が見ている「」は、実際は異なる波長の光が混ざりあったものですが、CGレンダリングでは通常波長は考慮しません。(一部のレンダラーは波長まで考慮するものがあるそうですが、少なくともBlender cyclesレンダラーは波長を考慮しません)そこで、Blenderで波長を扱いたい場合、 波長をRGB値に変換する必要があります。
実はBlenderにはWavelengthノードと呼ばれる、波長をRGBに変換するノードが存在するのですが、実際にどういう計算をしているのかは知っておいたほうが良いので調べてみることにしました。 同じことを考えている人はいたみたいですが、このフォーラムでは満足な答えが得られていなかったので、独自に調べました。

スポンサーリンク

波長RGB変換機

Blender Wavelength nodeのアルゴリズムを調べて、Webで同じ結果が出るようにした計算機を作りました。380 nm ~ 780 nm の波長に対応しています(wavelengthノードも同様)。好きな波長を入力すると、それに対応したRGBの値が、0~1の浮動小数点(float)、8bitの10進数表記、8bitの16進数カラーコード表記で表示されます。
またプリセットとして、CIE(国際照明委員会 Commission internationale de l’eclairage)で定められた、赤(R):700.0 [nm]、緑(G):546.1[nm]、青(B):435.8[nm]と、等色関数(後述)から得られる、純粋なRGB成分が出る波長を用意しています。
※700nmは、この変換式ではほぼ黒(に寄った赤)になります。

Wavelength (波長) [nm] : nm

Color(float) : R,G,B = 0.00000,0.00000,0.00000
Color (8bit decimal) : R,G,B = 0, 0, 0
Color (8bit hexadecimal) : RGB = #000000
■色見本■

解説

CIE測色標準観察者等色関数(XYZ色空間)

Blenderのソースコードをwavelengthで検索すると、変換の仕組みが見えてきました。このWavelengthノードは CIE 1931 色空間をベースにしている様子です。 CIEのCIE測色標準観察者等色関数(The CIE XYZ standard observer color matching functions)がソースコードに組み込まれていました。

2024/02追記:Blenderソースコードの構成がいつの間にか変わってたので再リンクしました。また、CIE関数が埋め込まれているソースも別ファイルに切り出されてました。
(/intern/cycles/kernel/svm/svm_wavelength.h → /intern/cycles/kernel/svm/wavelength.h)

Figure 1. CIE測色標準観察者等色関数

グラフの元になるのは、以下のデータです。

Table 1. CIE測色標準観察者等色関数

IDIncrementWavelength (nm)xBaryBarzBar
003800.00140.00000.0065
153850.00220.00010.0105
2103900.00420.00010.0201
3153950.00760.00020.0362
4204000.01430.00040.0679
5254050.02320.00060.1102
6304100.04350.00120.2074
7354150.07760.00220.3713
8404200.13440.00400.6456
9454250.21480.00731.0391
10504300.28390.01161.3856
11554350.32850.01681.6230
12604400.34830.02301.7471
13654450.34810.02981.7826
14704500.33620.03801.7721
15754550.31870.04801.7441
16804600.29080.06001.6692
17854650.25110.07391.5281
18904700.19540.09101.2876
19954750.14210.11261.0419
201004800.09560.13900.8130
211054850.05800.16930.6162
221104900.03200.20800.4652
231154950.01470.25860.3533
241205000.00490.32300.2720
251255050.00240.40730.2123
261305100.00930.50300.1582
271355150.02910.60820.1117
281405200.06330.71000.0782
291455250.10960.79320.0573
301505300.16550.86200.0422
311555350.22570.91490.0298
321605400.29040.95400.0203
331655450.35970.98030.0134
341705500.43340.99500.0087
351755550.51211.00000.0057
361805600.59450.99500.0039
371855650.67840.97860.0027
381905700.76210.95200.0021
391955750.84250.91540.0018
402005800.91630.87000.0017
412055850.97860.81630.0014
422105901.02630.75700.0011
432155951.05670.69490.0010
442206001.06220.63100.0008
452256051.04560.56680.0006
462306101.00260.50300.0003
472356150.93840.44120.0002
482406200.85440.38100.0002
492456250.75140.32100.0001
502506300.64240.26500.0000
512556350.54190.21700.0000
522606400.44790.17500.0000
532656450.36080.13820.0000
542706500.28350.10700.0000
552756550.21870.08160.0000
562806600.16490.06100.0000
572856650.12120.04460.0000
582906700.08740.03200.0000
592956750.06360.02320.0000
603006800.04680.01700.0000
613056850.03290.01190.0000
623106900.02270.00820.0000
633156950.01580.00570.0000
643207000.01140.00410.0000
653257050.00810.00290.0000
663307100.00580.00210.0000
673357150.00410.00150.0000
683407200.00290.00100.0000
693457250.00200.00070.0000
703507300.00140.00050.0000
713557350.00100.00040.0000
723607400.00070.00020.0000
733657450.00050.00020.0000
743707500.00030.00010.0000
753757550.00020.00010.0000
763807600.00020.00010.0000
773857650.00010.00000.0000
783907700.00010.00000.0000
793957750.00010.00000.0000
804007800.00000.00000.0000

これは波長をCIE XYZ 色空間の値に変換するもので、これを基底変換してRGBに変換することができるそう。(歴史的には、RGBが先でXYZが後にできた色空間だそうです)変換については、詳細なまとめ記事があったのでそちらを参照してください。

XYZ色空間に迫る(2) - Qiita
XYZ色空間に迫る(1) からの続きになります。いよいよ本題のXYZ色空間です。イケてないRGB色空間前回で1931 CIE RGB等色関数を手に入れました。そしてRGBの三値刺激は空間の座標…

XYZ色空間からRGB色空間へ

CIEの定義

CIE RGB色空間からCIE XYZ色空間への変換は、線形変換として定義できます。CIE特別委員会で定義された変換式は以下のような形です。


$$\begin{pmatrix} X \\ Y \\ Z \end{pmatrix}=\begin{pmatrix} 2.7688 & 1.7517 & 1.1301 \\ 1.0000 & 4.5906 & 0.060067 \\ 0.0000 & 0.056507 & 5.5942 \end{pmatrix}\begin{pmatrix} R_{\mathrm{CIE}} \\ G_{\mathrm{CIE}} \\ B_{\mathrm{CIE}} \end{pmatrix}\tag{1}$$
$$\begin{pmatrix} R_{\mathrm{CIE}} \\ G_{\mathrm{CIE}} \\ B _{\mathrm{CIE}}\end{pmatrix}=\begin{pmatrix} 0.41847 & -0.15866 & -0.082835 \\ -0.091169 & 0.25243 & 0.015708 \\ 0.00092090 & -0.0025498 & 0.17860 \end{pmatrix}\begin{pmatrix} X \\ Y \\ Z \end{pmatrix}\tag{2}$$

この変換式は、WikipediaのCIE1931色空間のページにあるものと同等のものです。(有効数字5桁)

CIE 1931 色空間 - Wikipedia

上記のCIE測色標準観察者等色関数とCIE RGB色空間からCIE XYZ色空間への変換式を組み合わせることで、波長をRGBに変換することができます。(ページ上部変換器1)
しかし、

ITU Rec.709の定義

Blenderソースコード中で見つかった変換式は、上記のものとは違っていました。

調べたところ、Blenderのノード中では、ITU Recommendation BT.709 (Rec.709) 準拠の変換式が組み込まれているようです。これは白色点(Whitepoint) D65での変換式とのこと。Blenderはグラフィックソフトなので、現代の映像で一番用いられている色空間を採用しているということですかね。


$$\begin{pmatrix} X \\ Y \\ Z \end{pmatrix}=\begin{pmatrix} 0.412453 & 0.357580 & 0.180423 \\ 0.212671 & 0.715160 & 0.072169 \\ 0.019334 & 0.119193 & 0.950277 \end{pmatrix}\begin{pmatrix} R_{709} \\ G_{709} \\ B_{709} \end{pmatrix}\tag{3}$$
$$\begin{pmatrix} R_{709} \\ G_{709} \\ B_{709} \end{pmatrix}=\begin{pmatrix} 3.240479 & -1.537150 & -0.498535 \\ -0.969256 & 1.875992 & 0.041556 \\ 0.055648 & -0.204043 & 1.057311 \end{pmatrix}\begin{pmatrix} X \\ Y \\ Z \end{pmatrix}\tag{4}$$

2024/02追記:Blenderソースコードの構成がいつの間にか変わってたので再リンクしました
(/intern/cycles/kernel/shaders/node_color.h#L76 → /intern/cycles/kernel/osl/shaders/node_color.h#L75

細かい処理

また、ソースコードには、

color *= 1.0f / 2.52f;  // Empirical scale from lg to make all comps <= 1

という部分があり、得られたRGBの値を2.52で割っていることがわかりました。調べたところ、ソニー・ピクチャーズ傘下のImageworks社が開発する、OSL(Open Shading Language)のソースコードから転用されていることがわかりました。すべての値が1を下回るようにするための経験的な値ということですね。

さらに、最後に0を下回った(負の)値を0にクランプする処理が行われて完成です。

  /* Clamp to zero if values are smaller */
  color = max(color, make_float3(0.0f, 0.0f, 0.0f));

ということで、BlenderのWavelength node(波長ノード)の仕組みを解き明かしました。
この計算式をWeb上で計算できるようにした計算機がこのページ上部にありますので、活用してください。Wavelengthノードと全く同じ色になるはずです。

コメント

  1. Y.S. より:

    式(4)の行列の2行1列成分は−0.969256では?

    • CGBeginner より:

      コメントありがとうございます。おっしゃる通り間違っていたので修正しました。

タイトルとURLをコピーしました