image-ghost-canvas はブラウザにある canvas の機能を用いて画像の縦横比を保ったまま拡大または縮小した後に正方形にトリミングを行うライブラリツールです。
ブラウザ上でアイコンの画像をトリミングして表示したい時などに便利なツールです。

使い方

使い方は非常に簡単です。現時点ではトリミング機能を行う「resizeImage」メソッドしか用意しておりませんが、機能の拡張も検討しております。また、Plunker に anugularJS 上で動く物をアップしてあります。そちらの方も確認してみてください。

インストール

npm コマンドでインストールします。

npm i image-ghost-canvas

インポート

import します。下記は javascript の import を例にした場合です。

import  ImageGhostCanvas  from  'image-ghost-canvas';

インスタンス化

import したら今度はインスタンス化します。「new ImageGhostCanvas」はオプションとして size を渡すことができます。size はリサイズ後の画像サイズを指定できます。オプションの詳細はこちらから確認することができます。

public imageGhostCanvas = new ImageGhostCanvas();

resizeImage メソッドを実行

トリミングには resizeImage メソッドを使用します。data URL 化した画像データの情報を resizeImage メソッドに渡すと、Promise を返します。この Promise は成功すると、結果を data URL の形式で返し、失敗した場合は Error オブジェクトを返します。

const reader = new FileReader();  

// data.srcElement.files[0] is file information from image was upload via input file tag.
reader.readAsDataURL(data.srcElement.files[0]);  
reader.onload = () => {
 // success
 imageGhostCanvas.resizeImage(reader.result)
 .then((data) => {
     imageSrc = data;
 });

 // error
 imageGhostCanvas.resizeImage(reader.result)
 .catch((error) => {
     // when it result error, do something for error.
 });
};

npm ライブラリ

https://www.npmjs.com/package/image-ghost-canvas



Node.js で Cloud Spanner を使う

Cloud SpannerGoogle Cloud Platform(GCP) で提供されているデータベースです。
分散データベースによるスケーラビリティーと、ACID トランザクションによる強い整合性が両立されているのが特徴です。

また、SQL を使ったデータの抽出も可能で NoSQL を使う場合の整合性の問題と、SQL を使う場合のスケーラビリティの問題のどちらも解決が可能です。

私は名前を聞いたことがある程度だったのですが、先日「『ドラゴンボール レジェンズ』の舞台裏を支える Google Cloud」という記事を読み、Cloud Spanner に興味を持ちました。

そこで、普段から使っている Node.js と Node.js 用のクライアントライブラリである @google-cloud/spanner を利用して、Cloud Spanner を試してみることにしました。

いつものどおり、作成したプログラムは GitHub で公開していますので、合わせて参考にしてみてください。

なお、今回は Vagrant などは使わず、私の個人のマシンに環境を構築して開発と動作確認を行いました。

Name Version
OS Debian GNU/Linux 10(Buster / Testing)
Node.js 10.6.0
@google-cloud/spanner 1.5.0

参考にした情報

Cloud Spanner を動かして見る前に、名前しか知らなかったこのデータベースについての情報を集めました。
公式のドキュメントAPI のリファレンス以外で参考にしたのは、次のような情報です。

これらの情報によると、Cloud Spanner を利用する際には、次のような点に注意する必要があるようでした。

  • 連番や時間ベースの ID は使わず、ハッシュ関数に通したり UUID を利用する
  • JOIN が必要なテーブルは同じサーバーに配置するため、親子関係(インターリーブ)の指定を行う
  • クエリキャッシュを活用するため、クエリの作成に QueryBuilder を使用する

Max connections

Cloud Spanner を際に注意することが判りましたが、他にはないのでしょうか。
MySQL を使っていた頃に、よく同時接続数が問題になっていた経験があるため、その辺りは特に気になりました。
しかし Cloud Spanner のドキュメントには、同時接続数についての説明が無いように見えました。

これは、Google さんがうまい感じにやってくれているから必要ないのかな? と思いつつ、資料をみていると「REST を使用して Cloud Spanner を使ってみる」というページの「セッションを作成する」という項目に、次のような記載がありました。

データの追加、更新、削除、クエリを行うには、その前にセッションを作成する必要があります。
セッションは Cloud Spanner データベース サービスとの通信チャンネルを表します。
(Cloud Spanner クライアント ライブラリを使用している場合は直接セッションを使用せず、このクライアント ライブラリが代わりにセッションを管理します)

参考資料にもある「超実践 Cloud Spanner 設計講座」を見る限り、セッション数は「チャンネル数 x 100」で、デフォルトでは 4 x 100 = 400 のようです。
基本的にはデフォルトのままでも同時接続数が問題になることは余り無さそうです。
また、ノード毎に 10,000 セッションが上限ということで、必要に応じてチャンネルやノードを増やしていけば良さそうです。

Cloud Spanner を使ってみる

事前に準備が必要なこと

Clous Spanner の API にアクセスするにはサービスアカウントキーというものが必要になります。
サービスアカウントキーは Google Cloud Console の画面から作成が可能です。
Cloud Spanner Client Libraries のページなどを参考に、サービスアカウントキーを作成し、JSON ファイルをダウンロードしておいてください。

ダウンロードした JSON ファイルのパスを GOOGLE_APPLICATION_CREDENTIALS という環境変数に設定することで、API へアクセスできるようになります。

テーブルの作成

今回、テストで使用するテーブルは Google Cloud Console の画面から作成しました。
本来であれば API を使って Node.js からテーブルを作成したり Terraform などを利用した方がいいと思います。
しかし、今回は Cloud Spanner を使ってみるとが目的なので、その辺りは省いています。

