光の波長から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による物理シミュレーション」を参照ください。




