• 検索結果がありません。

Tekutama AR ~ 拡張現実感によるオーバーレイ表示と動作 ~ 情報物理研究室 渡部 修平 1

N/A
N/A
Protected

Academic year: 2021

シェア "Tekutama AR ~ 拡張現実感によるオーバーレイ表示と動作 ~ 情報物理研究室 渡部 修平 1"

Copied!
39
0
0

読み込み中.... (全文を見る)

全文

(1)

Tekutama AR

~拡張現実感によるオーバーレイ表示と動作~

情報物理研究室

(2)

目次

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

(3)

1.

はじめに

 Augmented Reality(以下AR)とは,拡張現実感と呼ばれ,現実世界にデジタル情報を重 ね合わせ,現実世界を拡張させる技術である.近年ARを用いた代表的なサービスとして 「セカイカメラ」やカーナビゲーションの「サイバーナビ」がある.  セカイカメラとは,iPhoneやAndroidなどの携帯端末のカメラを通して現実世界を見ると, 他の人が書き込んだその場・その時のタグ情報を,カメラが映し出している現実映像に重ね 合わせて表示するサービスである.例えば,ある店をセカイカメラで通して見ると,その店 の位置を解析し,その店のメニューやクーポン情報,口コミ情報のタグを表示することが出 来る.  サイバーナビは,フロントガラスに配置した車載カメラの現実映像に,ナビゲーション情 報をリアルタイムに重ねて表示することが出来る.  また,ARを広報活動に活用する例もある.トヨタ自動車は,マーカを認識するとトヨタ 社製の車が描画され,車が動くアニメーションもされる.  本研究では,AR技術を用い,近畿大学工学部のイメージキャラクターである「てくた ま」をARで表現し,「てくたま」の認知度を高め,近畿大学工学部の広報活動を推進する アプリケーション開発を行った. 図 1 Tekutama AR 実行画面

(4)

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のアーキテクチャ

(5)

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 グラフィック処理モジュールの階層構造の違い

(6)

2-3

Metasequoia

 Metasequoia(メタセコイア)とは、3DCGモデルを作成するための3Dモデリングソフト ウェアである.てくたまの3DCGモデル作成に用いた.シェアウェア版とフリーウェア版の 2 種類が提供されており、本研究ではフリーウェア版のmetasequoia LE R2.4aを使用した.

2-4

GLMetaseq

 GLMetaseqは、3Dモデリングソフトウェア「Metasequoia」で作成した3Dモデルのデータ である「mqo」ファイルを読み込んでOpenGLで表示するためのC/C++用のプログラミング ライブラリである.これを使用すると、3Dモデルを現実世界に表示するプログラムが非常 に容易になる.

(7)

3.

3DCG

モデルの作成

 てくたまをARで表現するために、3DCGモデルを作成する.作成には「Metasequoia」を 用い、以下の手順で行った. 1. 3DCGモデルの基となる画像を入手(図 5) 図 5 基となる画像 2. Metasequoiaに画像を取り込み、画像に合わせ各パーツを作成する. 3. 各パーツを組み合わせ、一つのモデルにする.(図 6) 図 6 無色透明のモデル

(8)

4. モデルは無色透明であるため、各パーツに色を塗り、模様などの複雑な部分はテク スチャマッピングを行う.

5. 最後に光源設定を行う.(図 7)

(9)

4.

AR

プログラムの構成

 本研究では、紙に印刷されたマーカーをカメラで読み取り,その上に3DCGモデルのてく たまをオーバーレイ表示し、さらにアニメーションするアプリケーション開発を行った. マーカーは 2 枚用意し、1 枚はオリジナルマーカーである近畿大学校章マーカーを用意した. (図 8) 図 8 近畿大学校章マーカーと HIRO マーカー  プログラムは、gsubとgsub_liteのどちらかを用いてプログラムを構築していくが、3DCG モデルにテクスチャマッピングを行っている場合はgsub_liteを推奨する.理由は「2-2

OpenGL・GLUT」で述べた通りであるが、gsub_liteを用いることでOpenGLやGLUTについ てより理解することができる. 

図 9 プログラムの全体像

 ARプログラムの全体像を図 9 に示す.main関数では主に初期化が行われ、その後メイン ループ関数に入り、キーイベント処理による終了処理が行われるまでメインループ関数の処 理を繰り返す.次にmain関数とメインループ関数の説明を行う.

(10)

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 : デプスバッファを使用

(11)

 ⑤3DCGモデルのロード  てくたまの3DCGモデルのmqoファイルをロードする.ロードには「GLMetaseq」ライブラ リの関数であるmqoCreateModel()関数を使う.  ⑥キャプチャの開始  初期化、設定を終え、arVideoCapStart()関数を使用しビデオキャプチャを開始する.  ⑦メイループに入る  glutMainLoop()関数を実行しメインループに入る.この関数を呼び出すとプログラムはイ ベント待ち状態になる.またイベントに対して行う処理関数のことをコールバック関数と呼 び、glutMainLoop()関数を実行する前にはコールバック関数の登録を行う必要がある.以下、 主なコールバック関数を示す.  ・glutDisplayFunc() ・・・・・ 描画処理が必要な時に行う関数  ・glutKeyboadFunc() ・・・・・ キーボードの入力があった時に行う関数  ・glutIdleFunc()   ・・・・・ 他に処理すべきイベントが何も無い時に行う関数  本研究ではglutIdleFunc()関数で繰り返される処理をメインループとする.

