Quest 3 のパススルーカメラから QR コードを読み取れるようにしてみました 🎉
最終的にはこんな感じになります 👇
環境
- Unity 6000.0.28f1
- Meta All-in-One SDK 77.0.0
- Quest 3 実機
- Render Pipeline: URP / Built-in 両方で検証
- ZXing.Net for Unity
1. AndroidManifest に必要な権限
Quest 3 でパススルーカメラを使うには、通常の CAMERA
に加えて ヘッドセット専用のカメラ権限 が必要です。
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="horizonos.permission.HEADSET_CAMERA" />
Unity のビルド結果の AndroidManifest.xml に両方が入っているか必ず確認してください。
2. カメラ権限のリクエスト
Unity 側では起動時に以下のように権限をチェックしてリクエストします。
#if UNITY_ANDROID && !UNITY_EDITOR
if (!Permission.HasUserAuthorizedPermission("android.permission.CAMERA") ||
!Permission.HasUserAuthorizedPermission("horizonos.permission.HEADSET_CAMERA"))
{
Permission.RequestUserPermissions(
new[] { "android.permission.CAMERA", "horizonos.permission.HEADSET_CAMERA" },
new PermissionCallbacks()
);
return;
}
#endif
3. WebCamTexture.devices の挙動 (URP と Built-in)
実機で試したところ、Render Pipeline によって WebCamTexture.devices のインデックス が異なっていました。
URPプロジェクトではdevices[0]は映像が取得できず、devices[1],devices[2]で映像が取得できました。
Built-inプロジェクトではdevices[0]で映像が取得できていました。
👉 そのため「devices[0] を使えばいい」と決め打ちすると環境依存でハマります。
4. ZXing を使った QR 読み取り
ZXing の BarcodeReader を使い、取得したカメラ映像 (cam.GetPixels32()) をデコードします。
reader = new BarcodeReader
{
AutoRotate = true,
Options = new DecodingOptions
{
TryHarder = true,
TryInverted = true,
PossibleFormats = new[] { BarcodeFormat.QR_CODE }
}
};
5. 実装例 (UI付き)
以下は 読み取り結果を TextMeshPro で表示し、RawImage にカメラ映像を出すサンプル です。
using TMPro;
using UnityEngine;
using ZXing;
using ZXing.Common;
using UnityEngine.Android;
using UnityEngine.UI;
public class QrTestWithUI : MonoBehaviour
{
public TextMeshProUGUI resultText; // QR結果表示用
public TextMeshProUGUI cameraNameText; // 現在使用中カメラ名表示
public RawImage previewImage; // 映像表示用
private WebCamTexture cam;
private BarcodeReader reader;
private bool permissionRequested = false;
private int decodeCount = 0;
private bool hasScanned = false;
void Start()
{
#if UNITY_ANDROID && !UNITY_EDITOR
if (!Permission.HasUserAuthorizedPermission("android.permission.CAMERA") ||
!Permission.HasUserAuthorizedPermission("horizonos.permission.HEADSET_CAMERA"))
{
Permission.RequestUserPermissions(
new[] { "android.permission.CAMERA", "horizonos.permission.HEADSET_CAMERA" },
new PermissionCallbacks()
);
permissionRequested = true;
resultText.text = "カメラ権限をリクエスト中…";
return;
}
#endif
StartCamera();
}
void Update()
{
#if UNITY_ANDROID && !UNITY_EDITOR
if (permissionRequested &&
Permission.HasUserAuthorizedPermission("android.permission.CAMERA") &&
Permission.HasUserAuthorizedPermission("horizonos.permission.HEADSET_CAMERA"))
{
permissionRequested = false;
StartCamera();
}
#endif
if (cam != null && cam.didUpdateThisFrame)
{
if (hasScanned) return;
decodeCount++;
var pixels = cam.GetPixels32();
var result = reader.Decode(pixels, cam.width, cam.height);
if (result != null)
{
hasScanned = true;
resultText.text = $"読み取り成功: {result.Text}";
resultText.color = Color.green;
}
else
{
resultText.text = $"スキャン中… (#{decodeCount})";
resultText.color = Color.yellow;
}
}
}
void StartCamera()
{
var devices = WebCamTexture.devices;
if (devices.Length == 0)
{
resultText.text = "カメラが見つかりません";
return;
}
// Camera1 を検索して使用
var target = System.Array.Find(devices, d => d.name.Contains("Camera1"));
if (target.name == null)
{
resultText.text = "Camera1 が見つかりません";
return;
}
cameraNameText.text = $"使用カメラ: {target.name}";
cam = new WebCamTexture(target.name, 1280, 720, 30);
cam.Play();
previewImage.texture = cam;
reader = new BarcodeReader
{
AutoRotate = true,
Options = new DecodingOptions
{
TryHarder = true,
TryInverted = true,
PossibleFormats = new[] { BarcodeFormat.QR_CODE }
}
};
decodeCount = 0;
hasScanned = false;
resultText.text = "QRスキャン中…";
resultText.color = Color.white;
}
}
6. 実行結果
-
Quest 3 実機でビルドするとパススルーカメラ映像が RawImage に表示されます
-
QR コードをかざすと「読み取り成功: (テキスト)」が表示されます
-
一度読み取ったら処理を止める仕様にしています
まとめ
-
Quest 3 のパススルーカメラには 2種類の権限 が必要
-
WebCamTexture.devices のインデックスは URP / Built-in で異なる
-
ZXing を使えば簡単に QR コード認識を組み込める
これで Quest 3 MR アプリに 現実の QR を使った体験 を追加できます 🎉
応用例
-
会場で配布した QR を読み取ってシーン切り替え
-
現実オブジェクトの位置合わせ用マーカー
-
Web 連携で情報を呼び出す