【技術検証】ExpoとViroで始めるAR開発
はじめに
今回、Expoを使ったAR開発の技術検証を行いました。
Expo環境でARを実装する方法のひとつとして、Viro というライブラリを使用したので紹介します。
例えば、以下のようなAR機能を実装できます。
本記事ではライブラリの紹介と、技術検証の結果について触れています。
背景
現在、プロジェクトでは「ARを使った動画を投稿するSNS」のような「AR×SNS」を融合したアプリケーションを開発中です。
この開発にあたり、AR機能とSNS機能の両方をスムーズに実装できる技術スタックを選定する必要がありました。候補として挙がったのは、以下のアプローチです。
- ネイティブ開発(ARKit / ARCore)
- Unity
- React Native(Viro)
技術 | 特徴 |
---|---|
ネイティブ開発(ARKit / ARCore) | iOSならARKit、AndroidならARCoreを活用し、各プラットフォームのネイティブなAR機能を最大限に活かせる。 |
Unity | 高度な3Dレンダリングや物理エンジンを活用し、クロスプラットフォームでリッチなAR表現が可能。 |
React Native(Viro) | React Native環境でARKit / ARCoreをラップし、JS/TSで手軽にAR開発ができる。 |
ARの精度を最重視する場合は「ネイティブ開発」や「Unity」が有力でしたが、SNS機能との統合や開発スピードを重視する場合は「React Native(Viro)」が適していました。
今回の「AR × SNS」アプリでは、開発のしやすさとクロスプラットフォーム対応を最優先し、React Native(Viro)でのAR機能の技術検証を行いました。
Viroとは
Viroの特徴
Viroは、React NativeでARを簡単に実装できるライブラリです。
内部的には、iOSではARKit、AndroidではARCoreが動作しており、ネイティブのAR機能をReact Nativeのコードで扱えるようになっています。
Viroの歴史・サポート状況
ViroReact was originaly developed by the Viro Media company, but was open sourced in 2019. In late 2020 the Viro Community was formed to help maintain and move the project onwards, updating it so it could run with modern versions of react native, and start to add in new features.
ViroはもともとViro Media社によって開発され、2019年にオープンソース化されました。
その後、2020年以降はViroコミュニティがメンテナンスを継続し、最新のReact Nativeバージョンに対応するための更新が進められています。
様々なAR
以下のようなARの実装が可能です。
- テキスト・画像・3Dオブジェクト表示
- 画像認識
- アニメーション
そのほかにも様々なARのサンプルが用意されています。
Expoで始める
ViroをExpo環境で使うために、expo-starter-kit-typescript というスターターキットが用意されています。
ExpoでViroを利用するには、app.json に以下の設定を追加します。
{
"plugins": [
[
"@reactvision/react-viro"
]
]
}
Viroの基本実装
ViroでARを実装するにあたって重要な2つのコンポーネントがあります。
- ①ViroARScene(AR空間を定義するコンポーネント)
- ②ViroARSceneNavigator(シーンを管理・遷移するコンポーネント)
①ViroARScene(AR空間を定義するコンポーネント)
Viroでは、カメラを通して表示されるAR体験エリアを「シーン(AR空間)」と呼びます。
シーンを作成するには、ViroARScene を使い、その中にARコンポーネント(ViroText, ViroImage, Viro3DObject など)を配置します。
const MyScene = () => {
return (
<ViroARScene>
<ViroText/>
<ViroImage/>
<Viro3DObject/>
<ViroBox/>
</ViroARScene>
);
};
このViroARSceneを使って、テキスト・画像・3Dオブジェクトなどを組み合わせ、自分だけのARを作ることができます。
②ViroARSceneNavigator(シーンを管理・遷移するコンポーネント)
ViroARSceneNavigator は、作成したARシーン(ViroARScene)をアプリの画面上に表示し、複数のシーンを管理・切り替えるためのコンポーネントです。
const MyScene = () => {
return (
<ViroARScene>
<ViroText/>
</ViroARScene>
);
};
const App = () => {
return (
<ViroARSceneNavigator
initialScene={{
scene: MyScene, // 作成したシーンを登録
}}
/>
);
};
initialSceneに、作成したシーン(例: MyScene)を登録し、React NativeのUI上に配置することで、画面にシーンを表示することができます。
アプリ内で複数のARシーンを切り替えたり、シーンごとに異なる体験を提供したりすることができます。
様々なARの実装
3DオブジェクトAR
3Dオブジェクトを表示させたり、ユーザー操作によって移動、回転、拡大縮小させる方法です。
1.3Dオブジェクトを用意
まず、ARで表示する3Dオブジェクトを用意します。
- 自作する場合 → BlenderやMayaなどの3Dモデリングツールで作成
- 既存のモデルを使う場合 → 無料の3Dオブジェクトサイトからダウンロード
Viroでは、以下の3Dファイル形式がサポートされています。
- .glb / .gltf(推奨) → そのまま読み込める※アニメーションはサポートされていない
- .fbx(要変換) → Viro独自のvrx形式に変換する必要あり※アニメーションをサポート
2.3Dオブジェクトを表示
次に、用意した3DオブジェクトをViroで表示します。
3Dオブジェクトをassetsフォルダに配置し、Viro3DObjectコンポーネントを使用し、初期表示設定
(位置、大きさ、向き)を指定します。
const MyScene = () => {
return (
<ViroARScene>
<ViroAmbientLight color="#ffffff" />
<Viro3DObject
source={require("../../assets/ar/chair.glb")}
position={[0, 0, -2]} // 位置
scale={[0.5, 0.5, 0.5]} // 大きさ
rotation={[0, 0, 0]} // 向き
type="GLB"
/>
</ViroARScene>
);
};
シーン内に3Dオブジェクトを表示することができます。
3.ユーザー操作による移動・回転・拡大/縮小
Viroでは、ユーザーの操作に応じて3Dオブジェクトを動かすことが可能です。
例えば、以下のようなインタラクションを実装できます。
- ドラッグ操作(オブジェクトの移動)
- ピンチ操作(オブジェクトの拡大・縮小)
- 回転操作(オブジェクトの向きを変更)
これらの操作を検知し、リアルタイムでオブジェクトの状態を更新するカスタムフックを作成することで、直感的なAR体験を実現できます。
const MyScene = () => {
const { onRotate, onPinch, currentScale, currentRotation } = useOperation();
return (
<ViroARScene>
<ViroAmbientLight color="#ffffff" />
<Viro3DObject
source={require("../../assets/ar/chair.glb")}
position={[0, 0, -2]}
scale={currentScale} // 拡大縮小
rotation={currentRotation} // 回転
type="GLB"
onDrag={() => {}}
onPinch={onPinch}
onRotate={onRotate}
/>
</ViroARScene>
);
};
このカスタムフック useOperation() では、ユーザーの操作(ピンチ・回転)を処理し、3Dオブジェクトの状態を管理するための値や関数を返却しています。
import { useState } from "react";
import { Tuple3D } from "../jotai/atom";
// ピンチ操作、回転操作を返却するカスタムhook
export const useOperation = () => {
// オブジェクトの状態管理
const [baseScale, setBaseScale] = useState<Tuple3D>([1, 1, 1]); // 確定スケール
const [currentScale, setCurrentScale] = useState<Tuple3D>([1, 1, 1]); // 一時的なスケール
const [baseRotation, setBaseRotation] = useState<Tuple3D>([0, 0, 0]); // 確定回転値
const [currentRotation, setCurrentRotation] = useState<Tuple3D>([0, 0, 0]); // 一時的回転値
const onRotate = (rotateState: number, rotationFactor: number) => {
console.log("Rotate:", rotateState, rotationFactor);
if (rotateState === 1 || rotateState === 2) {
// 操作中の回転をリアルタイムに反映
const newRotation: Tuple3D = [...baseRotation];
newRotation[1] += rotationFactor; // Y軸の回転
setCurrentRotation(newRotation);
} else if (rotateState === 3) {
// 操作終了時に確定値を更新
setBaseRotation([...currentRotation]);
console.log("Rotation finalized:", currentRotation);
}
};
const onPinch = (pinchState: number, scaleFactor: number) => {
if (pinchState === 1) {
// ピンチ開始時
console.log("Pinch Start", pinchState, scaleFactor);
} else if (pinchState === 2) {
// ピンチ中(リアルタイム更新)
const newCurrentScale: Tuple3D = baseScale.map(
(s) => s * scaleFactor
) as Tuple3D;
setCurrentScale(newCurrentScale); // 一時スケールを更新
} else if (pinchState === 3) {
// ピンチ終了時
const newBaseScale: Tuple3D = currentScale.map((s) =>
Math.min(Math.max(s, 0.5), 2)
) as Tuple3D; // 最小0.5、最大2に制限
setBaseScale(newBaseScale); // 確定スケールを更新
setCurrentScale(newBaseScale); // 一時スケールも確定値で更新
console.log("Pinch End. Final Scale:", newBaseScale);
}
};
return {
onRotate,
onPinch,
currentScale,
currentRotation,
};
};
画像認識AR
画像登録した物体を認識して、オブジェクトを表示させる方法です。
詳細はこちらの記事で説明しています。
アニメーションAR
アニメーションARには2種類あります。
- ①コード上で実装するアニメーション方法
- ②3Dオブジェクトそのものが持つアニメーションを再生する方法
①コード上で実装するアニメーション方法
詳細はこちらの記事で説明しています。
②3Dオブジェクトそのものが持つアニメーションを再生する方法
Viroでは、3Dオブジェクトが持つアニメーションを再生することができます。
例えば、BlenderやMayaで作成した3Dモデルにアニメーションを設定し、Viroでそのアニメーションを呼び出して動かすことが可能です。
- BlenderやMayaで3Dオブジェクトを作成し、アニメーションを設定
例: "jump", "breath" などのアニメーション名を付ける - FBX形式でエクスポートし、ViroFBX ConverterでVRX形式に変換
- Viro3DObjectの animation プロパティでアニメーションを再生
const MyScene = () => {
const [anim, setAnim] = useState("jump");
return (
<ViroARScene>
<ViroAmbientLight color="#ffffff" intensity={200} />
<Viro3DObject
source={require("../../assets/ar/poster/blackpanther/object_bpanther_anim.vrx")}
position={[0, -1.45, 0]}
scale={[0.5, 0.5, 0.5]}
type="VRX"
animation={{
name: anim,
run: true,
loop: false,
onFinish: () => {
setAnim("breath");
},
}}
/>
</ViroARScene>
);
};
このライブラリの課題
ViroはReact NativeでARを手軽に実装できるライブラリですが、いくつかの課題や制約も存在します。
情報が少ない
Viroはオープンソースのコミュニティ主導で運営されていますが、公式の情報が少なく、ドキュメントの整備が不十分です。Viroに関する最新情報やサポートは、コミュニティのDiscordで確認する必要があります。
メンテナンス頻度が少ない
Viroは2020年以降、コミュニティによってメンテナンスが続けられていますが、更新頻度が低いという問題があります。
React Nativeのバージョンアップに対応が遅れることが多く、新しい環境での動作検証が必要だったり、公式のGitHubリポジトリでは、未解決のIssueが溜まっているため、バグの修正や新機能の追加がスムーズに行われないことがあります。
実際に私自身も不具合を見つけOSSコントリビュートを行いました。
3Dオブジェクトの制約が厳しい
Viroでは3Dオブジェクトを表示できますが、表示するための制約が多く、適切にエクスポートしないと正しく描画されないことがあります。
エクスポート方法に厳しい指定があり、(推奨される3Dモデリングツールは Maya だが、Blenderでも対応可能)BlenderやMayaでの調整が必要になり、3Dオブジェクトの知識が求められます。
エクスポート方法については以下の記事をご覧ください。
カメラ機能が充実していない
ViroではAR表示にカメラを利用しますが、基本的なカメラ機能に制限があるため、カメラを活用した高度なAR機能を作るのが難しいです。
内カメラ、フラッシュ、手動フォーカスなどには対応していないため、実現できない場合があります。
- 内カメラ(インカメラ)に対応していない → 自撮りARエフェクトなどが作れない
- フラッシュ機能がない → 暗い場所でのAR表示が難しい
まとめ
Viroは、手軽にARを実装できることとクロスプラットフォーム対応が大きな強みです。
しかし、高度なAR表現やカメラ機能の充実を求める場合、ネイティブ開発(ARKit / ARCore)を検討する必要があります。
技術検証の結果
今回、私たちのプロジェクトでは技術検証を行った結果、Viro単体でのAR開発は難しいと判断し、採用を見送りました。
その主な理由は、以下の2点です。
3Dオブジェクトの制約
3Dオブジェクトの制約について、ViroではMayaでのエクスポートが推奨されており、Blenderとの互換性に問題があることが判明しました。
Mayaは料金も高く、多くの3DモデラーがBlenderを使用している現状を考えると、Blenderで作成した3Dオブジェクトが正しく表示されないのは致命的な問題でした。
カメラ機能の不足
内カメラ(インカメラ)が利用できず、自撮りARエフェクトの実装が不可能なため、SNS向けのAR機能として十分な体験を提供できないと判断しました。
さらに、フラッシュ機能や手動フォーカス機能も未対応であるため、カメラを活用した高度なAR表現を求めるアプリには適していませんでした。
今後の方針
SNS機能はReact Nativeを使って開発を進めつつ、AR機能については以下のどちらかのアプローチで進めていく方針です。
- SNS部分はReact Nativeで構築し、AR部分はネイティブ開発(ARKit / ARCore)を導入する
- WebARのサービスを活用し、WebViewを通じてモバイルアプリ内に表示する
技術検証の結果は以上です。ありがとうございました!
Discussion