データベースを作成する際に「データベーススキーマの定義」の項目で「テキストとして編集」の項目を ON にして、次のようなスキーマを登録しました。

CREATE TABLE members (
    member_id STRING(36),
    email STRING(255),
    password STRING(64)
) PRIMARY KEY(member_id);

CREATE TABLE profiles (
    member_id STRING(36),
    name STRING(64) NOT NULL,
    kana STRING(64),
    age INT64,
    birthday STRING(5),
    units Array<STRING(36)>
) PRIMARY KEY(member_id), INTERLEAVE IN PARENT members ON DELETE CASCADE;

CREATE TABLE units (
    unit_id STRING(36),
    name STRING(64)
) PRIMARY KEY(unit_id);

コメントが使えない

MySQL では #-- 以降がコメントとして扱われます。
また /* */ といったコメントの書き方もあります。
それに習ってコメントを書いていたのですが Google Cloud Console の画面に貼り付けたところ、エラーとなってしまいました。

また、普段は各カラムにも COMMENT "コメント" の形でコメントを残すようにしているのですが、こちらも同様にエラーとなりました。

親テーブルは複数設定することができない

最初、スキーマには次のような関連テーブルがありました。
しかし、このスキーマはエラーになってしまい、登録することができません。

CREATE TABLE member_unit (
    member_id STRING(36),
    unit_id STRING(36)
) PRIMARY KEY(member_id, unit_id),
  INTERLEAVE IN PARENT members ON DELETE CASCADE,
  INTERLEAVE IN PARENT units ON DELETE CASCADE;

調べてみたところ、1 つのテーブルが持てる親テーブルは、1 つだけのようでした。
テーブルを階層化することで解決できるようではありますが、関連テーブルが複数ある場合には、この方法が取れません。

インターリーブは FOREIGN KEY に近という説明もありましたが、思想も違うように思いますし、同じように使えるものでは無さそうです。
Cloud Spanner では、データ型として Array をサポートしているため、関連テーブルは作らずに、配列で情報をもつのが良さそうです。

CRUD のテスト

簡単な使い方の解説は「Node.js で Cloud Spanner を使ってみる」にあります。
@google-cloud/spanner のリポジトリに含まれている、サンプルのコードも参考になると思います。

また、@google-cloud/spanner の各メソッドに渡すパラメータや、オプションなどの詳細についての説明は Client API Reference にありあます。

データの取得

Cloud Spanner でデータを取得する方法は 2 つあります。
ひとつは SQL を使って取得する方法、もうひとつは、トランザクションやテーブルのクラスがもつ read というメソッドを呼び出す方法です。

SQL を使ってデータを取得するメリットは、テーブルの結合や、条件を指定しての抽出が可能なことです。
Table.read() メソッドを使う場合は、単一のテーブルからキーを指定して値を取得する KVS のような使い方になります。

SQL を使ってデータを取得する

次のようなコードで、SQL を使ってデータを取得できます。
サンプルのコードでは使っていませんが GROUP BY、ORDER BY などのクエリも、もちろん利用可能です。

const Spanner = require("@google-cloud/spanner");
const client = new Spanner({
    projectId: "PROJECT ID"
});
const instance = client.instance("INSTANCE ID");
const database = instance.database("DATABASE ID");

(async () => {
    const query = {
        sql: "SELECT * FROM members LEFT JOIN profiles ON members.member_id = profiles.member_id WHERE members.member_id = @member_id",
        params: {
            member_id: "MEMBER_ID"
        },
        json: true
    };

    const result = await database.run(query);
})();
Table.read() を使ってデータを取得する

Table.read() を使用してデータを取得する場合、すべてのカラムを取得したいときには columns に、すべてのカラム名を指定しないといけないことに注意してください。

「*」が利用できないため、スキーマが変更された場合、合わせて修正する必要もあります。

const Spanner = require("@google-cloud/spanner");
const client = new Spanner({
    projectId: "PROJECT ID"
});
const instance = client.instance("INSTANCE ID");
const database = instance.database("DATABASE ID");

(async () => {
    const member_table = database.table("profiles");

    const result = await member_table.read({
        keys: ["MEMBER_ID"],
        columns: [
            "member_id",
            "name",
            "kana",
            "age",
            "birthday",
            "units"
        ],
        json: true
    });
})();
UNNEST と CROSS JOIN を使用した配列の展開

profiles テーブルでは Array 型の units カラムに、複数の unit_id が保存されています。
このとき、プロフィールにユニット名を表示するために profiles テーブルと units テーブルを結合することを考えます。

NoSQL でやるのと同じく、別々に別取得してプログラムで結合するという方法も可能ですが、SQL を使って取得することもできます。
SQL を使って取得する場合は、次のように UNNEST と CROSS JOIN を使用します。

SELECT * FROM profiles CROSS JOIN UNNEST(units) AS unit LEFT JOIN units ON unit = unit_id;

データの登録

データの登録は SQL では行えないため、Table.insert() メソッドを利用します。
引数には登録したいデータを単体で渡すことも可能ですが、配列を渡すことで複数のデータを一度に登録することも可能です。

const Spanner = require("@google-cloud/spanner");
const client = new Spanner({
    projectId: "PROJECT ID"
});
const instance = client.instance("INSTANCE ID");
const database = instance.database("DATABASE ID");

(async () => {
    const unit_table = database.table("units");
    const data = {  unit_id: "UNIT_ID",
        name: "NAME"
    };

    await unit_table.insert([data]);
})();

