映像との相互作用
Last update: <2004/03/13 17:30:57 +0900>
TVや映画の映像は,人間が何も行動しなくても一方的に(勝手に)映像が表示されます.決められた通りに編集された映像が放映(上映)されるだけで,人間はただの傍観者にすぎません.これは情報の一方通行を意味します.
情報の一方通行ではない例は,今,目の前にあるような計算機のシステム,TVゲームなどでしょうか.マウス操作,キーボード操作あるいはコントローラの操作によって,人間の知的活動を計算機にさせたり,キャラクターを動かしてゲームを楽しむことが可能となります.こうしたシステムでは人間の入力操作が映像に反映され,対話的な環境が生じます.このような情報のやり取りが発生するような対話的な操作を相互作用(interaction)といいます*15.
対話的な(インタラクティブな)映像とそうでない映像の決定的な違いは,作り置きできるかどうか,ということです.映画やTV放送はあらかじめ映像を作っておけるわけですから,映像生成には時間をかけることが可能です.しかし,インタラクティブ映像は人間の入力に対して即座に反応した映像を生成する必要があります.このことは第1回で少し触れましたが,インタラクティブ映像ではリアルタイム性重視の映像生成が不可欠であり,この映像生成ツールとしてはOpenGLが極めて有用です.
さて,これまで演習で作成してきた映像は,人間の入力を受け付けない単なるムービー*16でした.今回はインタラクティブな映像について考えてみます.
インタラクティブな映像を生成する際の入力装置として,ここでは世間一般的に用いられているものと,VRで良く使われている代表的なものを列挙します.
- キーボード
もはや説明の必要がないでしょう.人間が計算機に指示を出すのに用いられている装置で,現在のところ最も普及している装置です.システム側は打鍵の有無を検知することにより,人間の入力を受け付けます.
- マウス
これも説明の必要がないでしょう.人間が計算機に指示を出すのはキーボードと同じですが,2次元のポインティングができる装置であり,Windows等のGUIにより広く普及している装置です.システム側はマウスのボタンのON・OFF情報と,2次元の移動量(x,y)を検知することにより,人間の入力を受け付けます.
- トラックボール
ちょうどボール回転式マウスをひっくり返した形で,ボール回転面を手先で転がすことにより使用します.マウスと同様,2次元の移動量を検知することにより,人間の入力を受け付けます.
- SpaceBall
ゴルフボール大のボール型センサを指先で押す,引く,まわすなどして,並進3自由度,回転3自由度,合計6自由度の操作入力が可能となっています.
- SpaceMouse
SpaceBallの玉が円筒形になったもので,原理的には同じです.
- CyberGlobe
手袋型の装置です.手袋に光ファイバーのセンサが装着してあり,曲げ角によって光線の通過量が変化することから,手の指先の角度を測定します.データグローブともいいます.本学のVR室にもあります.なお,姉妹品のCyberTouchは振動フィードバック付きのデータグローブ,CyberGraspはエグゾスケルトン型力覚フィードバック付きのデータグローブです.
- 3SPACE(Fastrak,Isotrak)
POLHEMUS社製の3次元磁気センサです.社名よりポヒマスセンサ,あるいはポヒマスと呼ばれています.VR関係者でこれを知らないと相当恥ずかしい,それほど有名なセンサです.本学のVR室にもあります.仕組みは単純で,コイル(トランスミッタ)によって周囲に微弱な磁場をつくり,センサ(レシーバ)が磁場を読み取ることによって,トランスミッタとレシーバの位置姿勢関係が測定される,というものです.磁場を使うので周囲に金属物体があると干渉します.位置姿勢の6自由度を測定できます.
- フォースディスプレイ
人間に力感覚をフィードバックする装置のことです.ハプティックディスプレイ(Haptic Display)と呼ぶこともあります.通常は3自由度,6自由度のロボットアームで構成されており,人間がアームの先端部分を把持して空間的な位置姿勢入力をおこなうことができます.またアームにはアクチュエータが取り付けられており,先端部分に反力を呈示することも可能です.前述してきた装置は単なる入力装置ですが,この種の装置では装置自身だけ入出力が可能となります.
代表的な装置として,SensAbleというベンチャー企業が販売しているPHANToMがあります.
さて,演習ではマウスとキーボードによって物体を操作することをやります.物体の位置姿勢をキーボードやマウスで変化させるためには,その物体の位置姿勢をパラメータで指定しなければなりません.次の節ではパラメータによる指定方法について述べます.
3次元空間で物体の位置を特定するためには3つのパラメータ(x,y,z)を使用します.このことは前回の勉強会で既に述べた通りです.位置の指定方法はこの(x,y,z)を使い,平行移動(Trans)を使えば一意に決まります.
さて,問題は姿勢の指定方法です.前回の勉強会ではx軸周りの回転,y軸周りの回転,z軸周りの回転,という形で説明しました.この指定方法はある軸周りだけの回転(例えばx軸周り)なら簡単に表現できますが,回転を組み合わせて逐次変換すると非常にややこしくなります.ここでは回転移動の一つのルールであるオイラー角について説明します.
オイラー角とは,回転軸をかえながら回転を順次組み合わせて任意の姿勢に移動することのできる変換のことです.図のように3つの回転角度を指定することで,あらゆる姿勢を定義することができます.オイラー角は一見複雑そうに見えますが,同次変換行列の逐次変換を用いれば非常に簡単に表わせます.
図:オイラー角
オイラー角変換は具体的には次のようになります.
- まずz軸まわりにα回転します.同次変換行列はRot(z,α)です.
- つぎに,新しい座標系においてy軸回りにβ回転します.同次変換行列はRot(y,β)です.
- 最後に,新しい座標系においてz軸回りにγ回転します.同次変換行列はRot(z,γ)です.
一連の変換を一つにまとめると,このオイラー角変換は,
となります.
オイラー角変換には大きくわけて2種類あります.例えば上記のz-y-z軸まわりの変換がその一つで,2種類の回転軸を組み合わせています.
一方,前述したポヒマスセンサではオイラー角としてz-y-x軸まわりを利用しており,3種類の回転軸を組み合わせています.どの軸を使うかは用途によっていろいろです.
参考まで回転軸が異なることによって,3つのオイラー角の呼び名も違ってきます.
z-y-z軸系での呼び名は,ヘディング(heading),ピッチ(pitch),バンク(bank)が用いられています.z-y-x軸系での呼び名は,アジマス(azimuth),エレベーション(elevation),ロール(roll)が使われる場合もありますし,ヨー(yaw),ピッチ,ロールが使われる場合もあります.
オイラー角を利用する場合は,その回転軸と順序に注意して同次変換行列に導入してください.
物体の配置と視点の配置とは切っても切れない関係にあります.視点を物体の左に移動することとは,物体を視点の右に移動することと結果として等しいからです.
このことより,視点移動と物体移動の関係について少し詳しく考えてみます.
まず座標系を3つ考えてみて下さい.視点座標系Eとワールド座標系Wそしてモデル座標系Mです.視点座標系は視点の位置と姿勢を表します.またモデル座標系は物体の位置姿勢を表現します.
下の図のような状況を考えてみて下さい.
この図では,視点座標系がワールド座標系からz軸正方向に100,モデル座標系がワールド座標系からz軸負方向に200離れて位置しています.
ワールド座標系から視点座標系に座標変換すると,以下の式のようになります.
(式1)
同様にワールド座標系からモデル座標系に変換すると,次のようになります.
(式2)
実際に表示される見え方は,視点からモデルまで300離れた状態になります.これを式で考えてみましょう.(式1)を変形すると,
(式3)
となります.行列を右辺から左辺に移項する場合,逆行列になります.(式3)を(式2)に代入すると,
(式4)
さて,この平行移動に関する逆行列は,
(式5)
となりますので,(式4)より
(式6)
と変形できます.視点からモデルまでz軸負方向に300だけへ平行移動している変換となり,最初の見解と一致します.
ここで重要なのは,ワールド座標系から視点座標系への座標変換は,逆行列で効いてくるということです.冒頭でも述べたように,視点を物体の左に移動することと,物体を視点の右に移動することとは結果として等しい,というのはこれを意味しています.
これは平行移動だけでなく回転移動を含んだ複雑な変換にも適用可能です.
少し複雑な例を考えてみましょう.
この例では視点がワールド座標系からz軸正方向に100平行移動して,x軸周りに-α度回転します.ワールド座標系から視点座標系への変換を式で表すと,
(式7)
ワールド座標系からモデル座標系への変換を式で表すと,
(式8)
さて,視点からモデルまでの座標変換を考えてみます.(式7)を変形すると,
(式9)
となります.逆行列を左からかける順番に注意して下さい.
(式9)を(式8)に代入すると,
(式10)
のようになります.その結果,視点からワールド座標系への変換はx軸周りに+α度回転し,z軸正方向に100平行移動して,さらにz軸正方向に200平行移動したものになっています.
くりかえしますが,ワールド座標系から視点座標系への座標変換は,逆行列で効いてきます.逆行列ですから,平行移動や回転の符号が逆転するのは当然ですが,行列をかける順番まで逆転することに注意して下さい.視点の移動を自由に制御できるようになれば,座標変換マスターといえるでしょう.*17
- キーボードより入力を受け付ける方法について演習します.
- 回転,移動のために,まずTeapotを1つ表示するプログラムを作成します.前回までのプログラムを参考に,画面のように表示されるものを作ってみて下さい.厳密に同じ物でなくて構いません.画面にティーポットが1つ入る大きさになるよう,視体積の大きさや視点の位置は適宜調整します.
ティーポットの表示には,glutSolidTeapot(float size)を使います.
確認用です(ensyu4.c).
-
キー入力を受け付けるためには,キーボードが押されたときに呼ばれる関数を作成し,その関数をmain関数で指定します.例えば,プログラム実行中に'Q'または'q'キーを押したら終了するには,
void keyboard(unsigned char key, int x, int y)
{
switch(key){
case 'q':
case 'Q':
exit(1);
}
}
という関数を作成し,main関数で,
glutKeyboardFunc(keyboard);
と関数名をmain関数の中(glutMainLoop()の手前付近)で指定します.
- 作成する関数(ここではkeyboard.名前は何でも構わない)は,引数としてunsigned char key, int x, int y)を持っています.keyにはキーボードのアスキーコード,x,yにはキーボードが押されたときのマウスカーソルの位置が入っています.下記の一文をこの関数(ここではkeyboard)に入れて,確認してみましょう.
printf("%c %d %d\n", key, x,y);
カーソルキーやファンクションキーも試してみましょう.
-
カーソルキーやファンクションキーなど特殊な機能を持つキーは,上の方法で使うことができません.特殊なキーを使うには例えば
void special_key(int key, int x, int y)
{
switch(key){
case GLUT_KEY_F1:
printf("F1が押された\n");
break;
}
}
という名前の関数を追加し,main関数で,
glutSpecialFunc(special_key);
と関数名を指定します.作成する関数(ここではspecial_key)の引数は,先ほどと異なり(int key, int x, int y)となることに注意して下さい.特殊なキーのコードについてはGLUTの資料で調べて下さい.
-
カーソルキーを押すと,ティーポットがx方向,y方向,z方向に移動するプログラムを作成して下さい.
ヒント
- x,y,z方向動作用にグローバル変数を3つ用意します.
- キーボードを押すとグローバル変数が増減するようにします
- カーソルキー上:GLUT_KEY_UP
- カーソルキー下:GLUT_KEY_DOWN
- カーソルキー左:GLUT_KEY_LEFT
- カーソルキー右:GLUT_KEY_RIGHT
- PageUp キー:GLUT_KEY_PAGE_UP
- PageDown キー:GLUT_KEY_PAGE_DOWN
- glTranslatefにより,グローバル変数だけ平行移動するようにします.
確認用です(ensyu4-1.c).
- オイラー角とファイル入力について演習します.
- キーボードによりティーポットを回転させてみましょう.オイラー角のパラメータは3つありました.先ほどの例と同様に,3つのパラメータのグローバル変数(例えばalpha,beta,gamma)を用意し,キーボードで値を変更できるようにしましょう.
参考までに,z-y-z軸系オイラー角の変換順序はRot(z,α)Rot(y,β)Rot(z,γ)でした.これをOpenGLで記述すると,
glRotatef(alpha, 0.0, 0.0, 1.0);
glRotatef(beta, 0.0, 1.0, 0.0);
glRotatef(gamma, 0.0, 0.0, 1.0);
のようになります.
確認用です(ensyu4-2.c).
- オイラー角の指定方法により,位置姿勢を6つのパラメータで指定できるようになりました.ここで入力をキーボードではなくファイルにした例を紹介します.
ensyu4-3.cは,軌跡ファイルを読み取り,軌跡にしたがってティーポットを表示するプログラムです.ファイルをダウンロードし,プロジェクトに加えてコンパイルしてみて下さい.なお,動作には軌跡データファイルが別途必要です.path.csvをダウンロードして,プログラムと同じ場所に保存して下さい.
- path.csvの中身を確認してみましょう.6つのパラメータが約700行ほどならんでいるのがわかります.
- 外部ファイルを読みこむためには,fopenでファイルをオープンし,fscanfでデータを読み取った後,fclose()します.過去の授業でやっているはずですが,研究でもよく使うので復習です.
ファイルオープンのおまじないは
if((fp=fopen("path.csv","r"))==NULL){
fprintf(stderr,"file not found\n");
exit(1);
}
です.fpはファイルポインタ(ファイルディスクリプタ)と呼びます.ファイルが見つからなければエラーを出して強制終了です.ファイルクローズは
if(feof(fp)){
fclose(fp);
exit(1);
}
で,ファイルポインタが終端にきたら(読み取るデータがなくなれば)ファイルをクローズし強制終了です.ファイルの読み取りは
fscanf(fp, "%f,%f,%f,%f,%f,%f",
&pos_x, &pos_y, &pos_z, &alpha, &beta, &gamma);
とパラメータを6つずつ読み取って,
glTranslatef(pos_x, pos_y, pos_z);
glRotatef(gamma, 0.0, 0.0, 1.0);
glRotatef(beta, 0.0, 1.0, 0.0);
glRotatef(alpha, 1.0, 0.0, 0.0); /* heading */
と,z-y-x軸系でのオイラー角変換によって物体を回転しています.パラメータの読み取りはdisplay関数の中にあるので,描画毎にパラメータを6つ読み取ることになります.
プログラムを何度か実行して,ティーポットが軌跡を描くことを確認してみて下さい.
- マウスの入力について演習します.
- マウスを使用するためには,キーボードのときと同様,マウスのボタンが押された(放した)ときの関数を用意し,main関数で指定します.ボタンの押された(放した)ときの状態とカーソルの位置について表示するには,
void mouse(int button, int state, int x, int y)
{
if(button==GLUT_RIGHT_BUTTON)
printf("right button %d pos(%d,%d)%\n", state, x, y);
if(button==GLUT_LEFT_BUTTON)
printf("left button %d pos(%d,%d)\n", state, x, y);
}
という関数を追加して,
glutMouseFunc(mouse);
をmain関数に追加します.演習で一番最初に作ったプログラム(ティーポットを表示するだけのもの)で試してみましょう.原型が残っていない人はensyu4.cを使って下さい.
- マウスのボタンが押された(放した)位置によって,ティーポットの色を変えてみましょう.押された時のxの値が赤,yの値が緑といった具合です.
確認用(ensyu4-4.c).
-
おまけ
ティーポットをマウスで回転させてみましょう.
マウス入力を回転行列に変換するために,以下のプログラムが必要です.ダウンロードして,ヘッダファイル以外をプロジェクトに追加して下さい.
自分が編集しているプログラムのヘッダに
#include "matrix.h"
#include "cg_track.h"
を加えて下さい.これは外部プログラムの関数を使うための宣言です.
さらに,ティーポット描画の直前に
ViewFromMouse_track();
を加えます.ここでマウス入力=>物体の座標変換をおこないます.最後にmain関数の中で
ControlMouse_track();
を加えます.これはglutMouseFunc()の代わりですので,glutMainLoop()の直前におきます.glutMouseFunc()が残っている場合は競合してしまうので削除して下さい.
確認用(ensyu4-5.c).
-
ティーポットを軌跡上で動かすというプログラムが演習問題にありました.(ensyu4-3.c)
このプログラムではティーポットが軌跡上を移動していましたが,こんどは軌跡に沿って視点を動かしてみてください.つまり,格子模様の立方体内で自分が移動するシーンを作成して下さい.この時,座標変換はどうなるでしょうか.
- ここで勉強したOpenGLの関数は,
glutKeyboardFunc(void (*func)(unsigned char key,int width,int height))
glutSpecialKeyFunc(void (*func)(int key,int width,int height))
glutMouseFunc(void (*func)(int button,int state,int x,int y))
です.赤本やGLUTのマニュアルを利用して,復習しておきましょう.
戻る