GLSL
に限らず画像処理やエフェクトを書いていると、RGB
座標系だけで考えていると限界があります。
別の色空間に変換して処理することで出来ることが増えたり、処理が楽になったりします。特に色合いと色の強度を分離できると多くの場面で役立ちます。
普段よく使うYUV
色空間とHSV
色空間をGLSL
でも使ってみます。
YUV
YUV
(YCbCr
、YPbPr
)は輝度Yと、青・赤の色差によって表現する色空間で動画などで多く用いられます。
映像規格ITU-R BT.709
に基づいて変換してみます。
単純な行列を使った線形変換です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
//ITU-R BT.709 precision highp float; varying vec2 uv; uniform sampler2D t; uniform vec2 resolution; void main(){ mat3 rgb2yuv=mat3( .2126,.7152,.0722, -.114572,-.385428,.5, .5,-.454153,-.045847 ); mat3 yuv2rgb=mat3( 1.,0.,1.5748, 1.,-.187324,-.468124, 1.,1.8556,0. ); vec3 rgb=texture2D(t,uv).rgb; vec3 yuv=rgb*rgb2yuv; //輝度Y yuv.x+=0.1;//0 ~ 1 //色差(Cr, Cb) yuv.y=.5;//-0.5 ~ 0.5 yuv.z=-.5;//-0.5 ~ 0.5 gl_FragColor=vec4(yuv*yuv2rgb,1); } |
行列の掛け算が逆じゃない?って思った方は以前の投稿を参照してください。
この変換では輝度を少し増やして、色相を固定しています。
余談ですが、この変換の輝度Y
をそのままRGB
に適応すると同規格のグレースケール変換になります。
HSV
HSV
は色相(Hue
)と彩度(Saturation
)、明度(Value
)で表す色空間です。
色相環はペイントソフト等でもよく使いますね。
非線形変換で多少複雑な計算になります。
色環を[0~2π]
、0
を赤として円錐モデルで変換します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
precision highp float; varying vec2 uv; uniform sampler2D t; uniform vec2 resolution; float tau=acos(-1.)*2.; void main(){ vec3 rgb=texture2D(t,uv).rgb; //rgb->hsv 計算 float ma=max(max(rgb.r,rgb.g),rgb.b); float mi=min(min(rgb.r,rgb.g),rgb.b); float s=ma-mi,v=ma; float h=ma==rgb.r?(rgb.g-rgb.b)/s:ma==rgb.g?(rgb.b-rgb.r)/s+tau/3.:(rgb.r-rgb.g)/s+2.*tau/3.; //色相をx座標に合わせてずらす h=mod(h+uv.x*tau,tau); //彩度をいじる s+=.5; //明度をいじる v=.9; //hsv->rgb 計算 float r,g,b; float unit=tau/6.; ma=v; mi=ma-s*ma; if(h<=unit){ r=ma; g=h/unit*s+mi; b=mi; }else if(h<2.*unit){ r=(2.*unit-h)/unit*s+mi; g=ma; b=mi; }else if(h<3.*unit){ r=mi; g=ma; b=(h-2.*unit)/unit*s+mi; }else if(h<4.*unit){ r=mi; g=(4.*unit-h)/unit*s+mi; b=ma; }else if(h<5.*unit){ r=(h-4.*unit)/unit*s+mi; g=mi; b=ma; }else if(h<6.*unit){ r=ma; g=mi; b=(6.*unit-h)/unit*s+mi;; } gl_FragColor=vec4(r,g,b,1); } |
hsv->rgb
の変換が特にめんどくさいですね。
RGB
がすべて同じ場合の処理は無視(赤として処理)してます。
また余談ですが彩度がSaturation
なのにどうしても違和感があります。photometric
な世界だと飽和のほうで使われるので。
HSV
では特に明るさに関わらない色そのものに対して処理できます。
照明の当たり方の異なる同じ物体に対して彩度、明度を固定する(色相のみの情報にする)とこうなります。
R=G=B
の情報が落ちてますが、明るさに関わらず処理できることがわかります。