はじめに
2014/5/3 に Ovrvision SDK v0.6 がリリースされました。
今回のアップデートは以下の様な形です。
- To improved precision of AR markers(Aruco)
- ArUco の AR マーカの精度の向上
- Add chroma key function in the Unity Pro.
- クロマキー機能(Unity Pro のみ)を追加
今回のアップデートの目玉はクロマキー機能です。AR マーカの精度向上に関しては内部的なものなようです。そこで今回はこのクロマキー機能に注目して紹介をしたいと思います。v0.5 のアップデート内容については前回のエントリをご覧ください。
デモ
Sci-Fi な世界に自分の手を持ち込んでみました。グリーンバック持ってなかったので酷い環境ですがご容赦下さい。。
クロマキー機能紹介
クロマキーとは
天気予報などでお天気キャスターの人の背景に投影された天気予報図を良く見ると思うのですが、実際にあの図が背景にあるわけではなく、青ないし緑色の布(グリーンバック)の前に立っている、ということはご存知かもしれません。あれがクロマキーです。緑色の部分を別の映像に置き換えることによって、あたかもその場に居るように表現しているわけです。
Oculus Rift + Ovrvision でクロマキー合成を行うと、更に立体視が可能になります。例えば自分の手をバーチャルな世界に送り込んだり、友人にグリーンバックの前に立ってもらえば、その人がバーチャルアイドルとイチャイチャしている様子も見れるわけです。
仕組みと使い方
仕組みと使い方を同時に見て行きましょう。Ovrvision SDK v0.6 をダウンロードし、その中にあるサンプルの「unity_ar_example\Assets\ovrvision_sence.unity」シーンを開きます。Hierarchy ビューにある OvrvisionSDK を選択して Inspector を見てみると、Ovrvision コンポーネントに新しい項目の「Camera view shader」が追加されているのが分かります。ここで「Chroma-key shader」を選択すると幾つかのスライダが現れます。
スライダを見てみると、Hue / Saturation / Brightness の 3 要素の Max / Min を設定する形になっています。この 3 要素は HSV モデル(cf. RGB)と呼ばれる色の表現方法で、ペイント(mspaint)の色選択のウィンドウで表示されているアレを想像して頂けると分かりやすいと思います(※ H = Hue = 色彩、S = Saturation = 彩度 ≒ Chroma、V = Value = 明度 ≒ Brightness、Lightness です、クロマキーの Chroma key はここから来てるわけですね、多分)。
この図を念頭に置いた上で、もう一度設定可能な項目を見てみましょう。この HSV それぞれの値についての Min / Max が設定できるのでした。これを図解するとこうなります。
このエリア内に当てはまる色がカメラがとしては透明になるわけです。これを調整してやると...、
このカメラ画が、
こんな風に見えるようになります(分かりやすいように背面に真っ赤なプレーンを置いています)。
コードから見るクロマキー
クロマキーの処理はシェーダで行っています。関係の有りそうな場所だけ抜粋したコードが以下になります。
Shader "Ovrvision/ovChromaticMask" { ... SubShader { // 常に最前面 Tags { "Queue" = "Overlay+1" "RenderType"="Overlay" } ... Pass { Lighting Off ZWrite Off ZTest Always Blend SrcAlpha OneMinusSrcAlpha ... float3 RGBtoHSV(float3 RGB) { ... return HSV; } ... float4 frag (v2f i) : COLOR0 { float3 colors = tex2D(_MainTex, i.uv_MainTex).rgb; float3 hsvcolor = RGBtoHSV(colors); // ここでそれぞれの項目を見て当てはまれば棄却(discard = 透明になる) if(hsvcolor.r >= _Color_minh && hsvcolor.r <= _Color_maxh) { if(hsvcolor.g >= _Color_mins && hsvcolor.g <= _Color_maxs) { if(hsvcolor.b >= _Color_minv && hsvcolor.b <= _Color_maxv) discard; } } return float4(colors,1.0); } ENDCG } } FallBack Off }
処理のミソはフラグメントシェーダの中の if 文です。ここで先ほど設定した Min / Max それぞれの値とカメラ画の HSV を比較して当てはまれば discard して透明にしている形になっています。ここを Brightness に応じてアルファを変える形にすると影も反映させることが出来ます。
float4 frag (v2f i) : COLOR0 { float4 colors = float4(tex2D(_MainTex, i.uv_MainTex).rgb, 1.0); float3 hsvcolor = RGBtoHSV(colors); if (hsvcolor.r >= _Color_minh && hsvcolor.r <= _Color_maxh) { if (hsvcolor.g >= _Color_mins && hsvcolor.g <= _Color_maxs) { if (hsvcolor.b >= _Color_minv && hsvcolor.b <= _Color_maxv) { colors.rgb = float3(0, 0, 0); colors.a = _Color_maxv - hsvcolor.b; } } } return colors; }
少し雑なので暗くなってしまっていますが、適切に a をパラメタ調整してやれば良くなると思います。