Custom Pose Trackerの実装
このチュートリアルでは、3.CustomPoseTrackerサンプルプロジェクトを直接実装し、Custom Pose Trackerを直接開発して適用する方法を学びます。Custom Pose Trackerとは、ユーザーが直接開発したPoseTrackerを意味します。
ほとんどの場合、VLSDKが提供する基本Pose Trackerを使用してアプリを開発できます。しかし、ロボット、ARグラスなど、VLSDKが基本的に提供するPoseTrackerを使用するのが難しい環境の場合、PoseTrackerを直接開発して使用する必要があります。
このチュートリアルを始める前に、シンプルなアプリの実装を完了する必要があります。
1. CustomPoseTrackerの使用フロー
VLSDK全体の動作プロセスにおいて、PoseTrackerはARFrameを生成し、VLSDKのコアロジックに渡す役割を果たします。詳細はドキュメントを参照してください。
2. CustomPoseTrackerの作成(基本)
2.1 PoseTracker継承スクリプトの作成
- 任意の場所に
MyPoseTracker.csファイルを作成します。CustomPoseTrackerAdaptorは、任意の場所に作成したPoseTrackerファイルも認識できます。

- 次のように
MyPoseTrackerクラスを実装します。
using UnityEngine;
using ARCeye;
public class MyPoseTracker : PoseTracker // <- PoseTrackerを継承するクラス。
{
// 毎フレーム呼び出されるメソッド。ARFrameを生成して返します。
protected override ARFrame CreateARFrame()
{
// 画像を読み込みます。迅速なテストのため、毎フレームResourcesディレクトリの画像を使用します。
var texture = Resources.Load<Texture2D>("001");
// ARFrameを生成します。
ARFrame frame = new ARFrame();
frame.texture = texture;
return frame;
}
}
- テストに使用した画像は、次のようなパスに配置されています。

3. CustomPoseTrackerの適用
3.1 CustomPoseTrackerAdaptor
VLSDKManagerを選択し、CustomPoseTrackerAdaptorコンポーネントを追加します。Use Custom Editor Pose Tracker項目にチェックを入れると、Editor環境でPlay Mode進入時に使用されるPoseTrackerを選択できます。Use Custom Device Pose Tracker項目にチェックを入れると、実際のデバイス環境で使用されるPoseTrackerを選択できます。ここではUse Custom Editor Pose Trackerにチェックを入れ、先ほど作成したMyPoseTrackerを選択します。

Play Modeに入ると、MyPoseTrackerを通じてARFrameを生成し、VLリクエストを送信する様子を確認できます。

4. CustomPoseTrackerの作成(拡張)
次は、CustomPoseTrackerを使用する高度な例です。ユーザーのシステムが独自のループを持っている場合、以下のような形式で実装できます。以下の例はARFoundationをベースにしていますが、様々なシステムに応用可能です。
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using Unity.Collections.LowLevel.Unsafe;
using ARCeye;
public class MyPoseTracker : PoseTracker
{
private ARCameraManager m_CameraManager;
private ARCameraFrameEventArgs m_LastFrameEventArgs;
private Texture2D m_CameraTexture;
private RawImage m_RequestedTexture;
// MyPoseTracker最初の生成時に呼び出されるイベント。
public override void OnCreate(Config config)
{
config.tracker.useFaceBlurring = false;
// ARFoundationを使用。
m_CameraManager = GameObject.FindObjectOfType<ARCameraManager>();
// デバッグ用のカメラプレビュー出力用画像。
m_RequestedTexture = GameObject.Find("Canvas/RawImage_Test").GetComponent<RawImage>();
}
// ループイベント登録。
public override void RegisterFrameLoop()
{
m_CameraManager.frameReceived += OnCameraFrameReceived;
}
// ループイベント解除。
public override void UnregisterFrameLoop()
{
m_CameraManager.frameReceived -= OnCameraFrameReceived;
}
private void OnCameraFrameReceived(ARCameraFrameEventArgs eventArgs)
{
if (!m_IsInitialized)
{
return;
}
m_LastFrameEventArgs = eventArgs;
OnFrameLoop();
}
// ARFrame生成イベント。
protected override ARFrame CreateARFrame()
{
return CreateARFrameFromEventArgs(m_LastFrameEventArgs);
}
private ARFrame CreateARFrameFromEventArgs(ARCameraFrameEventArgs eventArgs)
{
ARFrame frame = new ARFrame();
// Camera texture.
frame.texture = GetCameraTexture();
m_RequestedTexture.texture = frame.texture;
// Camera model matrix.
frame.localPosition = m_ARCamera.transform.localPosition;
frame.localRotation = m_ARCamera.transform.localRotation;
// Intrinsic matrix.
frame.intrinsic = AquireCameraIntrinsic();
// Projection matrix.
frame.projMatrix = eventArgs.projectionMatrix ?? Camera.main.projectionMatrix;
// Display matrix.
frame.displayMatrix = eventArgs.displayMatrix ?? Matrix4x4.identity;
return frame;
}
unsafe private Texture2D GetCameraTexture()
{
if (!m_CameraManager.TryAcquireLatestCpuImage(out XRCpuImage cpuImage))
{
Debug.LogError("Failed to acquire latest CPU image.");
return null;
}
TryUpdateCameraTexture(cpuImage);
var rawTextureData = m_CameraTexture.GetRawTextureData<byte>();
var rawTexturePtr = new IntPtr(rawTextureData.GetUnsafePtr());
var conversionParams = new XRCpuImage.ConversionParams(cpuImage, TextureFormat.RGBA32);
try
{
conversionParams.inputRect = new RectInt(0, 0, cpuImage.width, cpuImage.height);
conversionParams.outputDimensions = cpuImage.dimensions;
cpuImage.Convert(conversionParams, rawTexturePtr, rawTextureData.Length);
}
finally
{
cpuImage.Dispose();
}
m_CameraTexture.Apply();
return m_CameraTexture;
}
private void TryUpdateCameraTexture(XRCpuImage cpuImage)
{
var outputDimensions = cpuImage.dimensions;
if (IsCameraTextureUpdated(outputDimensions))
{
m_CameraTexture = new Texture2D(cpuImage.width, cpuImage.height, TextureFormat.RGBA32, false);
}
}
private bool IsCameraTextureUpdated(Vector2Int outputDimensions)
{
return m_CameraTexture == null || m_CameraTexture.width != outputDimensions.x || m_CameraTexture.height != outputDimensions.y;
}
public ARIntrinsic AquireCameraIntrinsic()
{
float fx, fy, cx, cy;
if (m_CameraManager.TryGetIntrinsics(out XRCameraIntrinsics cameraIntrinsics))
{
fx = cameraIntrinsics.focalLength.x;
fy = cameraIntrinsics.focalLength.y;
cx = cameraIntrinsics.principalPoint.x;
cy = cameraIntrinsics.principalPoint.y;
}
else
{
fx = 0;
fy = 0;
cx = 0;
cy = 0;
}
return new ARIntrinsic(fx, fy, cx, cy);
}
}
5. まとめ
PoseTrackerを拡張したCustomPoseTrackerを使用して、様々な環境でVLSDKを使用する方法を学びました。これをもとに、ARFoundationをサポートしていない開発環境でも、VLSDKを連動したサービスを開発できます。