データの更新

基本的にはデータの登録と変わりません。
データの更新には Table.insert() ではなく、Table.update() というメソッドを使います。
このメソッドにも配列でデータを渡すことができるため、複数の値を同時に更新することが可能です。

また、Table.update() メソッドに渡すデータには、テーブルのすべてのカラムの情報を含める必要はありません。
データに含まれていないカラムについては、削除されるのではなく、現在の情報が保持されます。

なお、Table.update() は同じ PRIMARY KEY をもつデータが上書きされ、データがない場合はエラーとなります。
データがないときには新規にデータを登録したいという場合には Table.upsert() が利用できます。

const Spanner = require("@google-cloud/spanner");
const client = new Spanner({
    projectId: "PROJECT ID"
});
const instance = client.instance("INSTANCE ID");
const database = instance.database("DATABASE ID");

(async () => {
    const unit_table = database.table("units");
    const data = {
        unit_id: "UNIT_ID",
        name: "NAME"
    };

    await unit_table.update([data]);
})();

データの削除

データの削除も SQL からは行えないため Table.deleteRows() メソッドを利用します。
テーブル自体を削除する Table.delete() というメソッドが存在することに注意してください。

名前のとおり、複数の情報を一度に削除することも可能です。
その場合は、削除したいデータの PRIMARY KEY を配列で渡します。

const Spanner = require("@google-cloud/spanner");
const client = new Spanner({
    projectId: "PROJECT ID"
});
const instance = client.instance("INSTANCE ID");
const database = instance.database("DATABASE ID");

(async () => {
    const unit_table = database.table("units");

    await unit_table.deleteRows(["UNIT_ID"]);
})();

トランザクション

Cloud Spanner には、読み込み専用のトランザクションと、読み書き可能なロック型のトランザクションの 2 種類がなります。
これらの違いや、トランザクションにについての解説は、公式のドキュメント にありますので、ここでは省略します。

しかし、注意点として次のような内容が挙げられているため、内容確認した上で利用することをお勧めします。

注: 読み取り専用トランザクションはどのレプリカでも実行できるため、可能であれば、トランザクションのすべての読み取りを読み取り専用トランザクションで実行することをおすすめします。
ロック型読み書きトランザクションは、次のセクションで説明するシナリオでのみ使用してください。

今回は、シナリオの 1 つである「1 つ以上の読み取りの結果に応じて 1 つ以上の書き込みを行う可能性がある場合」を想定して、ロック型読み書きトランザクションを利用してみました。

const Spanner = require("@google-cloud/spanner");
const client = new Spanner({
    projectId: "PROJECT ID"
});
const instance = client.instance("INSTANCE ID");
const database = instance.database("DATABASE ID");

(async () => {
    await new Promise((resolve) => {
        database.runTransaction(async (error: Error, transaction: any) => {
            const data = await transaction.read("profiles", {
                keys: ["UNIT_ID"],
                columns: [
                    "member_id",
                    "name",
                    "kana",
                    "age",
                    "birthday",
                    "units"
                ],
                json: true
            });

            data.age = data.age + 1;
            await transaction.update("profiles", data);
            await transaction.commit();

            resolve();
        });
    });
})();

もちろん、問題があったときには、ロールバックが可能です。

const Spanner = require("@google-cloud/spanner");
const client = new Spanner({
    projectId: "PROJECT ID"
});
const instance = client.instance("INSTANCE ID");
const database = instance.database("DATABASE ID");

database.runTransaction(async (error: Error, transaction: any) => {
    await transaction.rollback();
});

まとめ

今回、はじめて Cloud Spanner を使いましたが、抑える部分だけ抑えておけば他のデータベースと同じ様に利用できそうだなと思いました。
MySQL などの RDBMS と O/R マッパを使ってシステムを構築したことがあれば、導入の敷居は低そうです。
ただ、プライマリーキーに連番が使えなかったりするため、既存のデータベースを Cloud Spanner に移行するのは、少し大変かもしれません。

また、導入を検討する際に料金の高さが問題になるかもしれません。
Cloud Spanner を東京リージョン(asia-northeast1)で使う場合、2018 年 07 月 05 日の時点で、1 ノード毎に 1 時間あたり 1.17 USD かかります。 
ストレージの使用料や、ネットワークの使用料を別にしても、1 ノードで 1.17 x 24 x 30 = 842.4 USD になります。

そのため、どんな案件にでもお勧めできるというわけではなさそうです。
Cloud Spanner の提供するスケーラビリティと、強い整合性の両立という部分が必要ないのであれば、他の選択肢も検討した方がいいのかもしれません。


これまでのARはデバイス間でAR上のオブジェクトを共有することができませんでしたが、Cloud AnchorはそれをCloudにAnchor情報を保持しデバイス間で共有することが目的の機能となります。

APIキーの準備

