AndARのソースコードを確認
どのような仕組みで動いているか実際にソースコードを見ながら確認していきましょう。「src」以下には以下の5つのパッケージがあります。
- edu.dhbw.andar
ARを実現するためのコアとなるクラスを格納 - edu.dhbw.andar.exceptions
例外(Exception)に関するクラスを格納 - edu.dhbw.andar.interfaces
インターフェイスを格納 - edu.dhbw.andar.pub
カスタマイズ用のクラスを格納 - edu.dhbw.andar.util
ユーティリティクラスを格納
アプリを起動したとき、最初に呼ばれるのが「edu.dhbw.andar.pub.CustomActivity.onCreate()」です。このクラスはAndARActivityを継承しています。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
「super.onCreate(savedInstanceState)」で「edu.dhbw.andar.AndARActivity.onCreate()」を呼び、さまざまな初期化処理を行います。その後、「super.setNonARRenderer(renderer)」でレンダラを設定し、「artoolkit.registerARObject(someObject)」で「どのマーカーに対してどのような3Dモデルを描画するのか」という情報を登録しています。
AndARは「AndARActivity.onCreate()」を基点としてARを実現するためのコアな処理を次々と実行していきます。そのため、ここからは「AndARActivity.onCreate()」がどのような処理をしているのか詳しく見ていきます。
AndARActivity.onCreate()の処理
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
処理の流れは、以下のようになっています。
- ARToolkitクラス(ネイティブコードのARToolkitとの橋渡しを担っているクラス)のartoolkitを生成
- SurfaceViewの生成とSurfaceHolderの設定
- GLSurfaceViewの生成とAndARRendererクラスのインスタンスのセット(AndARRenderer.onDrawFrame()を定期的に呼ぶ)
- PreviewCallbackの設定(cameraHandler.onPreviewFrame()を定期的に呼ぶ)
つまり、AndARActivityはカメラプレビューとレンダリングの設定を行っていることになります。これにより、「CameraPreviewHandler.onPreviewFrame()」「AndARRenderer.onDrawFrame()」を定期的に呼ぶようになります。
基本的には、この2つのメソッドが協調して動作することにより、カメラから取得したプレビュー画像上に3Dモデルを表示できます。
具体的には、以下の流れで処理を実行します。
- CameraPreviewHandler.onPreviewFrame()でカメラから取得したプレビューデータの中にマーカーが存在するかをチェック
- もしマーカーが存在すれば、AndARRenderer.onDrawFrame()でマーカー上に3Dモデルをレンダリング
ここからは、「CameraPreviewHandler.onPreviewFrame()」「AndARRenderer.onDrawFrame()」について、それぞれ詳しく見ていきましょう。
edu.dhbw.andar.CameraPreviewHandler.onPreviewFrame()の処理
まず、CameraPreviewHandler.onPreviewFrame()のソースコードを確認します。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
ここでは、カメラプレビューを取得し、取得したデータを利用して、それぞれ以下の処理を行っています。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
この2つの処理ではThreadを継承した「convWorker」「detectMarkerWorker」というスレッドが動作しています。この2つの処理についてそれぞれ詳しく説明していきます。
まず、「convWorker.nextFrame(data)」について説明します。convWorkerはThreadクラスを継承した「ConversionWorker」というクラスのインスタンスで、カメラプレビューから取得した「YCbCr 4:2:0 SP」という形式のデータをRGB形式に変換する処理を行っています。
このとき、カメラプレビューは定期的に取得され続けるので、1フレームずつ取り出して変換処理に渡す必要があるのですが、すべてのフレームを変換するのはパフォーマンスの面で効率が良くありません。そのため、変換処理が完了するまで新規のフレームを破棄し、受け付けないようにしています。
その制御を行っているのがconvWorker.nextFrame(data)です。このようにconvWorkerではカメラから取得したプレビューのデータを次々とRGB形式に変換していきます。変換したデータは保持され、レンダリング処理時に利用されます。
次に、「markerInfo.detectMarkers(data)」について説明します。markerInfoはARToolkitクラスのインスタンスなので、ARToolkitクラスの「detectMarkers(data)」が呼ばれます。その中で、「detectMarkerWorker.nextFrame(image)」が呼ばれます。
detectMarkerWorkerは、Threadクラスを継承した「DetectMarkerWorker」というクラスのインスタンスです。detectMarkerWorkerはカメラプレビューのデータからマーカーの検出と変換行列の計算を行っています。具体的には以下のメソッドで行います。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
このネイティブメソッドの戻り値によりマーカーを検出したかどうかを判断し、検出した場合にはARObjectクラスの「visible」をtureとします。また、このとき変換行列の計算を行い、ARObjectクラスのglMatrixに格納しています。
convWorkerと同様に、カメラから取得可能なプレビューデータ全てからマーカー検出をするのは非効率なので、「detectMarkerWorker.nextFrame(image)」でマーカーを検出するフレームを制御しています。
まとめると、CameraPreviewHandler.onPreviewFrame()ではプレビューのデータを取得し、2つのスレッドを使って以下の処理を行っています。
- convWorkerでは、取得したプレビューのデータを「YCbCr 4:2:0 SP」からRGBに変換し、データを格納する(データは「AndARRenderer.onDrawFrame()」でレンダリングする際に利用)
- detectMarkerWorkerは取得したプレビューのデータの中からマーカーが存在するかを検出し、存在した場合には変換行列を計算
ここまでで、カメラプレビューの取得とプレビュー内でのマーカーの判定のロジックを確認していきました。次は、実際にマーカー検出時の3Dモデルのレンダリングについて見ていきます。
edu.dhbw.andar.AndARRenderer.onDrawFrame()の処理
ここからは、マーカー検出時に、どのように3Dモデルをレンダリングしているのかについて説明します。
3Dモデルのレンダリングには「AndARRenderer」クラスを利用します。先ほど、CustomAcitvityのonCreateでの初期設定により「AndARRenderer.onDrawFrame()」を定期的に呼ぶことは説明しました。
「AndARRenderer.onDrawFrame()」では、具体的に何をしているのかソースコードを確認してみましょう。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
AndARRenderer.onDrawFrame()では大きく3つの処理を行っています。
- カメラから取得したデータを2Dのテクスチャにしてレンダリングする処理(convWorkerで取得したRGB形式のデータを利用)
- 3Dモデルをレンダリングする処理
- スクリーンショットを取得する処理
今回は「2. 3Dモデルをレンダリングする」についてのみ説明します。3Dモデルをレンダリングしているのは、以下のメソッドです。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
markerInfoはedu.dhbw.andar.ARToolkitクラスのインスタンスです。「ARToolkit.draw()」は以下のようになっており、ARObjectクラスの配列「arobjects」内にあるARObjectのインスタンスを1つずつ取り出して、obj.isVisible()がtrueであるかチェックしています。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
arobjectsへのARObjectインスタンスの格納は、「edu.dhbw.andar.pub.CustomActivity」で行います。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
具体的には、「artoolkit.registerARObject(someObject)」を呼ぶことによって、引数のsomeObjectをarobjectsに格納します。
someObjectはARObjectクラスのサブクラスであるCustomObjectのインスタンスで、インスタンス生成時に検出したいマーカーの種類を第2引数(patt.hiro)で渡しています。ここで指定したマーカーを検出した場合には、someObject.isVisible()が「true」となります。
つまり、ARToolkit.draw()でのobjはCustomObjectのインスタンスであり、マーカーを検出した場合にはobj.isVisible()がtrueとなるため、CustomObjectのdraw()を呼ぶことになります。以下はCustomObject.draw()のコードです。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
CustomObjectは3Dモデルの描画を一手に引き受けています。
CustomObjectはARObjectを継承しているので、super.draw(gl)が呼ばれると、edu.dhbw.andar.ARObject.draw()を実行します。マーカー上に3Dモデルを表示するにはマーカーから検出した変換行列を適用する必要がありますが、それらの処理はARObject.draw()が行います。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
具体的には、「glMatrixBuffer.put(glMatrix)」で、検出したマーカー情報から取得した変換行列を適用しています。変換行列の適用が終われば、後は3Dモデルを描画するだけでマーカー上に3Dモデルを表示するようになります。
「super.draw(gl)」を呼ぶだけで変換行列の適用まで行うので、ARObjectのサブクラスのdrawメソッドではsuper.draw(gl)を呼んだ後は、3Dモデルの描画のみに意識をすれば良くなります。
今回、描画すべき3Dモデルは立方体ですが、それはbox.draw(gl)を呼ぶことにより実現します。詳細は割愛しますが、このメソッドはOpenGL ESを使って立方体を描画します。
AndARでは、このようにして、カメラから取得した画像をレンダリングした後、その画像内のマーカー上に3Dモデルをレンダリングすることによってマーカー型のARを実現しています。
次ページでは、ここで中身を紹介したサンプルアプリをカスタマイズしてオリジナルにする方法を解説します。
Copyright © ITmedia, Inc. All Rights Reserved.