ARKitを使っていると、ARとは別にカメラからの映像を取得したい場合があります。そこで、この記事ではARFoundationを使ってカメラ映像を取得する方法を紹介したいと思います。目次は次のとおりです。
ARFoundationのセットアップ
まずはARFoundationを使うため、ARSessionとARSessionOriginをインスペクタから追加します。ヒエラルキーウインドウから「Create→XR→ARSession」と「Create→XR→AR Session Origin」を追加します。
ヒエラルキーウインドウから「Create→3D Object→Plane」を選択して、カメラ(AR Camera)の映像を映すための平面を作ります。常にカメラに映るように、カメラに正対するように配置して、カメラの子要素にしておきます。
また、このPlaneがライティングの影響を受けないように、プロジェクトウィンドウで右クリックしてCreate→Materialを選択してマテリアルを作ります。マテリアルのShaderをUnlit→Textureに設定してからPlaneにドラッグしておきましょう。
カメラ映像をフックするスクリプトを作る
続いて、ARFoundationで使用しているカメラ映像をフックするスクリプトを作成します。ARFounationでカメラ映像を取得する流れは次のようになります。
- カメラの更新イベントを登録
- カメラフレームが更新されたタイミングでフレーム取得
- 必要に応じて回転・フリップを行う
プロジェクトウインドウで右クリックして、Create→C# Scriptでスクリプトを作成し、CamerImageControllerという名前で保存してください。保存できたら先程のPlaneにアタッチしておきましょう。
アタッチできたら、CameraImageControllerに次のスクリプトを入力します。
using System; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; public class CamerImageController : MonoBehaviour { public ARCameraManager cameraManager; private Texture2D mTexture; private MeshRenderer mRenderer; private void Start() { mRenderer = GetComponent<MeshRenderer>(); } void OnEnable() { cameraManager.frameReceived += OnCameraFrameReceived; } void OnDisable() { cameraManager.frameReceived -= OnCameraFrameReceived; } unsafe void OnCameraFrameReceived(ARCameraFrameEventArgs eventArgs) { XRCameraImage image; if (!cameraManager.TryGetLatestImage(out image)) return; var conversionParams = new XRCameraImageConversionParams ( image, TextureFormat.RGBA32, CameraImageTransformation.None ); if (mTexture == null || mTexture.width != image.width || mTexture.height != image.height) { mTexture = new Texture2D(conversionParams.outputDimensions.x, conversionParams.outputDimensions.y, conversionParams.outputFormat, false); } var buffer = mTexture.GetRawTextureData<byte>(); image.Convert(conversionParams, new IntPtr(buffer.GetUnsafePtr()), buffer.Length); mTexture.Apply(); mRenderer.material.mainTexture = mTexture; buffer.Dispose(); image.Dispose(); } }
このスクリプトではframeReceivedイベントにOnCameraFrameReceivedメソッドを追加しています。これにより、カメラのフレームが更新されるたびにOnCameraFrameReceivedメソッドが呼び出されるようになります。OnCameraFrameReceivedメソッドではTryGetLatestImageメソッドを使ってカメラ映像をXRCameraImage型のimage変数に代入しています。
続いて、XRCameraImageクラスのConvertメソッドを使って取得したカメラ映像をTexture2D型に変換しています。変換後の画像フォーマットはXRCameraImageConversionParamsで指定します。ここではカラーフォーマットはRGBA32、回転フォーマットは無しで映像を取得しています。カラーフォーマットには次のような値を指定できます。
フォーマット名 | 意味 |
---|---|
TextureFormat.R8 | 1チャネルで8bitの画像 |
TextureFormat.Alpha8 | アルファチャネルのみ |
TextureFormat.RGB24 | RGBの3チャネルで各8bitの画像 |
TextureFormat.RGBA32 | RGBAの4チャネルで各8bit画像 |
TextureFormat.ARGBA32 | ARGBの4チャネルで各8bit画像 |
TextureFormat.BGRA32 | BGRAの4チャネルで各8bit画像 |
また、回転フォーマットには次の値を指定できます。
フォーマット | 意味 |
---|---|
MirrorX | X軸でフリップ |
MirrorY | Y軸でフリップ |
None | 回転しない |
最後にTexture2DをApplyして変更を反映した後に、PlaneのMesh Rendererに設定してPlaneにカメラ映像を表示しています。
unsafeコードの設定
上記のスクリプトではunsafeコードを使っているため、そのままではコンパイルエラーになってしまいます。メニューバーからEdit→Project Settings→Playerを選択して、Allow unsafe codeにチェックを入れます。
アウトレット接続
スクリプト中で宣言したARCameraManagerに値を代入します。UnityのインスペクタにCamerImageControllerを表示し、ARCameraManagerの欄にヒエラルキーウインドウのAR Cameraをドラッグ&ドロップしてください。
これでカメラ映像がPlaneにも映るようになっているはずです。ビルドして確かめてみてください。