Cloud Anchorを使用する場合、クラウドを通したデータ共有が行われるので、Google Cloud Platformへのアクセスが発生します。そのため、APIキーを取得する必要があります。
APIキーの取得方法は以下のようになります(Googleアカウントを保持していることが前提となります)。
  1. Google Cloud PlatformのConsoleにログインし、使用する任意のプロジェクトのダッシュボードを開く(または作成)
  2. ダッシュボードの左側あたりに「APIを有効化し、鍵などの認証情報を取得」を選択

  3. 「APIとサービス」の「APIとサービスの有効化」を選択

  4. 検索ボックスが表示されるので、「ARCore」と入力。入力すると「ARCore Cloud Anchor API」が絞り込まれるので、それを選択

  5. 「有効にする」を選択(既に有効になっている場合、「管理」が表示されます)

  6. 有効にすると「APIとサービス」画面のダッシュボードに遷移するので、画面左側の「認証情報」を選択

  7. 「認証情報を作成」を選択して、ドロップダウンメニューの「APIキー」を選択

  8. これでAPIキーの作成は完了しますが、他のアプリでも使用できる状態なので、パッケージ名と SHA-1 署名証明書フィンガープリントを設定したり、使用できるAPIを設定したりして、使用制限を行うのがよいでしょう

    • 使用できるアプリを制限

    • 使用できるAPIを制限

UnityプロジェクトへAPIキーの設定

UnityプロジェクトへAPIキーを設定する方法ですが、ARCoreのDeveloperサイトには以下のように記載されています。
「https://developers.google.com/ar/develop/unity/cloud-anchors/cloud-anchors-quickstart-unity-android?hl=ja#add_an_api_key」より抜粋

ということで「Unity 2017.4.5f1(64bit)」+「ARCore SDK for Unity Version 1.2.1」で上記の「Edit」-「Project Settings」-「ARCore」を選択してみると、以下のようなメニューになりました。


どうも正しい場合は以下のように表示されるようです。
「http://jyuko49.hatenablog.com/entry/2018/06/06/092513#Unityのセットアップを行う」より抜粋


このような場合は、ちょっと面倒ですが、AndroidManifestをPlugins/Android配下にアップして、それに直接APIキーを記載しましょう。
詳細はこちらを参照してください。

実装

Cloud Anchorを有効にする

  1. Session Configを作成して、"Cloud Anchor"を有効にする


  2. 作成・設定したSession Configを使用するARCore Deviceに設定

Cloud Anchorを有効にする実装は以上となります。
ただし、Cloud Anchorの機能としては、CloudからCloud Anchorの一覧を取得する方法は提供されていません。
なので、Cloud Anchor IDをやり取りする機構を追加しないとCloud Anchorを共有することは出来ません。また、Cloud AnchorのTransformを変更しても、それを参照している別のデバイス上のCloud Anchorには影響を与えることはできません。
そのため、マルチデバイスで同じものを共有するには不十分な機能(※)に見えます。
※ 別途Cloud Anchort IDを共有するためのサーバやAnchorに紐づくGameObjectの情報を共有するためのサーバを追加する必要がありそうです。

サンプルアプリ

お試しで作成したアプリを以下の場所にアップしておきました。
https://github.com/eq-inc/eq-unity-google-arcore/tree/v0.4.1
クローンした上で、以下のことを実行してください。
  1. 「git submodule update -i」でサブモジュールを取得
  2. ここで「ARCore SDK for Unity」(実装時はv1.2.1)をダウンロード
  3. クローンした環境をUnityで開き、上記の「ARCore SDK for Unity」をインポート
  4. 上記のようにCloud Anchor用のAPIキーをGoogle Cloud Consoleにて生成し、「Assets/Plugins/Android/AndroidManifest.xml」内の「YOUR_API_KEY」を取得したAPIキーで置き換える
  5. ビルドして、起動した後、ボタン「Cloud Anchor」を押下
  6. 任意の箇所をタッチして、Android Robotを表示させる
  7. 暫くしてから、「List Cloud Anchor」を選択して、表示されるCloud Anchor IDを取得
  8. 別の端末で同アプリを起動し、ボタン「Cloud Anchor」を押下
  9. 「Add Cloud Anchor」を選択し、テキスト入力欄に先のCloud Anchor IDを入力
「Augmented Image」は先に登録しておいた画像が実世界上に現れたときに、そこにアプリで作成した画像を上書きする技術のことです。他のAR SDKでは「マーカー型」と呼ばれているものになります。
これまでの「マーカー型」はサービスとして提供されているために、サービス提供元のサーバにアクセスできないと動作しませんでした。
それに対して、「Augmented Image」はアプリ内に画像DBを保持(※)し、画像認識自体もデバイス内部(恐らくARCoreアプリ内)で実施するので、通信ができない環境でも動作させることが可能となります。
※ 画像DBはアプリ起動時に動的に組み立てることも可能なようです。
認識できる画像としては以下のような制約があります(ARCore SDK for Unity: Version 1.2.1現在)。
  • 15cm × 15cm以上の画像であること
  • 画像は平面状になっていること(しわがあったり、ボトルに巻いてあったりしてはいなけない)

実装手順

画像DBを固定で保持する実装について記載します。

画像DBの作成と登録

  1. Unityプロジェクトを作成し、ARCoreが利用できる状態に設定(詳細はこちら)
  2. 作成したUnityプロジェクトにターゲットにしたい画像ファイルを格納
  3. 追加した画像ファイルを全て選択し、右クリックから「Create」-「GoogleARCore」-「AugmentedImageDatabase」を選択し、画像DBを構築

  4. UnityプロジェクトにSessionConfigを追加

  5. 追加したSessionConfigの「Augmented Image Database」に先ほど作成した画像DBを設定

  6. ARCore Deviceの「AR Core Session」-「Session Config」に先ほど作成したSessionConfigを設定

画像DBへのアクセス

登録した画像DBへアクセスするコードは以下のようになります。
// 以下のAPIで認識したターゲット画像の一覧を取得(引数のListに結果が格納される)
Session.GetTrackables(mAugImageList);
取得した認識しているターゲット画像から位置などの情報を取得するためにAnchorを作成します。
mTargetImageAnchor = mTargetImage.CreateAnchor(mTargetImage.CenterPose);
作成したAnchorはComponentなので、不要になったらDestroyしてあげましょう。

