連載
» 2011年11月16日 03時26分 公開

NyARToolKit for Androidよりも簡単なAndARとはモバイルARアプリ開発“超”入門(3)(3/3 ページ)

[金岡徹,TIS株式会社]
前のページへ 1|2|3       

オリジナルの3Dモデルを表示するには

 AndARのソースコードを確認したところで、ここからは別の3Dモデルを表示してみましょう。

 実際に、3Dモデルを表示したい場合は、サンプルのような立方体ではなく複雑なデータを表示したい場合が多いのではないでしょうか。

 複雑なデータをOpenGL ESを使ってコーディングしていくのは大変です。一般的には、objファイル(WaveFront形式)などの3Dモデルデータを読み込ませる方法が多いと思います。

 3Dモデルデータを読み込ませて3Dモデルとして表示するには、3DモデルデータをパースしてOpenGL ESを使ってレンダリングできるようにする必要がありますが、残念ながらAndARには3Dモデルの読み込みやパースする処理などはありません。

そこで、これらを実現するために、AndARModelViewerのソースを参考に実装してみます。

 「AndARModelViewer」というのはAndARを利用したアプリで、AndARModelViewerもAndARと同様にソースコードを公開しています。このアプリは、objファイルのパーサを実装しているためobjファイルを読み込んで3Dモデルを表示できます。

 このパーサを参考に3Dモデルの読み込みを実装してみます。まず、AndARModelViewerのsrc以下にある以下のパッケージをAndARのsrc以下に配置します。

  • edu.dhbw.andobjviewer.models
  • edu.dhbw.andobjviewer.parser;
  • edu.dhbw.andobjviewer.util;
