おもちゃラボ

Unityで遊びを作ってます

【ARFoundation】カメラ映像を取得する

ARKitを使っていると、ARとは別にカメラからの映像を取得したい場合があります。そこで、この記事ではARFoundationを使ってカメラ映像を取得する方法を紹介したいと思います。目次は次のとおりです。

ARFoundationのセットアップ

まずはARFoundationを使うため、ARSessionとARSessionOriginをインスペクタから追加します。ヒエラルキーウインドウから「Create→XR→ARSession」と「Create→XR→AR Session Origin」を追加します。

f:id:nn_hokuson:20200129213752p:plain:w230

ヒエラルキーウインドウから「Create→3D Object→Plane」を選択して、カメラ(AR Camera)の映像を映すための平面を作ります。常にカメラに映るように、カメラに正対するように配置して、カメラの子要素にしておきます。

f:id:nn_hokuson:20200129214111p:plain

また、このPlaneがライティングの影響を受けないように、プロジェクトウィンドウで右クリックしてCreate→Materialを選択してマテリアルを作ります。マテリアルのShaderをUnlit→Textureに設定してからPlaneにドラッグしておきましょう。

f:id:nn_hokuson:20200129214741j:plain

カメラ映像をフックするスクリプトを作る

続いて、ARFoundationで使用しているカメラ映像をフックするスクリプトを作成します。ARFounationでカメラ映像を取得する流れは次のようになります。

  1. カメラの更新イベントを登録
  2. カメラフレームが更新されたタイミングでフレーム取得
  3. 必要に応じて回転・フリップを行う

プロジェクトウインドウで右クリックして、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にチェックを入れます。

f:id:nn_hokuson:20200129215259p:plain

アウトレット接続

スクリプト中で宣言したARCameraManagerに値を代入します。UnityのインスペクタにCamerImageControllerを表示し、ARCameraManagerの欄にヒエラルキーウインドウのAR Cameraをドラッグ&ドロップしてください。

f:id:nn_hokuson:20200129215637j:plain

これでカメラ映像がPlaneにも映るようになっているはずです。ビルドして確かめてみてください。

Visual Studio for Macでタブ幅・インデント幅を変える

ソリューションウインドウ(表示されていない場合はメニューバーから「表示」→「パッド」→「ソリューション」)のプロジェクト名の上で右クリックして「オプション」を選択します。

f:id:nn_hokuson:20200125204458j:plain:w600

ソースコード→コードのフォーマット→C#のソースコードを選択します。
ポリシーの欄で「Default」を選択するか、またはテキストファイルの規定の設定を使用のチェックを外して、タブ幅、インデント幅を設定してから、右下の「OK」ボタンを押します。

f:id:nn_hokuson:20200125204533p:plain

【Unity】Xcodeで library not found for liPhone-libのエラーが出る

Unity2019とXcode11を使ってUnityのプロジェクトをビルドすると「library not found for -liPhone-lib」というエラーが出ることがあります。

ld: library not found for -liPhone-lib
clang: error: linker command failed with exit code 1 (use -v to see invocation)

f:id:nn_hokuson:20191021150812p:plain

Xcodeプロジェクトより上の階層に日本語のフォルダがあるとこのエラーが出るようです。解決策としては次の3つがあります。

  1. 日本語フォルダを英語にリネームする
  2. Xcodeプロジェクトを日本語のフォルダ名を含まない場所に移動する
  3. XcodeのLibrary Search Pathを設定する

3つめのLibrary Search Pathの方法だけ、少し手順が複雑なので詳しく説明します。

XcodeのLibrary Search Pathを設定する

Xcodeの左カラムからプロジェクト名(ここではUnity-iPhone)を選択した状態でBuild Settingsタブを開きます。その中のSearch PathsセクションのLibrary Search Pathsを見つけて下さい。

f:id:nn_hokuson:20191021151907p:plain

Library Search Pathsのパス名をダブルクリックするとウインドウが開くので、「"$(SRCROOT)"」を選択して、マイナスボタンで削除します。

f:id:nn_hokuson:20191021152220p:plain:w400

設定できたら、再度ビルドボタンを押してビルドが通ることを確認して下さい。