サンプルアプリ

お試しで作成したアプリを以下の場所にアップしておきました。
https://github.com/eq-inc/eq-unity-google-arcore/tree/v0.4.1【2018.06.26修正】
クローンした上で、以下のことを実行してください。
  1. 「git submodule update -i」でサブモジュールを取得
  2. ここで「ARCore SDK for Unity」(実装時はv1.2.1)をダウンロード
  3. クローンした環境をUnityで開き、上記の「ARCore SDK for Unity」をインポート
  4. ビルドして、起動した後、ボタン「Augmented Image」を押下
  5. クローンした環境の以下の画像に反応します。【2018.06.26追記】
  6. 画像は全て「かわいいフリー素材集 いらすとや」様にて公開されているもので、著作権は「かわいいフリー素材集 いらすとや」様が所有されています。
    • Assets/Images/character_cthulhu_shoggoth.png
    • Assets/Images/fantasy_chikyu_heimen.png
    • Assets/Images/fantasy_wolpertinger.png
Google Tangoのとき、ここで記載した内容です。
iOSでもメジャーが標準でインストールされたことを機に思い出したので、ARCoreでも出来るのか確認してみます。

リファレンスの記述

リファレンスの記述が最も信頼出来る(ことになっている)ので、確認したところ、Unity向けのリファレンスにおいては、 GoogleARCore.AugmentedImage - ExtentXに記載されている内容ぐらいしか見つからなかったです。
Gets the estimated width, in meters, of the corresponding physical image, as measured along the local X-axis (point from image left to image right) of the coordinate space centered on the image. 

実際のところ

ドキュメントの記述が見つけられなかったので、実際に試してみました。
  1. ここからテストアプリ用のコードをクローン
  2. 同梱されている「README.md」に従って、「ARCore SDK for Unity」をダウンロードしインストール
  3. 実行すると以下のような画面が表示されるので、「Measure」を選択

  4. 画面上で任意の箇所をタッチすると、ARCoreが認識出来ている場合、〇が表示されるので、そこから測定したい箇所をタッチ
  5. ※ 左下のPoint数が多い状態でタッチした方がARCoreが認識出来ている箇所が多いので成功し易いです。
実際に試した感じだと、センチメートル単位で近い値が表示されていたので、Google Tangoと同じようにUnity内のWorld座標をそのままメートルとして扱って問題ないようです。

サンプルアプリ

お試しで作成したアプリを以下の場所にアップしておきました。
https://github.com/eq-inc/eq-unity-google-arcore/tree/v0.2.0
クローンした上で、以下のことを実行してください。
  1. 「git submodule update -i」でサブモジュールを取得
  2. ここで「ARCore SDK for Unity」(実装時はv1.2.1)をダウンロード
  3. クローンした環境をUnityで開き、上記の「ARCore SDK for Unity」をインポート
  4. ビルドして、起動した後、ボタン「Measure」を押下
今回はInstant Previewについてです。
Google VRには既に存在していたのですが、それがARCoreにも技術展開されたようです。

Instant Previewとは?

これまでは、実装したコードを動作確認するには、APKを作成してデバイスにインストールする一連の処理が必要でした。そして、その一連の処理が結構時間が掛かり試行錯誤する場合、この時間がとてもモヤモヤした時間でした。その時間を短縮するためにアプリ側で実装したスクリプト(SDK for Unity内のコードも含む)をPC側で実行して、デバイス上のInstant Previewを経由して実際のVR/AR処理を行うアプリ(VR: Google VRサービス、AR: ARCore)と通信を行い実行しているようです。
これによりAPKの作成とインストールがなくなるため、動作確認が早くなります。
ただし、Google VRとARCoreでInstant Previewを使用したときの見え方が異なります。
Google VR: デバイス上に表示される
ARCore: Unity Editorとデバイスの両方に表示される(※)
※ WindowsではオンボードのGPUではUnity Editorにしか表示されないようです(出典)。そして私のPCはNvidia GeForce GTX 960Mも入っていますが、それを使用するように変更してもデバイス側には表示されませんでした。。。

便利な機能ですが、一部制限があります。
  • ActivityのResume / Suspendイベントはサポートされていない
  • デバイス上でARCore Instant PreviewアプリをSuspendさせると、それからの出力が止まるために、Unity Editor上の表示は固まります。
  • ARCoreコンフィギュレーションの変更はサポートされていない
  • タッチイベントが一部制限される
  • 長押下やUI部品(※)へのタッチはできません。
    ※ UI部品へのタッチはUnityEditor上でマウスクリックすることは出来ます。

    また、UnityEditorの画面が端末の画面サイズと一致しないと座標が意図していない値になるようです。その場合、UnityEditorのサイズを変更すると改善されます。
    以下の部分を選択すると、プリセットされている画面サイズの下に「+」メニューがあるので、それを選択

    「+」を選択すると、以下のダイアログが表示されるので、そこに名称と画面幅・高さを設定して追加(ZenfoneARの場合、2560x1440に設定しました)。
    最初はUnityEditorのスケールが「1x」になっていると思いますので、全体が表示されるように変更すると見やすくなります。
  • 画面方向
  • Instant Previewはスクリプト自体はUnity Editor上で動作しているので、画面回転をハンドリングすることが出来ません。そのためにサンプルアプリ「ComputerVision」では常に画面縦方向(Portrait)として扱っています。そのため、Instant Previewで動作させると以下のように表示されます。


    ただ、これだと動作確認できないので、「Unity Editorの表示領域を変更すればいいよ」とGoogleから提案されています。実際にUnity Editorの表示領域をPortraitなものに変更すると以下のように表示されます。