図5 AndARの3Dモデルディレクトリ構造 図5 AndARの3Dモデルディレクトリ構造

 ここからはソースコードを修正していきます。今回利用したソースコードのリビジョンは199です。

 先ほど説明したように、基本的にはCustomActivity、CustomObject、CustomRendererの3ファイルを修正します。

 ただし、今回は少ない修正でobjファイルからの読み込みを実現するため、CustomObjectはedu.dhbw.andobjviewer.graphics.Model3Dクラスに、CustomRendererはedu.dhbw.andobjviewer.graphics.LightingRendererクラスに書き換えたとみなしてください。そのため、CustomActivityのみの修正となります。

 3Dモデルをobjファイルから読み込ませるためにAndARModelViewerのedu.dhbw.andobjviewer.AugmentedModelViewerActivityのsurfaceCreatedを参考に、CustomActivityにSurfaceHolder.Callbackをimplementsし、surfaceCreatedをオーバーライドします。

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        super.surfaceCreated(holder);
        //load the model
        //this is done here, to assure the surface was already created, so that the preview can be started
        //after loading the model
        if(model == null) {
            waitDialog = ProgressDialog.show(this, "",
            getResources().getText(R.string.loading), true);
            waitDialog.show();
            new ModelLoader().execute("sofa.obj");
        }
    }

 モデルの読み込みは、ModelLoader().execute("sofa.obj")で行っています。今回はAndARModelViewerに付属していたsofa.objを表示するため、引数に指定しています。

 ModelLoaderはAsyncTaskを使っているため、モデルを非同期に読み込みます。AugmentedModelViewerActivityでは、ModelLoaderを以下のように実装しています。

    private class ModelLoader extends AsyncTask<Void, Void, Void> {
 
        @Override
        protected Void doInBackground(Void... params) {
 
            Intent intent = getIntent();
            Bundle data = intent.getExtras();
            int type = data.getInt("type");
            String modelFileName = data.getString("name");
            BaseFileUtil fileUtil= null;
            File modelFile=null;
            switch(type) {
            case TYPE_EXTERNAL:
                fileUtil = new SDCardFileUtil();
                modelFile =  new File(URI.create(modelFileName));
                modelFileName = modelFile.getName();
                fileUtil.setBaseFolder(modelFile.getParentFile().getAbsolutePath());
                break;
            case TYPE_INTERNAL:
                fileUtil = new AssetsFileUtil(getResources().getAssets());
                fileUtil.setBaseFolder("models/");
                break;
            }
            //read the model file:
            if(modelFileName.endsWith(".obj")) {
                ObjParser parser = new ObjParser(fileUtil);
                try {
                    if(Config.DEBUG)
                        Debug.startMethodTracing("AndObjViewer");
                    if(type == TYPE_EXTERNAL) {
                        //an external file might be trimmed
                        BufferedReader modelFileReader = new BufferedReader(new FileReader(modelFile));
                        String shebang = modelFileReader.readLine();
                        if(!shebang.equals("#trimmed")) {
                            //trim the file:            
                            File trimmedFile = new File(modelFile.getAbsolutePath()+".tmp");
                            BufferedWriter trimmedFileWriter = new BufferedWriter(new FileWriter(trimmedFile));
                            Util.trim(modelFileReader, trimmedFileWriter);
                            if(modelFile.delete()) {
                                trimmedFile.renameTo(modelFile);
                            }
                        }
                    }
                    if(fileUtil != null) {
                        BufferedReader fileReader = fileUtil.getReaderFromName(modelFileName);
                        if(fileReader != null) {
                            model = parser.parse("Model", fileReader);
                            model3d = new Model3D(model);
                        }
                    }
                    if(Config.DEBUG)
                        Debug.stopMethodTracing();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            waitDialog.dismiss();
            //register model
            try {
                if(model3d!=null)
                    artoolkit.registerARObject(model3d);
            } catch (AndARException e) {
                e.printStackTrace();
            }
            startPreview();
        }
    }

 ただし、ModelLoaderをこのまま実装しても、うまく動作しないので、ソースコードを修正していきます。

 まず、「ModelLoader().execute()」にString型の引数を取らせるため、Genericsの1番目の型をStringとし、「doInBackground(Void…… params)」の引数を「doInBackground(String…… params)」に変更します。

 次に、Intentからデータを受け取らないように変更します。AndARModelViewerではIntentからtypeとdataを取り出していますが、渡ってくるIntentはないのでコメントアウトして、以下のコードを追加します。

    type = TYPE_INTERNAL;
    modelFileName = params[0];

 「type」は、どこからモデルデータを読み出すかを指定しています。今回はAndroidアプリ内のassets/modelからデータを読み込むために「TYPE_INTERNAL」とします。読み込むデータは、「assets/models/sofa.obj, sofa.mtl, sofa.png」として、AndARModelViewerからコピーして配置します。

 「modelFileName」は、3Dモデルデータのファイル名を示しており「ModelLoader().execute("sofa.obj")」の引数で渡されている文字列「sofa.obj」を代入します。

 修正したものが以下のコードです。

//    private class ModelLoader extends AsyncTask<Void, Void, Void> {
    private class ModelLoader extends AsyncTask<String, Void, Void> {
 
        @Override
//        protected Void doInBackground(Void... params) {
        protected Void doInBackground(String... params) {
//            Intent intent = getIntent();
//            Bundle data = intent.getExtras();
//            int type = data.getInt("type");
            int type = TYPE_INTERNAL;
//            String modelFileName = data.getString("name");
            modelFileName = params[0];

 これで、assets/models/sofa.objを読み込めるようになります。objファイルを読み込んだら、以下のメソッドでobjファイルをパースしてModelクラスのインスタンスmodelを作成します。

model = parser.parse("Model", fileReader);

 さらに作成したmodelを引数としてARobjcectクラスを継承したModel3Dのインスタンスを作成し、マーカー検出時に3Dモデルを描画するように登録します。以下のメソッドで実現しています。

artoolkit.registerARObject(model3d);

 これにより、マーカー検出時にmodel3dのクラスModel3Dのdrawメソッドを呼びます。詳細は割愛しますが、OpenGL ESを利用して3Dモデルを描画します。

 検出するマーカーについては、Model3Dのコンストラクタ内の以下のメソッドで検出するマーカーの種類を定義します。

super("model", "barcode.patt", 80.0, new double[]{0,0})

 今回利用するマーカーは幅80mmの「barcode.patt」です。

 最後に、CustomActivityのonCreateを以下のように修正します。

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
//        CustomRenderer renderer = new CustomRenderer();//optional, may be set to null
        LightingRenderer renderer = new LightingRenderer();//optional, may be set to null
        super.setNonARRenderer(renderer);//or might be omited
        artoolkit = getArtoolkit();
        getSurfaceView().getHolder().addCallback(this);
 
/*
        try {
            artoolkit = super.getArtoolkit();
            someObject = new CustomObject("test", "patt.hiro", 80.0, new double[]{0,0}); // 【※】
            artoolkit.registerARObject(someObject); // 【※】
            someObject = new CustomObject("test", "android.patt", 80.0, new double[]{0,0});
            artoolkit.registerARObject(someObject);
        } catch (AndARException ex){
            //handle the exception, that means: show the user what happened
            System.out.println("");
        }
*/

【※】の2つの処理は、それぞれ以下の理由によりコメントアウトします。

  • Model3Dクラスのコンストラクタで呼ばれている
  • ModelLoader.onPostExecute(Void result)で同メソッドが呼ばれている

 また、「artoolkit.registerARObject(someObject)」をコメントアウトしたため、try-catchも不要になります。

 「LightingRenderer renderer = new LightingRenderer()」は、「CustomRenderer renderer = new CustomRenderer()」のままだと3Dモデルが全体的に暗くレンダリングしてしまうので、AndARModelViewerのrendererのLightingRenderer()メソッドに置き換えます。

 さらに、CustomActivityでsurfaceCreatedをオーバーライドしたため、getSurfaceView().getHolder().addCallback(this)でcallbackを再設定しておきます。

 後は、適切なパッケージをimportし、R.string.loadingは「res/values/string.xml」にして、以下を追加します。

  • Name:loading
  • Value:Loading. Please wait...

 このように修正することで、objファイルを読み込んで3Dモデルを表示できるようになります。

カスタマイズしたAndARアプリを動かすと……

 では、実際にアプリを動かしてみましょう。アプリを起動したらbarcodeマーカー(図6)にかざします。すると、ソファーの形をした3Dモデルを表示していることが確認できると思います(図7)。

図6 barcodeマーカー 図6 barcodeマーカー
図7 アプリ起動後 図7 アプリ起動後

 これで、別の3Dモデルをマーカー上に表示できました。

 が、ここで注意点があります。このサンプルはAndARで読み込み可能なobjファイルの形式を制限しており、objファイルであっても読み込めない場合があります。具体的な制限は以下の通りです。objファイルを読み込む場合には、この点に注意してください。

  • テクスチャを貼っている場合、Vertex(頂点)、Texture(テクスチャ)、Normal(法線)の情報がそろっていること
  • テクスチャを貼っていない場合、Vertex(頂点)、Normal(法線)の情報がそろっていること
  • 面の構成は三角形になっていること

 独自に3Dモデルを作成した場合には、「Blender」というソフトを利用してobjファイルのエクスポートをすると、本アプリに対応する形式で出力できます。うまく表示しない場合は、出力時に上記の制約を満たすように設定を行い、エクスポートしてみてください。

AndARを利用するメリット/デメリット

 最後に、AndARのメリット・デメリットについてまとめておきます。

AndARのメリット

 AndARのメリットとしてはコアとなるクラスを継承して作成するようになっているため、「CustomActivity」「CustomObject」「CustomRenderer」の3クラスの修正だけでオリジナルの3Dモデルを自由に変更して表示できます。

 ソースコードもオブジェクト指向を強く意識しているため読みやすく、ネイティブコードにアクセスするといったコアな部分をラップしているため、設定などの定型的な処理や、マーカー検出や変換行列の計算などの複雑な処理をあまり意識しなくても比較的簡単にマーカー上に3Dオブジェクトを表示できます。そのため、その他のライブラリやサンプルアプリと比較すると扱いやすいと思います。「コアな部分は取りあえず置いて、おいて動かしたい!」という方向きです。

 一方、前回紹介したNyARToolkit for Androidはソースを追うことが難しい部分も多く、扱うには慣れが必要だと思います。ただし、処理の流れを確認したい場合には、とても勉強になると思います。

AndARのデメリット

 AndARのデメリットとしては複雑なモデルを読み込ませる場合に、大きく手を入れる必要があるという点があります。

 今回はAndARModelViewerからobjフォーマットの3Dモデルをパースする処理を持ってきましたが、先に述べたようにobjファイルの特定の形式にのみ対応しているため、汎用的なobjファイルの読み込みや他の形式の読み込みはできません。そのため、複雑なモデルを表示したい場合は、他のライブラリをうまく組み合わせて使うか、自作する必要があります。

 もし、すでに独自にパーサなどを保有している場合は、それらを利用することによって比較的少ないコストで3Dモデルを表示できる可能性もあります。

 ちなみに、NyARToolkit for Androidでは「min3d」というフレームワークを利用しているので、objファイル以外のファイル形式の3Dモデルも読み込ませることができます。

 この辺りは利用するシチュエーションによって、どちらが良いかが変わってくると思いますので、用途に合った方法を適切に選択してください。

次回は、OpenGL ESを利用したアニメーション

 ARアプリの作成は、ライブラリやサンプルアプリを利用することで手軽に実現できるようになってきました。ARを実現するためのライブラリは今回のAndARのようなオープンソースのものだけに限らず、さまざまな種類のものが続々と登場してきています。実現したいアプリに合わせて適切なライブラリやサンプルアプリを活用し、オリジナルなARアプリを作成してみてください。

 次回は、今回紹介したAndARをベースに3DのモデルをOpenGL ESを利用してアニメーションする手法について、簡単な例を交えて説明します。


前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。