光の波長からRGBを算出する関数の定義
波長とRGBの関係式(変換式)
光は、波長がおよそ380[nm]から750[nm]まで電磁波です。
3次元グラフィックスなどで波長λ[nm]に対応した描画色を与えるために、RGB値を算出する計算式を考えてみます。
CIE(国際照明委員会 Commission internationale de l'eclairage)では、赤(R):700.0[nm]、緑(G):546.1[nm]、青(B):435.8[nm]と定めています。
光の三原色(赤・緑・青)の重ねあわせ
まずは一番単純に、先のCIEで定義された赤・緑・青の波長に合わせて、380[nm]から750[nm]の光のスペクトルをこの3色の重ね合わせで表現してみます。 それぞれの色の明るさは、中心波長を最大値とする正規分布であると仮定します。
ただし、は明るさの最大強度、は各色の中心波長、は正規分布の半値半幅です。子の半値半幅の大きさは、赤・緑・青の各中心波長の波長で、他の色の強度が0.1程度となるように設定しています。
この関係式を用いて光のスペクトルを描画した結果と、RGB値の波長依存性をグラフ化した結果を示します。
光の三原色(赤・緑・青)のみの光のスペクトル
3色が独立しているように見えるため、光のスペクトルというより液晶のドットのようです。 これは、強度グラフを見ると、620[nm]あたりと490[nm]あたりでRGB値が低くなってしまうため、その部分が暗くなってしまっていることに起因します。 そこで、虹色(紫・青・藍・緑・黄・橙・赤)を構成する残りの色(+橙・黄・藍・紫)を加えることで、光のスペクトルを作成します。
光の三原色(赤・緑・青)+橙・黄・藍・紫の重ねあわせ
残りの4色(橙・黄・藍・紫)を同じ表式で考えます。添字は「o:橙」「y:黄」「c:藍」「p:紫」を表します。
ただし、各色の明るさの最大強度を、中心波長を、正規分布の半値半幅はです。4色を追加した際にRGB値の最大値が1となるように、先の赤・緑・青の明るさの最大強度をに再設定します。
それぞれの色はもともとRGB値の混合で表現されるため、その混同割合を次のとおりとします。
orange #ffa500 1: 0.715 : 0.230 yellow #ffff00 1: 1 : 0 cian #00ff00 0: 1 : 1 purple #804080 1: 0.5 : 1
虹色(紫・青・藍・緑・黄・橙・赤)による光のスペクトル
少し不自然な気がしないでもないですが、一応完成しました。 それらしく見えるように試行錯誤しました。より良いパラメータがわかりましたら教えて下さい。
プログラムソース
////////////////////////////////////////////// // 波長からRGB値を算出する関数「waveLengthToRGB関数」 ////////////////////////////////////////////// function waveLengthToRGB( lambda , color ){ //中心波長[nm] var r0 = 700.0; //赤色 var g0 = 546.1; //緑色 var b0 = 435.8; //青色 var o0 = 605.0; //橙色 var y0 = 580.0; //黄色 var c0 = 490.0; //藍色 var p0 = 400.0; //紫色 //半値半幅 var wR = 90; var wG = 80; var wB = 80; var wO = 60; var wY = 50; var wC = 50; var wP = 40; //強度 var iR = 0.95; var iG = 0.74; var iB = 0.75; var iO = 0.4; var iY = 0.1; var iC = 0.3; var iP = 0.3; //正規分布の計算 var r = iR * Math.exp( - ( lambda - r0 ) * ( lambda - r0 ) / ( wR * wR ) ); var g = iG * Math.exp( - ( lambda - g0 ) * ( lambda - g0 ) / ( wG * wG ) ); var b = iB * Math.exp( - ( lambda - b0 ) * ( lambda - b0 ) / ( wB * wB ) ); var o = iO * Math.exp( - ( lambda - o0 ) * ( lambda - o0 ) / ( wO * wO ) ); var y = iY * Math.exp( - ( lambda - y0 ) * ( lambda - y0 ) / ( wY * wY ) ); var c = iC * Math.exp( - ( lambda - c0 ) * ( lambda - c0 ) / ( wC * wC ) ); var p = iP * Math.exp( - ( lambda - p0 ) * ( lambda - p0 ) / ( wP * wP ) ); /* orange #ffa500 1: 0.715 : 0.230 yellow #ffff00 1: 1 : 0 cian #00ff00 0: 1 : 1 purple #804080 1: 0.5 : 1 */ r = r + o + y + p; g = g + o*0.715 + y*0.83 + c + p *0.50; b = b + o*0.23 + c + p; if( r > 1.0 ) r = 1.0; if( g > 1.0 ) g = 1.0; if( b > 1.0 ) b = 1.0; if ( typeof color == "object" ){ color.r = r; color.g = g; color.b = b; } else { return {r:r, g:g, b:b}; } }
参考
・数式の表示は「Tex表記によるHTML文書への式の埋め込み」をご覧ください。 ・グラフ描画は「HTML5による物理シミュレーション」を参照ください。