詳細はこちらを参照してみてください。

設定方法

設定方法を一応記載しますが、ほとんどすることは無いです。
  1. ここに記載されているように、Unityプロジェクトを生成
  2. Instant Preview使用時にタッチイベントを有効にする場合、以下のコードを追加
  3. #if UNITY_EDITOR
    using EditorInput = UnityEngine.Input;
    #endif
    
これで、動作させることが可能になります。
備忘録を兼ねて、Google ARCoreを実行させるための基本的な設定について纏めてみたいと思います。
Unityのインストールについては割愛しますが、ここに記載されているバージョンを満たすものをインストールしましょう。

使用した環境

本設定時に使用した環境は以下のようになります。
ソフトウェアバージョン
Unity2017.4.0f1(Personal)
ARCore SDK for Unityv1.1.0
デバイスZenfone AR
OSAndroid 7.0
ちなみに使用したZenfone ARはドライバに不具合があるらしく、端末を起動してから26時間ぐらい経過するとTrackingが開始できなくなります。これが発生した場合、端末を再起動することで直ります(詳細はGithubのissue #183#286を参照してみてください)。

設定手順

  1. 環境構築
  2. まずはUnityプロジェクトの環境構築を実施します。詳細はこちらを参照してください。
  3. "Main Camera"を削除
  4. Google Tangoのときと同じように、まずはUnityプロジェクトでデフォルトで設定されている"Main Camera"を削除します。
  5. プレハブ"ARCore Device"をHierarchyに追加

  6. ここまでで、ARとしての最低限の機能として、以下のことができるようになります。
    • 背景画像がカメラからの入力になる
    • デバイスのpose(position and orientation)が取得できる(Frame.Pose)
    • ARCoreが認識したそれぞれの座標を取得できる(Frame.PointCloud)
    • 平面の認識(Session.GetTrackables())
Google Tangoと違って、この段階で大抵のことができるようになっています。
付加機能として、「Environmental Light」を動作できるようにしたい場合、Prefab"Environmetal Light"を追加することで可能です。

機能の無効化

先の通り、ARCoreDeviceを追加することで大抵の機能が使用できるようになりますが、逆に使用したくないので無効化したい場合は、以下のようにGameObject「ARCore Device」のコンポーネント「AR Core Session」のプロパティ「Session Config」から変更が可能です。

以前、本ブログで掲載していた「UnityでGoogle Tangoと戯れる」シリーズを投稿後、GoogleがARCoreの開発とGoogle Tangoの終了を発表して以来、AR関連の記事がありませんでした。
先日ようやくARCoreが正式リリースされ、それを機にGoogle Tangoの遺産(?)であるZenfone AR(※)もサポート機種になったので、ARCoreについて調べていきたいと思います。
※ Zenfone ARはGoogle Tangoの調査のために購入したものだったので、無駄にならずにすんで助かりましたw

ARCoreでやれること

まずはGoogle TangoとARCoreでやれることに違いがあるのか確認してみたいと思います。
機能名称Google TangoARCore備考
Motion Tracking-
Environmental understanding
Light estimation×-
Area LearningARCoreではArea Learningとしては紹介されていないが、それで出来ることと同等のことがEnvironment understandingで可能
Depth PerceptionARCoreではDepth Perceptionとしては紹介されていないが、それで出来ることと同等のことがEnvironment understandingで可能
Visual Positioning Service××?Google Tangoのときに記載されていた機能だけど、当時からサンプルアプリが存在しなく動作していたか未確認

環境構築

ARCoreを開発するための必須環境は2018.04.06現在以下のようになっています。
  • Unity 2017.3.0f2以降
  • Androidデバイス: Zenfone ARなど。詳細はこちらを参照

Unityのインストール

こちらからインストーラをダウンロードして、インストールします。
インストールの仕方はこちらを参照してください。

ARCore SDK for Unityのダウンロード

ARCore SDK for UnityはGitHubにて公開されています。なので、こちらからダウンロードします。

Unityの設定を変更

ARCore SDK for UnityをUnityプロジェクトにインストールしたいところですが、まずはUnityの設定を変更する必要があるので、それから紹介していきたいと思います。
  1. Build Settingsを開く
  2. メニュー「File」 - 「Build Settings」よりBuild Settingsを開く。
  3. ビルド対象をAndroidに変更
  4. 下図のようにプラットフォーム一覧からAndroidを選択し、画面下部の「Switch Platform」を押下して、ビルド対象をAndroidに切り替える。
    その上でビルド設定の詳細を変更するために、「Player Settings」を押下する。
  5. Other Settingsの変更
  6. 画面右側の「Inspector」欄に「PlayerSettings」が表示されるので、その中の「Other Settings」内の以下の項目を変更する。
    設定名称変更前変更後
    Multithreaded RenderingチェックONチェックOFF
    Package Namecom.Company.ProductName任意の名称
    Minimum API LevelAPI Level 16API Level 24以上
    Target API LevelAutomaticAPI Level 24以上


  7. XR Settings
  8. 「ARCore Supported」を有効(チェックON)にする。

ARCore SDK for UnityをUnityプロジェクトにインストール

次に設定が済んだUnityプロジェクトにARCoreSDK for Unityを組み込みます。
組み込みかたは、シンプルで画面下部の「Project」欄の「Assets」に大して、右クリックすると表示されるコンテキストメニューから「Import Package」 - 「Custom Package」を選択し、UnityPackageを選択するダイアログが表示されるので、先にダウンロードしておいた「ARCore SDK for Unity」向けUnityPackageを選択。

選択すると、UnityPackage内のファイルの内、どのファイルをインポートするか確認されるので、全てのファイルを選択してインストールする。

これで準備自体は完了です。
次回から、機能毎に解説していきますが、待ちきれない方はARCore SDK for UnityをインポートするとAssets/GoogleARCore/Examples配下にサンプルプロジェクトが2つ(ComputerVision、HelloAR)あるので、それぞれのフォルダのSceneを読み込みことで動作を見ることが可能です。
下の画像はComputerVisionを動作させた際のスクリーンショットになります。物体を認識することで境界線を描画しているようです。

参考サイト

最近Google VRを触っていなかったので、久しぶりに更新したら、VR空間での音の聞こえ方について、これまでの「Spatial Audio」から「Resonance Audio」への切り替えが始まっていたので、「Resonance Audio」について調べてみました。

Resonance Audioとは

前身の「Spatial Audio」と基本的には同じで、よりパフォーマンスとマルチプラットフォームに最適化された空間オーディオ(Spatial Audio)とされています。

使用した環境

使用した環境は以下のようになります。
名称バージョン
Unity2017.3.0f3 - 64bit
Google VR SDK for Unityv1.120.0
Resonance Audio SDK for Unityv1.1.1

組み込み方

Spatial Audioのときは、Google VR SDK for Unityをインポートすれば、機能はインポートされて設定を変更すれば使用できましたが、Resonance Audioは未だ(※)Google VR SDK for Unityには組み込まれていないので、別途パッケージをインポートした上で設定を変更する必要があります。
※ 2018.02.05現在

  1. Resonance Audio向けのUnity Packageをダウンロード・インポート
  2. こちらから最新の「Resonance Audio SDK for Unity」(執筆時はv1.1.1)をダウンロードします。
    Unity Packageなので、Unityプロジェクト毎にインポートする必要があるので、インポートしたいUnityプロジェクトを開き、以下のようにダウンロードしたUnity Packageをインポートします。

  3. オーディオプラグインをResonance Audioに変更
  4. Edit - Project Settings - Audioから以下のプラグインを「Resonance Audio」に変更します。

    • Spatializer Plugin
    • Ambisonic Decoder Plugin


  5. Google VR SDK for Unityに元から含まれるオーディオライブラリを削除
  6. 以下のファイルが不要になるので、Unityプロジェクトから削除します。ただし、以下のファイルは「Google VR SDK for Unity v1.20.0」と「Resonance Audio SDK for Unity v1.1.1」の組み合わせ向けなので、最新の情報はこちらから確認した方が良いです。
    プロジェクト内パス概要備考
    Assets/GoogleVR/Plugins/Android/libs/armeabi-v7a/libaudioplugingvrunity.soオーディオ用のライブラリ-
    Assets/GoogleVR/Plugins/Android/libs/x86/libaudioplugingvrunity.soオーディオ用のライブラリ-
    Assets/GoogleVR/Plugins/iOS/libaudioplugingvrunity.aオーディオ用のライブラリ
    Assets/GoogleVR/Plugins/x86/audioplugingvrunity.dllオーディオ用のライブラリ-
    Assets/GoogleVR/Plugins/x86_64/audioplugingvrunity.bundleオーディオ用のライブラリ-
    Assets/GoogleVR/Plugins/x86_64/audioplugingvrunity.dllオーディオ用のライブラリ-
    Assets/GoogleVR/Plugins/x86_64/libaudioplugingvrunity.soオーディオ用のライブラリ-
    Assets/GoogleVR/Plugins/iOS/GvrAudioAppController.hObjective-C向けのasset-
    Assets/GoogleVR/Plugins/iOS/GvrAudioAppController.mmObjective-C向けのasset-
    Assets/GoogleVR/Legacy/Resources/GvrAudioMixer.mixeraudio mixer asset-
    Assets/GoogleVR/Legacy/prefabs/Audio/GvrAudioRoom.prefabオーディオ用のprefab削除しなくても可
    Assets/GoogleVR/Legacy/prefabs/Audio/GvrAudioSoundfield.prefabオーディオ用のprefab削除しなくても可
    Assets/GoogleVR/Legacy/prefabs/Audio/GvrAudioSource.prefabオーディオ用のprefab削除しなくても可
    Assets/GoogleVR/Legacy/Editor/Audio/GvrAudioListenerEditor.csオーディオ用のscript削除しなくても可
    Assets/GoogleVR/Legacy/Editor/Audio/GvrAudioRoomEditor.csオーディオ用のscript削除しなくても可
    Assets/GoogleVR/Legacy/Editor/Audio/GvrAudioSoundfieldEditor.csオーディオ用のscript削除しなくても可
    Assets/GoogleVR/Legacy/Editor/Audio/GvrAudioSourceEditor.csオーディオ用のscript削除しなくても可
    Assets/GoogleVR/Legacy/Scripts/Audio/GvrAudio.csオーディオ用のscript削除しなくても可
    Assets/GoogleVR/Legacy/Scripts/Audio/GvrAudioListener.csオーディオ用のscript削除しなくても可
    Assets/GoogleVR/Legacy/Scripts/Audio/GvrAudioRoom.csオーディオ用のscript削除しなくても可
    Assets/GoogleVR/Legacy/Scripts/Audio/GvrAudioSoundfield.csオーディオ用のscript削除しなくても可
    Assets/GoogleVR/Legacy/Scripts/Audio/GvrAudioSource.csオーディオ用のscript削除しなくても可

  7. 既にSpatial Audioを使用しているUnityプロジェクトの場合、オーディオ系GameObject、Componentを差し替えます。
  8. 差し替え前差し替え後備考
    GvrAudioListenerResonanceAudioListener-
    GvrAudioSourceResonanceAudioSource-
    GvrAudioSoundfield-GvrAudioSoundfieldはResonance AudioではResonanceAudioSourceにて対応される
    GvrAudioRoomResonanceAudioRoom-

  9. AudioSourceとResonanceAudioSourceの結びつけ
  10. ResonanceAudioSourceを任意のGameObjectにアタッチすると、自動的にAudioSourceもアタッチされます。
    ただし、このままではAudioSourceから再生されるだけで、Resonance Audioによるサウンド効果は得られません。AudioSourceの「Output」をMaster(Resonance Audio)に変更する必要があります。

  11. Spatial Blendの変更
  12. AudioSourceの「Spatial Blend」はデフォルトで2Dになっているので、立体音響のために3Dに変更する。

  13. 音源のSpatialize、Spatialize Post Effectsの有効化
  14. Developer Guide for Resonance Audio for Unity」にはAudioSourceの該当パラメータを有効にすることが記載されていますが、これにより微妙にトラブルに見舞われました。
    事象としては、PrefabにResonanceAudioSourceをアタッチして自動的にアタッチされるAudioSourceの「Spatialize」を有効にすると再生してもその音源から音が聞こえないといった事象になります。
    PrefabにアタッチされているAudioSourceの「Spatialize」はデフォルト無効にしておき、スクリプト上で、AudioSource.Play実行後にAudioSource.spatialize、AudioSource.spatializePostEffectsを有効にすることで回避できました。同じような事象が発生したら試してみてください。
    ※ シーン上に初めからインスタンス化されているGameObjectに設定しているResonanceAudioSourceでは本事象は発生しませんでした。

サンプルアプリ

ここにサンプルアプリを置きました。 試してみる場合は、以下の手順で確認してみてください。
  1. ここから環境をクローン
  2. サブモジュールが2つ組み込まれているので、それぞれを取り込む
  3. ブランチをタグ"v0.0.6"に切り替える
  4. Unityでクローンした環境を開く
  5. "Google VR SDK for Unity"、"Resonace Audio SDK for Unity"用のパッケージをimport
  6. ここを参考にして不要なファイルを削除
  7. 環境をビルドし、アプリをインストール
  8. 起動するとメニューが表示されるので「Resonance Audio」を選択(大分選択し辛いです。この辺についても調査予定)
  9. タッチパッドをクリックすると灰色の直方体が表示され、そこから猫の鳴き声が聞こえます(直方体をクリックすると直方体が消えて、鳴き声も聞こえなくなります)
前回、Smoothie-3D 2.0の基本的な使い方を記載してみましたが、今回はAndroid Robot画像で3Dモデリングを行ってみます。

Android Robotを3D化

Android Robotの画像を使用して3Dしてみたいと思います。
※ 以下のAndroid Robotを3D化しているときに、一気に作成して保存しようとしたらセッション切れが発生して保存に失敗しました。こまめに保存しましょう・・・orz

頭(頭頂部のアンテナのような2本の突起物は除く)を3D化してみます。
3D化の操作方針としては以下のようになります。
3Dオブジェクトフリーハンドオブジェクト
フリーハンドオブジェクトの奥行指定 頭の中心を軸とした回転


アンテナ

アンテナは左右対称なので「sym」メニュー - 「x」を使用します。
そのため、3D化の操作方針としては以下のようになります。
3Dオブジェクトフリーハンドオブジェクト
フリーハンドオブジェクトの奥行指定


このままだと、アンテナの奥行が長すぎるので奥行を縮めます。
下の図のようにを有効にした上で、同ボタンが表示されている灰色の領域でマウスの左ボタンを押下しながらカーソルを動かすことで傾きを変えられます。傾きを変えた上で、アンテナを選択し幅を狭めます。

腕・足

腕・足は殆ど構成が一緒になります。
腕は定形オブジェクトを上から球体・円柱・球体の順に並べて構成します(足は円柱・球体)。
また左右が一緒の構成になるので、全ての定形オブジェクトに大して「sym」 - 「x」を指定すると一気に作成することができます。
3D化の操作方針としては以下のようになります。
3Dオブジェクト 定形オブジェクトの組回せ
腕:球体・円柱・球体
足:円柱・球体

体ですが、定形オブジェクトの円柱でやりたいところだったのですが、正しくは円柱ではない(足の付け根辺りが丸みを帯びている)ので、 3D化の操作方針としては以下のようにしました。
3Dオブジェクトフリーハンドオブジェクト
フリーハンドオブジェクトの奥行指定 体の中心を軸とした回転

結果

結果として以下のようになりました。

作成したものを公開できるように指定できるので、こちらに公開しておきました。Smoothie-3D 2.0のアカウントを取得することで見ることが出来るようになります。
御覧になると気付くかと思いますが、Android Robotの後頭部にもが存在しています。この作り方ではどうしても目が後頭部にもついてしまいます。手っ取り早い方法(?)はAndroid Robotの横からの画像を使って同じようにやると、うまくいくと思います。