Tekutama AR
~拡張現実感によるオーバーレイ表示と動作~
情報物理研究室
目次
項
1.
はじめに
3
2.
開発環境
4
2-1. ARToolkit
4
2-2. OpenGL
・
GLUT
5
2-3. Metasequoia
6
2-4. GLMetaseq
6
3. 3DCG
モデルの作成
7
4. AR
プログラムの構成
9
4-1. main
関数
10
4-2.
メインループ関数
11
5.
アニメーション
14
5-1.
ジャンプ・宙返り
14
5-2.
複数マーカ
15
6.
おわりに
16
参考文献
17
付録
18
1.
はじめに
Augmented Reality(以下AR)とは,拡張現実感と呼ばれ,現実世界にデジタル情報を重 ね合わせ,現実世界を拡張させる技術である.近年ARを用いた代表的なサービスとして 「セカイカメラ」やカーナビゲーションの「サイバーナビ」がある. セカイカメラとは,iPhoneやAndroidなどの携帯端末のカメラを通して現実世界を見ると, 他の人が書き込んだその場・その時のタグ情報を,カメラが映し出している現実映像に重ね 合わせて表示するサービスである.例えば,ある店をセカイカメラで通して見ると,その店 の位置を解析し,その店のメニューやクーポン情報,口コミ情報のタグを表示することが出 来る. サイバーナビは,フロントガラスに配置した車載カメラの現実映像に,ナビゲーション情 報をリアルタイムに重ねて表示することが出来る. また,ARを広報活動に活用する例もある.トヨタ自動車は,マーカを認識するとトヨタ 社製の車が描画され,車が動くアニメーションもされる. 本研究では,AR技術を用い,近畿大学工学部のイメージキャラクターである「てくた ま」をARで表現し,「てくたま」の認知度を高め,近畿大学工学部の広報活動を推進する アプリケーション開発を行った. 図 1 Tekutama AR 実行画面2.
開発環境
2-1
ARToolkit
ARToolkitとは,マーカベースのARアプリケーション開発を支援するC/C++用のプログラ ミングライブラリである.ARToolkitを使用すると,紙に印刷されたパターンをカメラで読 み取り,その上に3Dオブジェクトをオーバーレイ表示するアプリケーションが容易に作る ことが出来る。ARToolkitが提供する機能として,カメラ画像の取得,マーカの検出とパ ターンの認識,マーカの 3 次元位置・姿勢の計測,実写画像と3DCGの合成表示,などがあ る. ARToolkitのアーキテクチャは図のようになっている。ARToolkitには主にビデオキャプ チャモジュール,AR計算モジュール,グラフィック処理モジュールがある.ビデオキャプ チャモジュールは,内部のOSに応じたインターフェースを使用している.AR計算モ ジュールはマーカ検出のための画像処理やパターン認識を行い,標準のAPIを使用している. VRML 描画モジュールは VRML 形式の3DCGを表示する処理にOpenVRMLを用いているが,本 研究では使用せず,代わりに「GLMetaseq」ライブラリを使用する.GLMetaseqは3Dモデ リングソフト「Metasequoia」で作成したモデルのデータであるmqoファイルを読み込み, OpenGLで表示するためのライブラリである. 図 2 ARToolkitのアーキテクチャ2-2
OpenGL
・
GLUT
ARToolkitのグラフィック処理モジュールにはOpenGLとGLUTを用いる.OpenGLは, 点・線・多角形の描画,色やパターンの制御,幾何変換,シェーディング処理などを行う. GLUTは,主にウィンドウ管理やイベント処理の設定を行う.ウィンドウ管理やイベント処 理の設定はOSとウィンドウシステムに依存しているため,そろぞれのOSに則って設定を 行う必要がある.なお,OpenGLとGLUTはマルチプラットホームに対応しており,プラッ トホームに依存しないシステム構築が可能である. ARToolkitではカメラの画像の描画や射影幾何の設定を行う処理系に「gsub」と
「gsub_lite」がある.gsubはGLUTによるウィンドウ管理やイベント処理が含まれているが,
gsub_liteには含まれておらず,自力で実装することになる.当初gsubを用いて開発を行って いたが,てくたまを表示すると,図 3 のようにてくたまのテクスチャを貼った面に対して, 陰影処理が施されない問題が起きた.原因は,gsubのGLUTの処理をまとめて行っている関 数により「GLMetaseq」ライブラリのテクスチャの処理がされないことが分かり,gsub_lite での開発に至った. 図 3 gsubでの実行画面 図 4 グラフィック処理モジュールの階層構造の違い
2-3
Metasequoia
Metasequoia(メタセコイア)とは、3DCGモデルを作成するための3Dモデリングソフト ウェアである.てくたまの3DCGモデル作成に用いた.シェアウェア版とフリーウェア版の 2 種類が提供されており、本研究ではフリーウェア版のmetasequoia LE R2.4aを使用した.2-4
GLMetaseq
GLMetaseqは、3Dモデリングソフトウェア「Metasequoia」で作成した3Dモデルのデータ である「mqo」ファイルを読み込んでOpenGLで表示するためのC/C++用のプログラミング ライブラリである.これを使用すると、3Dモデルを現実世界に表示するプログラムが非常 に容易になる.3.
3DCG
モデルの作成
てくたまをARで表現するために、3DCGモデルを作成する.作成には「Metasequoia」を 用い、以下の手順で行った. 1. 3DCGモデルの基となる画像を入手(図 5) 図 5 基となる画像 2. Metasequoiaに画像を取り込み、画像に合わせ各パーツを作成する. 3. 各パーツを組み合わせ、一つのモデルにする.(図 6) 図 6 無色透明のモデル4. モデルは無色透明であるため、各パーツに色を塗り、模様などの複雑な部分はテク スチャマッピングを行う.
5. 最後に光源設定を行う.(図 7)
4.
AR
プログラムの構成
本研究では、紙に印刷されたマーカーをカメラで読み取り,その上に3DCGモデルのてく たまをオーバーレイ表示し、さらにアニメーションするアプリケーション開発を行った. マーカーは 2 枚用意し、1 枚はオリジナルマーカーである近畿大学校章マーカーを用意した. (図 8) 図 8 近畿大学校章マーカーと HIRO マーカー プログラムは、gsubとgsub_liteのどちらかを用いてプログラムを構築していくが、3DCG モデルにテクスチャマッピングを行っている場合はgsub_liteを推奨する.理由は「2-2OpenGL・GLUT」で述べた通りであるが、gsub_liteを用いることでOpenGLやGLUTについ てより理解することができる.
図 9 プログラムの全体像
ARプログラムの全体像を図 9 に示す.main関数では主に初期化が行われ、その後メイン ループ関数に入り、キーイベント処理による終了処理が行われるまでメインループ関数の処 理を繰り返す.次にmain関数とメインループ関数の説明を行う.
4-1
main
関数
①GLUTの初期化 このプログラムはGLUTを使用しているので最初にGLUTの初期化を行う必要がある. glutInit()関数を実行することで以降に行われるGLUTの処理を行えるようにする. ②ビデオ・カメラの設定 ビデオやカメラを使えるように設定を行う. arVideoOpen()関数によってビデオデバイスを 使える状態に設定する.設定ファイルにはxmlファイルを読み込んでいる.arParamLoad()関 数はカメラパラメータのファイルを読み込む.その後,使用するカメラの解像度に合わせて カメラパラメータを変更する必要があり、この処理にはarParamChangeSize()関数と arInitCparam()関数を使用する. ③パターンのロード マーカのパターンをロードする.ロードにはarLoadPatt()関数を使用し、読み込んだパター ンには個別のID番号が設定され、マーカの一致度を調べるときに使用する. ④ウィンドウの設定ウィンドウの設定はGLUTを用いる.glutInitDisplayMode()はGLUTのウィンドウの表示 モードや各種バッファ処理の有無を指定する.
GLUT_DOUBLE : ダブルバッファ GLUT_RGBA : RGBAカラーモード GLUT_DEPTH : デプスバッファを使用
⑤3DCGモデルのロード てくたまの3DCGモデルのmqoファイルをロードする.ロードには「GLMetaseq」ライブラ リの関数であるmqoCreateModel()関数を使う. ⑥キャプチャの開始 初期化、設定を終え、arVideoCapStart()関数を使用しビデオキャプチャを開始する. ⑦メイループに入る glutMainLoop()関数を実行しメインループに入る.この関数を呼び出すとプログラムはイ ベント待ち状態になる.またイベントに対して行う処理関数のことをコールバック関数と呼 び、glutMainLoop()関数を実行する前にはコールバック関数の登録を行う必要がある.以下、 主なコールバック関数を示す. ・glutDisplayFunc() ・・・・・ 描画処理が必要な時に行う関数 ・glutKeyboadFunc() ・・・・・ キーボードの入力があった時に行う関数 ・glutIdleFunc() ・・・・・ 他に処理すべきイベントが何も無い時に行う関数 本研究ではglutIdleFunc()関数で繰り返される処理をメインループとする.
4-2 メインループ関数
①カメラ画像の取得・描画 arVideoGetImage()関数を使用しビデオデバイスから画像を取得する.その後、 argDispImage()関数で取得した画像をウィンドウに描画する.②マーカの検出・認識 arDetectMarker()関数を使用すると取得したカメラ画像からマーカの検出・認識を行う.ま ず取得したカメラ画像を2値化処理によって白黒画像にする.次にラベリング処理によって、 白黒画像の塊になった領域ごとにラベル(番号)を割り当てる.ラベル付けされた領域の端 の一点を起点に輪郭の抽出を行い、最終的に4個の頂点を持つ領域を四角形とする.その後 テンプレートマッチングを行い、四角形の中のパターンを認識している.この処理で行われ た情報はARMarkerInfo型の構造体に格納される. ■ ARMarkerInfo 構造体 typedef struct { int area ; // マーカーのピクセル数 int id ; // マーカーID int dir ; // マーカーの回転方向( 3 : ↑ 2 : → 1 : ↓ 0 : ← ) double cf ; // 信頼度( 0.0 ~ 1.0 ) double pos[ 2 ]; // マーカーの中心座標( x, y ) double line[ 4 ][ 3 ]; // 直線の式 ( ax + by + c = 0 ) × 4辺 (枠線) double vertex[ 4 ][ 2 ]; // マーカーの頂点座標 ( x, y ) × 4点 } ARMarkerInfo; ③マーカの一致度の比較 arDetectMarker()関数は全ての「マーカらしき部分」を検出されるため、検出されたマー カから最も高い一致度のものを探す必要がある.そこでarDetectMarker()関数の処理で ARMarkerInfo構造体に格納された情報の中のcf(信頼度)を比較して、最も高い数値のマーカ をパターンとして認識している. ④マーカの位置・姿勢の計測 arGetTransMat()関数を用い、検出されたマーカの情報からマーカ・カメラ間の変換行列を 求める. ⑤座標変換行列の適用 求めた変換行列を適用することで、以降に行われる描画処理がマーカの中心とした座標系 で行われる.
⑥3Dオブジェクト描画 描画処理では、隠面処理、光源設定を行い、てくたまの3DCGモデルの描画を行う. 「GLMetaseq」ライブラリのmqoCallModel()関数を使用するとモデルがマーカの中心に描画 される.最後に次フレームのカメラ画像の取得を指示し、メインループの処理を繰り返す. なお、Metasequoiaの座標系はY軸が上を向いているのに対して.ARToolkitはZ軸が上を 向いている.このまま表示すると、モデルが倒れている状態で描画される.これを修正する ために、3DCGモデルをX軸まわりに 90 度回転させてから描画を行う. 図 8 てくたまの 3DCG モデルの描画
5.
アニメーション
5-1 ジャンプ・宙返り
ジャンプのアニメーションは平行移動を連続的に行う方法で実装した.平行移動には OpenGLのコマンドglTranslatef()を利用する.引数に移動させる位置を入力すると、原点 (マーカの中心)から平行移動して描画を行う. ジャンプは物理の鉛直投射を逐次計算するアルゴリズム(改良オイラー法)を用いて構築 した. Δt は時間の差分を表し,Δt 秒後の Z 座標を求める式である.実行時,カメラのフレー ムレートを 30 fps に選定すると,3D オブジェクトの描画も 30 fps となり,シミュレー ションを現実に合わせるにはΔt の値は 0.033 s(= 1/30 s)となる.a の加速度は重力加速 度 g となる.但しARToolkit では,OpenGL 上の長さを mm 単位で扱う.したがって g = -9800.0 mm/s2 である.Vz(0)の値を 1500mm/s とし,ジャンプから着地までのタイムを求め ると 0.297 s となる.この設定で描画すると運動のスピードが速く,3D オブジェクトの姿 勢と位置の変化が分かりづらい.そこでキーイベント処理で「3」,「5」をそれぞれ入力 すると,Δt の値を1/3 倍,1/5 倍にすることで,スロー・シミュレーションできるように した. 宙返りはジャンプの始動から着地までに360 度回転するように,Δt 間の回転角度を求め る.物体を回転させるにはOpenGL のコマンドglRotatef()を利用する.これは指定した軸ま わりを指定した数値分,回転を行う.ジャンプと宙返りを併せれば,前方宙返り,後方宙返 りなどの動きが可能である.5-2 複数マーカ
2 個のマーカ(以下マーカ 1,マーカ 2)を利用し,マーカ間をモデルが移動する機能を 実装した.マーカ間の位置関係を求めるには, ARToolkit のコマンドarUtilMatInv() と arUtilMatMul() を使用する.arUtilMatInv()はマーカ 1 の座標系からカメラの位置を取得す ることが出来る.arUtilMatMul()はマーカ 1 の座標系からマーカ 2 の位置・距離を求めるこ とが出来る.求めたマーカ間の距離を 1/100 ずつマーカ 1 からマーカ 2 に向け平行移動をす るようにした.マーカ 2 への到達判定には,マーカ間の距離と現在位置を比較し,現在位置 がマーカ間の距離より大きい場合,マーカ 2 の位置に到達したとし,マーカ 1 へ移動する ようにした.6.
おわりに
2011 年 11 月に東広島市で行われた生涯学習フェスティバルにこのアプリケーションを出 展した際、子供らに大変興味を持ってもらうことが出来た.本研究の目的である広報活動に 活用できるアプリケーションが開発できたと考える.また歩行時のてくたまの手足の動きな ど、細かいモーションを加えれば表現力が増すと考える. 今後の課題は、スマートフォンへの対応や、Twitterのつぶやき機能への展開が考えられる.参考文献
[1] 橋本 直:「ARToolkit 拡張現実感プログラミング入門」アスキー・メディアワーク ス(2008) [2] 谷尻 豊寿:「ARToolkit プログラミングテクニック」カットシステム(2008) [3] 床井 浩平:「GLUTによるOpenGL入門」工学社(2005) [4] 工学ナビ:http://kougaku-navi.net/ARToolKit.html付録
// ======================================================================= // インクルード //======================================================================= #ifdef _WIN32 #include <windows.h> #endif #include <stdio.h> #include <stdlib.h> #define _USE_MATH_DEFINES #include <math.h> #ifndef __APPLE__ #include <GL/gl.h> #include <GL/glut.h> #else #include <OpenGL/gl.h> #include <GLUT/glut.h> #endif #include <AR/config.h> #include <AR/video.h> #include <AR/param.h> #include <AR/ar.h> #include <AR/gsub_lite.h> #include "GLMetaseq.h" //======================================================================= // ライブラリ //======================================================================= #ifdef _DEBUG #pragma comment(lib,"libARd.lib") #pragma comment(lib,"libARgsubd.lib")
#pragma comment(lib,"libARvideod.lib") #pragma comment(lib,"libARgsub_lited.lib") #pragma comment(linker,"/NODEFAULTLIB:libcmtd.lib") #else #pragma comment(lib,"libAR.lib") #pragma comment(lib,"libARgsub.lib") #pragma comment(lib,"libARvideo.lib") #pragma comment(lib,"libARgsub_lite.lib") #pragma comment(linker,"/NODEFAULTLIB:libcmt.lib") #endif //======================================================================= // 型定義 //======================================================================= // キャラクタデータ構造体 typedef struct { MQO_MODELmodel; int pattId; int patt_found; double trans[3][4]; double patt_width; } CHARACTER_DATA; // マーカーのデータ(二つ)
static CHARACTER_DATA gCharacter[2];
//======================================================================= // グローバル変数
//=======================================================================
/* セットアップに必要な変数 */
// TRUE:ウィンドウモード/FALSE:フルスクリーンモード
static const int gWindowed = TRUE;
static const int gFullWidth = 800; // 画面解像度
static const int gFullHeight = 600; // 画面解像度
static const int gFullDepth = 32; // ビット数
static const int gFullRefresh = 0; // リフレッシュレート.0でデフォルト値.
// 現実世界/仮想世界のスケールファクタ
static const double gViewScaleFactor = 1.0;
// 視界の範囲(Min~Maxまでの範囲にあるオブジェクトを描画)
static const double gViewDistanceMin = 0.1; static const double gViewDistanceMax = 1000.0;
static char *gCparamName = "Data/camera_para.dat"; // カメラパラメータファイル
static char *gVconf = "Data/WDM_camera_flipV.xml"; // ビデオデバイス の設定ファイル
static ARUint8 *gARTImage = NULL; // カメラ画像へのポインタ
static int gARTThreshhold = 100; // 2値化の閾値
static ARParam gARTCparam; // カメラパラメータ
static ARGL_CONTEXT_SETTINGS_REF gArglSettings = NULL; // 描画系の設定情報
/* パターンファイルに関する変数 */
#define NUM_MODEL2 // 使用するマーカーの枚数
static char *gPattName[2] = {"Data/patt.kinki1", "Data/patt.hiro"}; // パターンファイル
static char *gModelName = "Data/tekutama2.mqo"; // モデルファイル
static double gPatt_centre[2] = {0.0, 0.0}; // パターンの中心位置(mm単位)
int count = 0; // fps計算のための変数 /* オブジェクト移動量 */ GLfloat exTx = 0.0; // x軸の移動量 GLfloat exTy = 0.0; // y軸の移動量 GLfloat exTz = 40.0; // z軸の移動量 int dirFlag = 0; // 移動方向フラグ GLfloat exRx = 90.0; // x軸まわりの回転角度 GLfloat exRy = 0.0; // y軸まわりの回転角度 GLfloat exRz = 0.0; // z軸まわりの回転角度
/* move_Model関数で使用する変数 */ int move_count = 0; // 0: モデルビュー 1: ジャンプ 2: コマ送り int View = 0; // モデルビュー int Jumping = 1; // ジャンプ int Frame_advance = 2; // コマ送り int c=0; // ジャンプカウンタ double dt = 0.011; // 時間刻み(通常:0.033 3倍スロー:0.011 5倍スロー:0.0066) double g = 9800.0; // 重力加速度 g (単位mm) double vz = 1500.0; // z方向の速度 double vz0 = 1500.0; // z方向の初速度(初期化用) double vz1; double exRz_angle = 0.0; // 宙返りの角度(時間刻み分) double time = 0.0; double vy = 5.0; double vy0 = 5.0; double vx = 5.0; double vx0 = 5.0; void Somersault_calc(void);
void Move_calc(double xmax, double ymax);
//======================================================================= // カメラのセットアップ関数
//======================================================================= static int SetupCamera( const char *cparam_name, char *vconf, ARParam *cparam )
{
ARParam wparam; // カメラパラメータ(作業用変数)
// ビデオデバイスを開く if ( arVideoOpen( vconf ) < 0 ) { printf( "カメラが見つかりません\n" ); return FALSE; } // 画像のサイズを取得
if ( arVideoInqSize( &xsize, &ysize ) < 0 ) {
printf( "画像サイズの取得に失敗しました\n" ); return FALSE;
}
// カメラパラメータのロード,値の修正と初期化 if ( arParamLoad( cparam_name, 1, &wparam ) < 0 ) {
printf( "カメラパラメータファイルのロードに失敗しました\n" );
return FALSE; }
arParamChangeSize( &wparam, xsize, ysize, cparam ); arInitCparam( cparam ); // ビデオキャプチャのスタート if ( arVideoCapStart() != 0 ) { printf( "ビデオキャプチャのスタートに失敗しました\n" ); return FALSE; } return TRUE; } //======================================================================= // マーカのセットアップ関数 //======================================================================= static int SetupMarker( const char *patt_name, int *patt_id )
// マーカのロード
if ( ( *patt_id = arLoadPatt( patt_name ) ) < 0 ) {
printf( "パターンファイルのロードに失敗しました\n" ); return FALSE; } return TRUE; } //======================================================================= // 描画系のセットアップ関数(ウィンドウ) //======================================================================= static int SetupGraphicsWin( const char *title, int dispWidth, int dispHeight )
{
// ディスプレイモードの設定
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH );
// ウィンドウの設定
glutInitWindowSize( dispWidth, dispHeight ); glutCreateWindow( title );
// gsub_lite ライブラリの初期化
if ( ( gArglSettings = arglSetupForCurrentContext() ) == NULL ) { return FALSE; } return TRUE; } //======================================================================= // 描画系のセットアップ関数(フルスクリーン) //======================================================================= static int SetupGraphicsFull( int dispWidth, int dispHeight, int dispDepth, int dispRefresh )
{
// ディスプレイモードの設定
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH );
// フルスクリーンの設定
if ( dispRefresh ) {
sprintf( glutGamemode, "%ix%i:%i@%i", dispWidth, dispHeight, dispDepth, dispRefresh );
} else {
sprintf( glutGamemode, "%ix%i:%i", dispWidth, dispHeight, dispDepth ); }
glutGameModeString( glutGamemode ); glutEnterGameMode();
// gsub_lite ライブラリの初期化
if ( ( gArglSettings = arglSetupForCurrentContext() ) == NULL ) { return FALSE; } return TRUE; } //======================================================================= // 終了関数 //======================================================================= static void Quit( void )
{ arVideoCapStop(); // ビデオキャプチャの停止 arVideoClose(); // ビデオデバイスを閉じる arglCleanup( gArglSettings ); // 描画系の終了処理 mqoDeleteModel( gCharacter[0].model ); // モデルの削除 mqoCleanup(); // GLMetaseqの終了処理 }
//======================================================================= // キーイベント関数
//======================================================================= static void Keyboard( unsigned char key, int x, int y )
{
switch ( key ) {
case 0x1B: // 終了コマンド
case 'Q': case 'q':
printf("*** %f (frame/sec)\n", (double)count/arUtilTimer()); exit(0); break; case 0x20: // コマ送り(ジャンプ/スペースキー) move_count = Frame_advance; vz1 = vz + (-g) * dt; exTz = exTz + (vz + vz1)/2 * dt; vz = vz1; if( (c % 2) == 0 ){
exRz = exRz + exRz_angle; // 前方宙返り
}else if( (c % 3) == 0 ){
exRz = exRz + exRz_angle * 2; // 前方2回転
}else
exRz = exRz - exRz_angle; // 後方宙返り
// 数値の初期化 if( exTz < 40.0 ){ exTz = 40.0; vz = vz0; exRz = 0.0; c++; // ジャンプカウンタ } break;
// ジャンプ case 'j': move_count = Jumping; break; // Viewモード case 'v': move_count = View; exTz = 40.0; exRz = 0.0; break; // ジャンプ 1 倍スロー(標準スピード) case '1': move_count = Jumping; dt = 0.033; // 時間刻み Somersault_calc(); // 宙返り(exRz_angle)計算 break; // ジャンプ 3 倍スロー case '3': move_count = Jumping; dt = 0.011; // 時間刻み Somersault_calc(); // 宙返り(exRz_angle)計算 break; // ジャンプ 5 倍スロー case '5': move_count = Jumping; dt = 0.0066; // 時間刻み Somersault_calc(); // 宙返り(exRz_angle)計算 break;
default: break; } } //======================================================================= // アイドル関数(メインループ) //======================================================================= static void Idle( void )
{
static int ms_prev; // 前回の時刻
int ms; // 現在の時刻 float s_elapsed; // 経過時間 ARUint8 *image; // カメラ画像 ARMarkerInfo *marker_info; // マーカ情報 int marker_num; // 検出されたマーカの数 int i, j, k; // ループカウンタ
double wmat1[3][4], wmat2[3][4]; // 変換行列
//前回の呼び出しからの経過時間を計測し,更新周期が100Hz以下になるようにする
ms = glutGet( GLUT_ELAPSED_TIME ); // 経過時間(ミリ秒)を取得
s_elapsed = (float)( ms - ms_prev ) * 0.001; // 経過時間(秒単位にする)
if ( s_elapsed < 0.01f ) return; // 100Hz以下は更新しない
ms_prev = ms; // 経過時間を一時保存
// カメラ画像の取得
if ( ( image = arVideoGetImage() ) != NULL ) {
// 画像へのポインタを取得
// マーカの検出状態を初期化
for (i=0; i<NUM_MODEL; i++) gCharacter[i].patt_found = FALSE;
// マーカの検出
if ( arDetectMarker( gARTImage, gARTThreshhold, &marker_info, &marker_num ) < 0 ) {
exit(0); }
for (i=0; i<NUM_MODEL; i++) {
// 最も信頼度の高いマーカを探す
k = -1;
for ( j = 0; j < marker_num; j++ ) {
if ( marker_info[j].id == gCharacter[i].pattId ) { if ( k == -1 ) k = j;
else if( marker_info[j].cf > marker_info[k].cf ) k = j; }
}
// 最も一致度の高いマーカ( k )に対して座標変換行列を求める
if ( k != -1 ) {
arGetTransMat( &( marker_info[k] ), gPatt_centre, gCharacter[i].patt_width, gCharacter[i].trans );
gCharacter[i].patt_found = TRUE; }
}
/* 2つのマーカー間の距離を取得( マーカ1[校章] と マーカ2[HIRO] ) */
if( gCharacter[0].patt_found > 0 && gCharacter[1].patt_found > 0 ){
// マーカ1の座標系でカメラの位置を取得
arUtilMatInv( gCharacter[0].trans, wmat1);
// マーカ1から見たマーカ2の位置を取得
Move_calc( wmat2[0][3], wmat2[1][3]);
}
else if( gCharacter[1].patt_found == FALSE || exTx > 1.0 ){ exTx = 0.0;
exTy = 0.0; }
if( count == 0 ) arUtilTimerReset(); // タイマーのリセット
count++; // 処理フレーム数のカウント // 画面の描画指示 glutPostRedisplay(); } } //======================================================================= // 移動量計算関数 //======================================================================= void Move_calc( double xmax, double ymax)
{ GLfloat x = 0.0; // x軸方向移動量 Glfloat y = 0.0; // y軸方向移動量 int posFlag; // マーカーの位置関係 /* オブジェクトの移動量 */ x = xmax * 0.02 ; // マーカー間の距離を分割 y = ymax * 0.02 ;
/* オブジェクトの移動方向 */ if ( dirFlag == 1 ){ x = -x ; y = -y ; } /* 描画位置を更新 */ exTx = exTx + x ; exTy = exTy + y ; /* マーカーの位置関係 */ if ( xmax > 0 ) posFlag = 0 ; // マーカー1が左,マーカー2が右
else if ( xmax < 0 ) posFlag = 1 ; // マーカー1が右,マーカー2が左
/* マーカーに到達したときに方向を反転 */ if ( posFlag == 0 ){ if ( exTx >= xmax ){ dirFlag = 1; exTx = xmax; exTy = ymax; } else if ( exTx < 0 ){ dirFlag = 0; exTx = 0.0; exTy = 0.0; } } else { if ( exTx <= xmax ){ dirFlag = 1; exTx = xmax; exTy = ymax;
} else if ( exTx > 0 ){ dirFlag = 0; exTx = 0.0; exTy = 0.0; } } } //======================================================================= // ウィンドウの可視性チェック関数 //======================================================================= static void Visibility( int visible )
{ // ウィンドウが見えていないときはアイドル関数を無効にする if ( visible == GLUT_VISIBLE ) { glutIdleFunc( Idle ); } else { glutIdleFunc( NULL ); } } //======================================================================= // ウィンドウサイズが変更されたときに呼ばれる関数 //======================================================================= static void Reshape( int w, int h )
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glViewport( 0, 0, (GLsizei) w, (GLsizei) h );
printf("%d,%d\n",w,h);
glMatrixMode( GL_PROJECTION ); glLoadIdentity();
glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); } //======================================================================= // 光源を設定する関数 //======================================================================= static void SetLight( GLenum light )
{ GLfloat light_diffuse[] = { 0.9, 0.9, 0.9, 1.0 }; // 拡散反射光 GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 }; // 鏡面反射光 GLfloat light_ambient[] = { 0.3, 0.3, 0.3, 0.1 }; // 環境光 GLfloat light_position[] = { 0.0, 0.0, 0.0, 1.0 }; // 位置と種類 // 光源の設定
glLightfv( light, GL_DIFFUSE, light_diffuse ); // 拡散反射光の設定
glLightfv( light, GL_SPECULAR, light_specular ); // 鏡面反射光の設定
glLightfv( light, GL_AMBIENT, light_ambient ); // 環境光の設定
glLightfv( light, GL_POSITION, light_position ); // 位置と種類の設定
glShadeModel(GL_SMOOTH); glEnable( light ); // 光源の有効化 } //======================================================================= // 材質特性の設定を行う関数 //======================================================================= void SetMaterial(void) { GLfloat mat_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; // 拡散反射光 GLfloat mat_specular[] = { 0.0, 0.0, 1.0, 1.0 }; // 鏡面反射光
GLfloat mat_ambient[] = { 0.0, 0.0, 1.0, 1.0 }; // 環境光
GLfloat shininess[] = { 50.0 }; // 鏡面光の指数
// 材質の設定
glMaterialfv( GL_FRONT, GL_DIFFUSE, mat_diffuse ); // 拡散反射光の設定
glMaterialfv( GL_FRONT, GL_SPECULAR, mat_specular ); // 鏡面反射光の設定
glMaterialfv( GL_FRONT, GL_AMBIENT, mat_ambient ); // 環境光の設定
glMaterialfv( GL_FRONT, GL_SHININESS, shininess ); // 位置と種類の設定
} //======================================================================= // 宙返り(exRz_angle)を求める関数 //======================================================================= void Somersault_calc(void){ double t = 0.0; double ex_Tz = 40.0; double v_z= vz0; double v_z1; exRz_angle = 0.0; /* ジャンプして着地するまでのタイムを計る */ while( ex_Tz >= 40.0 ){ t = t + dt; v_z1 = v_z + (-g) * dt; ex_Tz = ex_Tz + (v_z + v_z1) / 2 * dt; v_z = v_z1; }
// 時間刻み[dt]分の回転角度を求める exRz_angle = 360.0 / ( t / dt ); // 数値の初期化 vz = vz0; exRz = 0.0; exTz = 40.0; } //======================================================================= // 3Dモデルを動かす関数 //======================================================================= void move_Model(void){
if( move_count == Jumping ){
//速度の算出 vz1 = vz + (-g) * dt; //位置の算出 exTz = exTz + (vz + vz1)/2 * dt; vz = vz1; if( (c % 2) == 0 ){ // 2の倍数時
exRz = exRz + exRz_angle; // 前方宙返り
}else if( (c % 3) == 0 ){ // 3の倍数時(2,3の倍数のとき は2の倍数優先)
exRz = exRz + exRz_angle * 2; // 前方2回転
}else
exRz = exRz - exRz_angle; // 後方宙返り
if( exTz < 40.0 ){ // 数値の初期化
vz = vz0; exRz = 0.0; c++; } } } //======================================================================= // ディスプレイ関数 //======================================================================= static void Display( void )
{
GLdouble p[16]; // 射影変換行列
GLdouble m[16]; // 座標変換行列
glDrawBuffer( GL_BACK );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// カメラ画像の描画
arglDispImage( gARTImage, &gARTCparam, 1.0, gArglSettings );
// 次フレームのキャプチャ指示 arVideoCapNext(); gARTImage = NULL; // マーカが見つかっていれば if ( gCharacter[0].patt_found ) { // 3Dモデルの動作計算関数 //move_Model( 1, 360.0, 450.0 ); // 射影変換行列の適用
arglCameraFrustumRH( &gARTCparam, gViewDistanceMin, gViewDistanceMax, p );
glMatrixMode( GL_PROJECTION ); glLoadMatrixd( p ); // モデルビュー行列の初期化 glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glClear( GL_DEPTH_BUFFER_BIT ); // Zバッファの初期化 glEnable( GL_DEPTH_TEST ); // 隠面処理の適用 // 光源の設定(視点に対して固定) glPushMatrix(); SetLight( GL_LIGHT0 ); glPopMatrix(); // 座標変換行列の適用
arglCameraViewRH( gCharacter[0].trans, m, gViewScaleFactor ); glLoadMatrixd( m );
// 3Dオブジェクトの描画
glEnable( GL_LIGHTING ); // 光源ON
glPushMatrix();
move_Model(); // ジャンプ
glTranslatef( exTx, exTy, exTz );
glRotatef( exRx, 1.0, 0.0, 0.0 ); // モデルを立たせる(exRx=90)
glRotatef( exRy, 0.0, 1.0, 0.0 ); glRotatef( exRz, 0.0, 0.0, 1.0 );
mqoCallModel( gCharacter[0].model ); // モデルの描画
glPopMatrix();
glDisable( GL_LIGHTING ); // 光源OFF
glDisable( GL_DEPTH_TEST ); // 隠面処理の解除
glutSwapBuffers(); // バッファの反転(ダブルバッファ交換する)
}
//======================================================================= // main関数
//======================================================================= int main( int argc, char** argv )
{
int i;
// GLUTの初期化
glutInit( &argc, argv );
// カメラのセットアップ
if ( !SetupCamera( gCparamName, gVconf, &gARTCparam ) ) return 0;
// マーカのセットアップ
for (i=0; i<NUM_MODEL; i++) {
if ( !SetupMarker( gPattName[i], &(gCharacter[i].pattId) ) ) return 0; }
// 描画系のセットアップ
if ( gWindowed ) {
if ( !SetupGraphicsWin( "MQO Loader on ARToolKit - gsub_lite edition", arImXsize, arImYsize ) ) return 0;
} else {
if ( !SetupGraphicsFull( gFullWidth, gFullHeight, gFullDepth, gFullRefresh ) ) return 0;
}
// GLMetaseqの初期化
mqoInit();
if ( ( gCharacter[0].model = mqoCreateModel( gModelName, 0.5 ) ) == NULL ) { printf( "モデルの読み込みに失敗しました" );
return 0; }
// キャラクタのデータの初期化
for (i=0; i<NUM_MODEL; i++) {
gCharacter[i].patt_found = FALSE; } gCharacter[0].patt_width = 60.0; // パターンのサイズ(mm単位) gCharacter[1].patt_width = 80.0; // パターンのサイズ(mm単位) // 宙返り(exRz_angle)計算 Somersault_calc(); // 終了時に呼ばれる関数の設定 atexit( Quit ); // コールバック関数の設定 glutDisplayFunc( Display ); glutReshapeFunc( Reshape ); glutVisibilityFunc( Visibility ); glutKeyboardFunc( Keyboard ); glutMainLoop(); return 0; }