4-2 メインループ関数

 ①カメラ画像の取得・描画   arVideoGetImage()関数を使用しビデオデバイスから画像を取得する.その後、 argDispImage()関数で取得した画像をウィンドウに描画する.

(12)

 ②マーカの検出・認識  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()関数を用い、検出されたマーカの情報からマーカ・カメラ間の変換行列を 求める.  ⑤座標変換行列の適用  求めた変換行列を適用することで、以降に行われる描画処理がマーカの中心とした座標系 で行われる.

(13)

 ⑥3Dオブジェクト描画  描画処理では、隠面処理、光源設定を行い、てくたまの3DCGモデルの描画を行う. 「GLMetaseq」ライブラリのmqoCallModel()関数を使用するとモデルがマーカの中心に描画 される.最後に次フレームのカメラ画像の取得を指示し、メインループの処理を繰り返す.  なお、Metasequoiaの座標系はY軸が上を向いているのに対して.ARToolkitはZ軸が上を 向いている.このまま表示すると、モデルが倒れている状態で描画される.これを修正する ために、3DCGモデルをX軸まわりに 90 度回転させてから描画を行う. 図 8 てくたまの 3DCG モデルの描画

(14)

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()を利用する.これは指定した軸ま わりを指定した数値分,回転を行う.ジャンプと宙返りを併せれば,前方宙返り,後方宙返 りなどの動きが可能である.

(15)

5-2 複数マーカ

 2 個のマーカ(以下マーカ 1,マーカ 2)を利用し,マーカ間をモデルが移動する機能を 実装した.マーカ間の位置関係を求めるには, ARToolkit のコマンドarUtilMatInv() と arUtilMatMul() を使用する.arUtilMatInv()はマーカ 1 の座標系からカメラの位置を取得す ることが出来る.arUtilMatMul()はマーカ 1 の座標系からマーカ 2 の位置・距離を求めるこ とが出来る.求めたマーカ間の距離を 1/100 ずつマーカ 1 からマーカ 2 に向け平行移動をす るようにした.マーカ 2 への到達判定には,マーカ間の距離と現在位置を比較し,現在位置 がマーカ間の距離より大きい場合,マーカ 2 の位置に到達したとし,マーカ 1 へ移動する ようにした.

(16)

6.

おわりに

 2011 年 11 月に東広島市で行われた生涯学習フェスティバルにこのアプリケーションを出 展した際、子供らに大変興味を持ってもらうことが出来た.本研究の目的である広報活動に 活用できるアプリケーションが開発できたと考える.また歩行時のてくたまの手足の動きな ど、細かいモーションを加えれば表現力が増すと考える.  今後の課題は、スマートフォンへの対応や、Twitterのつぶやき機能への展開が考えられる.

(17)

参考文献

     [1] 橋本 直:「ARToolkit 拡張現実感プログラミング入門」アスキー・メディアワーク ス(2008)  [2] 谷尻 豊寿:「ARToolkit プログラミングテクニック」カットシステム(2008)  [3] 床井 浩平:「GLUTによるOpenGL入門」工学社(2005)  [4] 工学ナビ:http://kougaku-navi.net/ARToolKit.html

(18)

付録 

(19)

// ======================================================================= // インクルード //======================================================================= #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")

(20)

#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;

(21)

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軸まわりの回転角度

(22)

/* 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; // カメラパラメータ(作業用変数)

(23)

// ビデオデバイスを開く 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 )

(24)

// マーカのロード

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 )

{

(25)

// ディスプレイモードの設定

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の終了処理 }

(26)

//======================================================================= // キーイベント関数

//======================================================================= 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;

(27)

// ジャンプ 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;

(28)

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 ) {

// 画像へのポインタを取得

(29)

// マーカの検出状態を初期化

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の位置を取得

(30)

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 ;

(31)

/* オブジェクトの移動方向 */ 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;

(32)

} 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();

(33)

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 }; // 鏡面反射光

(34)

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; }

(35)

// 時間刻み[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 ){ // 数値の初期化

(36)

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 );

(37)

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 ); // 隠面処理の解除

(38)

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();

(39)

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; }

図 7 完成した 3DCG モデル
図 9 プログラムの全体像

参照

関連したドキュメント

1、研究の目的 本研究の目的は、開発教育の主体形成の理論的構造を明らかにし、今日の日本における

研究開発活動の状況につきましては、新型コロナウイルス感染症に対する治療薬、ワクチンの研究開発を最優先で

は、金沢大学の大滝幸子氏をはじめとする研究グループによって開発され

は、金沢大学の大滝幸子氏をはじめとする研究グループによって開発され

機械物理研究室では,光などの自然現象を 活用した高速・知的情報処理の創成を目指 した研究に取り組んでいます。応用物理学 会の「光

詳細情報: 発がん物質, 「第 1 群」はヒトに対して発がん性があ ると判断できる物質である.この群に分類される物質は,疫学研 究からの十分な証拠がある.. TWA

以上のことから,心情の発現の機能を「創造的感性」による宗獅勺感情の表現であると

第四章では、APNP による OATP2B1 発現抑制における、高分子の関与を示す事を目 的とした。APNP による OATP2B1 発現抑制は OATP2B1 遺伝子の 3’UTR