はじめに
この記事はクソアプリ Advent Calendar 2022 の3日目の記事です。
投稿が遅れてすみません(土下座)
今回はProcessing for AndroidのAR機能を使ってお寿司爆弾を作りました。
具体的には、「スマホカメラから取り込んだ現実世界の映像にARでお寿司の3Dモデルを重ねて配置して、一定時間が経過するとお寿司のネタが吹っ飛ぶ」 というAndroidアプリを作成しました。
「作る目的は?」とか「どんな役に立つの?」とか、そんな問いはクソアプリ開発には無用でしょう!
考えすぎて我に返る前に完成させられるかが勝負です。では参りましょう!
完成品🍣
とりあえず完成品をご覧ください。
こんな感じでタップした場所にARでお寿司が出現し、カウントダウンの後にネタが吹っ飛びます。ネタが地面に接触するとシャリとともに消滅します。複数を同時に設置することも可能です。
レシピ解説
では技術的な部分について解説していきます。
Processing for Android
今回はAndroidアプリを作成するためにProcessingという言語を使用しました。Processingはデジタルアートの制作に特化した言語です。コンテンツ制作以外の煩わしい処理を自動で行ってくれるためユーザーはコンテンツの制作のみに集中することができます。
また、この言語はJavaの一部として動くように作られており、Processingで作成したスケッチをAndroid上で動かせるようにするためのライブラリがProcessing for Androidです。こちらもほとんどの操作が簡略化されており、ユーザーはAndroidアプリの開発に詳しくなくてもIDEに従って操作をすることで比較的簡単にスケッチをAndroid上で実行できるようになっています。
AR in Processing
Processing for AndroidではAR機能も非常に簡単に利用することができます。
Processing for Androidのチュートリアルでは、「3Dコンテンツを現実世界の映像に重ねて描画するためには、物体認識やデバイスの姿勢検知など複雑な処理を実装する必要があるが、これらを自動で計算するためのフレームワークとしてGoogleの提供するARCoreがある。ProcessingからARCoreに直接アクセスすることも可能だが、ProcessingのAndroidモードのARライブラリを通してARCoreを使用することでさらに簡単にARコンテンツを作成することができる。」と説明されています。
先ほどの完成品の映像の中でも、奥行を考慮した上でタップしたポイントにお寿司が置かれていたり、スマホの動きに合わせて視点が移動したりしていますが、特別にコードを書くことなく自動でこういった処理を行ってくれます。
開発の流れ
お寿司の3Dデータを作成
まずARで描画するためのお寿司をつくります。Processingでは.objファイルを読み込むことができるため、.obj形式のお寿司を作ります。
今回はTinkercadを使用しました。
こんな感じで握っていきます。
今回の実装ではネタを吹っ飛ばす時に、シャリとネタを分離する必要があるため最終的にそれぞれ別のobjファイルとして保存しました。右上のエクスポートボタンから.OBJを指定すると3Dモデルのメッシュ構造の情報を持つ.objファイルと色やテクスチャの情報を持つ.mtlファイルの組としてデータをダウンロードできます。
Tinkercadで作った3DモデルをProccessingで使用するときの注意点
Tinkercadで作った3DモデルをProccessingで使用するにあたっていくつか注意することがあります。
x軸回りの向き
Tinkercadで作成した3DモデルをそのままProcessingで読み込むとx軸回りに90度回転した向きで表示されてしまいます。そのためあらかじめTinkercadで作成する時点で下の画像のように90度回転させておくとProcessing側で扱いやすいです。
ワークスペースの中心にオブジェクトを配置する
Tinkercadのワークスペースの中心にオブジェクトを配置しておかないとProcessingで読み込みだときに3Dモデルの座標がずれてしまいます。グリッドへのスナップなどをうまく使ってオブジェクトをワークスペースの中心に配置します。
オブジェクトの大きさ
Tinkercadで作ったオブジェクトをARを使用したProcessingで表示すると、思ったよりも3Dモデルが大きいことがあります。Tinkercad側で小さめに作っておくといいと思いますが、細かい調整はProcessing側のscale()
関数で行うと良いと思います。
.mtlファイルの名前の変更
Tinkercadで作成した3Dモデルは.objファイルと.mtlファイルの組としてダウンロードされます。
当然これらのファイル名を変更したい場面もあると思いますが、特に.mtlファイル名を変更したときは注意が必要です。.objファイルは内部で.mtlファイルを読み込んでいるため、.mtlファイル名を変更したときはそのファイルを読み込んでいる.objファイルの中身を以下のように書き換える必要があります。
- .objファイルをメモ帳等のテキストエディタで開く
-
mtllib ~~.mtl
という部分を探す -
~~.mtl
を自分が変更した.mtlのファイル名に合わせる
ProcessingによるARの実装
お寿司の3Dモデルができたので、いよいよコードを書いていきます。
コードを書くにあたって、公式サイトのチュートリアルのサンプルコードを参考にしました。
今回はProcessingのAndroidモードの環境構築については解説しません。公式のチュートリアル等が参考になります。
実行環境は以下の通りです。どちらも絶妙に古いですが、最新のバージョンでも動くはずです。
実行環境
- Processing 3.5.4
- Android 9
コードの解説
ProcessingでのARアプリ開発についての日本語の情報が少なく、僕自身今回の開発で苦労したこともあり、ARのスケッチに特有の書き方を中心に解説します。ソースコードとお寿司の3DデータはGitHubにあります。
3Dモデルの読み込み
先ほどTinkercadで作成した3Dモデルは、スケッチの.pdeファイルと同じ階層にdata
という名前のフォルダを作成しその中に配置します。loadShape()
関数で読み込んでPShape
クラスの変数に代入します。表示したいときはshape(shari)
やshape(neta)
のように使います。
PShape shari;
PShape neta;
void setup() {
shari = loadShape("shari.obj");
neta = loadShape("neta.obj");
}
ARライブラリのインポート
ARを扱いやすくするために、AR in Processingで説明したProcessingのARライブラリを使用します。
またfullScren()
ではARレンダラを指定します。ARレンダラを使用することでスケッチをカメラ映像に重ねて、デバイスの動きを自動で更新してくれます。
import processing.ar.*; // ARライブラリをインポート
void setup() {
fullScreen(AR); // ARレンダラを使用
}
ARライブラリで使用するクラス
AR機能を実装する際に必要になるクラスがいくつかあります。
ARTracker
ARのスケッチには1つ以上のARTracker
オブジェクトが必要で、カメラ映像から検出されたトラック可能な平面を扱うために使います。
ARTrackable
ARTrackable
クラスは、ARTracker
によって検出された平面を扱うためのクラスです。検出した平面の広さやその平面が床・天井・壁のどれなのかといった情報を取得したり、次に説明するARAnchor
を紐づけたりするために使用します。
ARAnchor
ARAnchor
クラスは、ARTrackable
オブジェクトに対して紐づけられた位置の情報を扱います。例えば画面をタップした時に、タップしたポイントが、ある一つのARTrackable
オブジェクトに対してどのような位置関係にあるか、といった情報を保存します。
それぞれのクラスの使いどころ
今回のプログラムで使用した場面を例に、それぞれのクラスの使い方を紹介します。
まずARTracker
についてはsetup()
内で使用する必要があります。これはもうテンプレートと考えていいと思います。
import processing.ar.*;
ARTracker tracker; // ARTrackerクラスの変数を宣言
void setup() {
fullScreen(AR);
tracker = new ARTracker(this); // ARTrackerオブジェクトを作成
tracker.start(); // start()メソッドでトラッキングを開始
}
タップ時に新しくお寿司を配置するときは通常のスケッチと同様mousePressed()
内に処理を書いていきます。
ARTrackable hit = tracker.get(mouseX, mouseY);
ではARTracker
のメソッドを使ってタップしたポイントに存在するトラック可能な平面のARTrackable
オブジェクトを取得しています。
タップしたポイントにトラック可能な平面が存在した場合、ARAnchor tapAnchor = new ARAnchor(hit);
でその平面に対するタップしたポイントの位置の情報を持つARAnchor
オブジェクトを生成しています。この場面では、このARAnchor
オブジェクトこそお寿司を配置するために必要なアンカーの情報になります。
お寿司爆弾は自作のOsushi
クラスで管理しており、それぞれのお寿司インスタンスに位置情報が必要なため、お寿司インスタンスを生成するときにこのARAnchor
オブジェクトを渡しています。
ArrayList<Osushi> osushiBombs = new ArrayList<Osushi>(); //お寿司爆弾のインスタンスのリスト
// ----
// 省略
// ---
void mousePressed() {
// お寿司の数をチェック
if(osushiBombs.size() < maxOsushi) {
ARTrackable hit = tracker.get(mouseX, mouseY);
if (hit != null) {
ARAnchor tapAnchor = new ARAnchor(hit);
osushiBombs.add(new Osushi(millis(), tapAnchor));
}
}
}
// ----
// 省略
// ---
class Osushi {
// ----
// 省略
// ---
}
お寿司のレンダリングには先ほどのARAnchor
オブジェクトの情報を使います。
this.anchor.attach();
を実行すると座標原点がアンカーの位置に移動します。
レンダリングが終わったらthis.anchor.detach();
で座標をもとに戻しています。
つまりこの2つのメソッドの間ではAR特有の難しいことを考えずに普通のスケッチと同様の感覚でコードを書くことができます。
class Osushi {
// ----
// 省略
// ---
ARAnchor anchor; //このお寿司のアンカー
Osushi(int madeTimeIn, ARAnchor anchorIn) {
this.madeTime = madeTimeIn;
this.anchor = anchorIn;
}
// ----
// 省略
// ---
void render() {
this.anchor.attach();
pushMatrix();
scale(this.scale*0.1);
rotateY(this.muki);
shape(shari);
pushMatrix();
translate(
this.netaX.x,
this.netaX.y,
this.netaX.z
);
rotateZ(this.netaTheta.x);
rotateY(this.netaTheta.y);
rotateX(this.netaTheta.z);
shape(neta);
popMatrix();
popMatrix();
fill(0);
pushMatrix();
float x = this.remaining - floor(this.remaining);
rotateY(TAU*x*x*(3-2*x));
rotateX(PI);
translate(0, -0.1, 0);
scale(0.001);
if(!this.isExploded) {
text(round(this.remaining), 0, 0, 0);
}
popMatrix();
noFill();
this.anchor.detach();
}
}
まとめ
はじめてのARアプリ作成でしたが、ひとまず作りたかったものを形にすることができました🍣
ProcessingのARライブラリを使ってみて、やはりProcessingらしく難しい部分をユーザーが触らなくてもいいように仕組みを単純化してある印象を受けました。複雑なことをするためには不十分かもしれませんが、とりあえずARを使ってみたいという用途であればかなり扱いやすいと思います。
今回はここまででしたが、機会があればこのアプリをPlayストアでの配布までやりたいと思います。
ここまで読んでいただきありがとうございました!