訪問Meta Quest攝像頭
(中國AI網 2025年04月08日)開發者現在已經可以訪問Meta Quest頭顯的攝像頭,但具體的操作是怎樣的呢。下面這篇來自Skarredghost的博文將逐步解釋如何使用Meta SDK的Passthrough Camera Access在Unity 6創建一個項目:

如果你是Unity或Meta SDK的初學者,你可以查看YouTube視頻。但如果你對Unity中的XR開發有一定的認知,并且你喜歡文本而非視頻,請繼續閱讀下文。
在我們開始之前……
用于攝像頭訪問的SDK屬于實驗性,在接下來的時間里可能會出現非常大的變化。
先決條件
我們先看看攝像頭訪問的先決條件是什么:
必須是Meta Quest 3或Meta Quest 3S。暫時不支持其他頭顯
設備必須至少是v74。
必須有最新版本的Unity。Meta建議2022.3.58f1或6000.0.38f1。不過,稍微不同的版本應該依然可行。本教程使用了6000.0.34f1。
請注意,在撰寫本文時,所述功能標記為實驗性,所以你不能在Horizon Store發布任何實現它的應用功能。按照過往經驗,數個月之后就會解禁。
Meta Quest 3攝像頭訪問的基礎
Quest 3的攝像頭訪問使用經典的Android工具來訪問攝像頭框架。特別地,它是以Camera2類作為基礎。
在Unity中,幾乎沒有人直接使用Camera2類,因為它需要JNI調用,而這總是非常痛苦。Unity開發者通常使用WebCamTexture類,它能夠將攝像頭幀捕獲到動態Unity紋理中。對于全新的透視API,可以完全遵循這個過程并獲得設備左或右前置攝像頭幀的WebCamTexture。在底層,WebCamTexture查詢Camera2。
一旦有了WebCamTexture,你就可以做任何事情:你可以在屏幕顯示紋理,或者抓取幀像素并分析它們(并可能將它們發送給AI算法)。記住,每次你從紋理中抓取像素并將它們移動到你的“CPU”內存進行分析時,你都會引入延遲,因為將數據從GPU移動到CPU是一個有點慢的操作。
Meta的這種方法有一個優勢:與Android手機使用的方法一樣,谷歌同樣表示,Android XR使用了同樣的方法。所以,創建跨平臺應用將變得相當容易。
這種方法的另一個優勢是給予用戶控制權:在Android訪問攝像頭總是需要用戶的許可,而這會發生在Quest身上。Meta已經創建了一個特殊的權限,而你必須請求用戶提供攝像頭幀訪問權限。用戶必須信任你,否則你將無法獲得攝像頭幀。
Passthrough Camera API示例
開始使用Passthrough Camera API的建議方法,包括官方建議,都是從Meta提供的官方示例開始。Meta建議從這個名為Unity-PassthroughCameraApiSamples的樣本項目開始。
所述示例項目是開始學習攝像頭訪問的好方法。你可以從現有的代碼中學習,或可以修改調整,甚至可以將其中的代碼復制粘貼到自己的項目中,以加快開發速度。
(順便說一句,除了官方的示例,另一個示例庫提供了一系列有趣的例子。當然,你應該先看完官方示例后再深入研究。
需要注意的是,在撰寫博文的時候,有一個小問題阻礙了這個項目在Unity 6順利運行。就理論而言,項目應該開箱即用,但實際上并不是。所以下面將分享如何獲得、修復和運行這個項目。
獲取和構建示例的步驟如下:
克隆你喜歡的存儲庫的示例
打開Unity Hub
選擇Add -> Project From Disk并選擇你剛剛克隆項目的文件夾。項目應該顯示為Unity 2022打開。點擊項目的Unity版本標簽,并在即將到來的窗口中選擇你的Unity 6版本和Android build平臺。這十分必要,因為本教程是關于Unity 6。


確認你想要的版本
打開項目
修復Activity名稱:點擊編輯器窗口上方的“Meta XR Tools”下拉列表,并從下拉列表中選擇Project Setup Tool

在下面的項目設置工具窗口中,你應該會看到一個錯誤,類似于“Always specify single GameActivity…”。單擊與其關聯的Fix按鈕,錯誤應該會消失
如果有其他錯誤,修復它們
關閉項目設置工具窗口
修復Android Manifest文件:在菜單中選擇“Meta -> Tools -> Update AndroidManifest.xml”
如果有彈出窗口要求確認是否覆蓋文件或類似,請確認操作

修復攝像頭紋理的初始化時間。Camera SDK應該在播放WebCamTexture之前給系統更多的初始化時間
打開腳本WebCamTextureManager.cs,位置是\Assets\ passthroughcameraapissamples \PassthroughCamera\Scripts\
找到OnEnable函數并將開始攝像頭初始化的最后幾行改成為初始化添加兩幀延遲的協程。一開始的代碼是這樣:
private void OnEnable()
{
PCD.DebugMessage(LogType.Log, $"PCA: {nameof(OnEnable)}() was called");
if (!PassthroughCameraUtils.IsSupported)
{
PCD.DebugMessage(LogType.Log, "PCA: Passthrough Camera functionality is not supported by the current device." +
$" Disabling {nameof(WebCamTextureManager)} object");
enabled = false;
return;
}
m_hasPermission = PassthroughCameraPermissions.HasCameraPermission == true;
if (!m_hasPermission)
{
PCD.DebugMessage(LogType.Error,
$"PCA: Passthrough Camera requires permission(s) {string.Join(" and ", PassthroughCameraPermissions.CameraPermissions)}. Waiting for them to be granted...");
return;
}
PCD.DebugMessage(LogType.Log, "PCA: All permissions have been granted");
_ = StartCoroutine(InitializeWebCamTexture());
}
private void OnDisable()
{
PCD.DebugMessage(LogType.Log, $"PCA: {nameof(OnDisable)}() was called");
StopCoroutine(InitializeWebCamTexture());
if (WebCamTexture != null)
{
WebCamTexture.Stop();
Destroy(WebCamTexture);
WebCamTexture = null;
}
}
你需要改成這樣(注意從第19行開始的與OnEnable相關的修改):
private void OnEnable()
{
PCD.DebugMessage(LogType.Log, $"PCA: {nameof(OnEnable)}() was called");
if (!PassthroughCameraUtils.IsSupported)
{
PCD.DebugMessage(LogType.Log, "PCA: Passthrough Camera functionality is not supported by the current device." +
$" Disabling {nameof(WebCamTextureManager)} object");
enabled = false;
return;
}
m_hasPermission = PassthroughCameraPermissions.HasCameraPermission == true;
if (!m_hasPermission)
{
PCD.DebugMessage(LogType.Error,
$"PCA: Passthrough Camera requires permission(s) {string.Join(" and ", PassthroughCameraPermissions.CameraPermissions)}. Waiting for them to be granted...");
return;
}
else
{
_ = StartCoroutine(WaitAndInitialize());
}
}
private IEnumerator WaitAndInitialize()
{
yield return null;
yield return null;
PCD.DebugMessage(LogType.Log, "PCA: All permissions have been granted");
_ = StartCoroutine(InitializeWebCamTexture());
}
private void OnDisable()
{
PCD.DebugMessage(LogType.Log, $"PCA: {nameof(OnDisable)}() was called");
StopCoroutine(InitializeWebCamTexture());
if (WebCamTexture != null)
{
WebCamTexture.Stop();
Destroy(WebCamTexture);
WebCamTexture = null;
}
}
(有一種替代方法,同一文件的第109行,那里已經有一個yield return null,讓你在播放WebCamTexture之前等待一幀,添加更多yield return null函數,令系統等待更長時間。那邊可以選擇這兩種方法中的任何一種,兩者都有效)
保存文件。由于這個修改,當你多次啟動應用程序時,它不會崩潰。你只是在初始化中添加了非常短的延遲,沒有人會注意到
現在你可以
將你的Quest 3接到電腦
選擇File -> Build And Run
選擇保存Build的位置
等待結束
現在你可以戴上頭顯并進行體驗。

修改示例
強烈建議你嘗試一下示例,研究不同的場景。然后,按照下面的說明創建自己的新場景。一般來說,請閱讀repo的README文件,閱讀Meta關于攝像頭訪問的官方文檔,并嘗試相關示例。
從零開始一個攝像頭訪問項目
我喜歡Meta提供的開箱即用示例。但有時候你真的想從零開始一個項目。這時候,你不能從提供的示例項目開始,而是要自己在項目中實現PCA。下面將介紹如何開始:
打開Unity Hub
創建一個新的Unity項目,選擇Unity 6作為版本,并指定項目類型為“Universal 3D Core”(非必須,但通常當你為Quest開發內容時,你總是使用URP)
等待Unity創建并打開新項目
切換到Android Build平臺。立即執行,以避免稍后重新導入大量內容。如果不清楚,你可以選擇File -> Build Profiles,然后在彈出的窗口中選擇Android選項卡,點擊“切換平臺”按鈕
現在項目已經在正確的平臺,如果愿意,你可以更改Project Settings,指定正確的名稱,并選擇Android包名稱。這一步完全屬于可選步驟
(從Windows菜單)打開Unity Package Manager
安裝MRUK包:點擊包管理器左上角的“+”按鈕,選擇“Install package by name…”,并指定名稱為“com.meta.xr.mrutilitykit”。單擊“Install”確認。安裝應該在幾秒鐘后開始
如果Unity提示重新啟動編輯器,重新啟動
關閉包管理器和任何打開的彈出窗口
重新打開項目設置(編輯->項目設置…)
在項目設置窗口中,單擊左側“XR插件管理”選項卡,并選擇右側XR Plug-in management按鈕
當安裝了XR Plug-in management后,窗口的右側將填充。進入與PC相關的選項卡,選擇OpenXR插件。如果一個彈出窗口要求你安裝什么Meta XR功能或類似,點擊“Yes”。

如果已經移動到與Project Validation相關的另一個選項卡,請移回XR Plug-in management的前一個選項卡。然后轉到Android相關的子選項卡,選擇OpenXR插件。
現在我們必須添加Meta Quest控制器的交互配置文件。在左側選擇選項卡XR Plug-in management-> OpenXR。你應該看到窗口右側顯示“Enabled Interaction Profiles”。點擊旁邊的“+”按鈕,添加“Meta Quest Touch Plus Controller Profile”。然后對“Meta Quest Touch Pro Controller Profile”執行同樣的操作。PC和Android均需重復這一操作。PC不是特別有用,但如果你想在編輯器中進行一定的測試,你可以嘗試一番。

在左側選擇選項卡XR Plug-in management->項目驗證。如果你是有經驗的用戶,請評估如何處理各種建議條目。對于這個示例,我們簡單行事,在PC和Android選項卡都點擊Fix All。
現在可以關閉“項目設置”窗口
你應該在項目中打開SampleScene,然后執行下面的操作:
刪除場景中的所有內容
在菜單中,轉到Meta -> Tools -> Building Blocks
將Camera Rig、Passthrough和Controller Tracking拖到場景中。這樣我們就可以有一個帶透視和可見控制器的場景
關閉Building Blocks

修復Meta XR設置的警告
單擊編輯器上方的Meta XR Tools下拉列表,并從下拉列表中選擇Project Setup Tool
在下面的Project Setup Tool窗口中,你應該看到一定的警告和錯誤
最后一個應該討論場景的特定權限(“當在項目中使用場景時,它需要執行……”)。點擊它旁邊的三個點,然后選擇“忽略”。它應該消失。之所以忽略,是因為我們不希望Meta自動請求場景權限,因為Camera Access SDK將在腳本之一自行執行
現在你可以點擊“Apply All”來修復所有剩下的問題
關閉項目設置工具窗口
從示例項目中導入Camera Access的基礎設施。注意,我們可以自己編寫,但既然Meta提供了現成的工具,為什么要麻煩呢?
克隆你喜歡的存儲庫(如果沒有在前一個教程中執行)
將示例項目中的\Assets\PassthroughCameraApiSamples\PassthroughCamera\文件夾復制到新項目的Assets文件夾中。如果愿意,你可以從Windows文件管理器拖拽到Unity項目窗口
如果有必要,應用與上文描述的WebCamTextureManager.cs文件相關修復(如果Meta修復了錯誤,或者如果你已經在遵循上面的教程修復,則現在沒有必要)

完成場景:
將Assets/PassthroughCamera/Prefabs/WebCamTextureManagerPrefab.拖到場景中。這個預制件包含WebCamTextureManager腳本,它將管理攝像頭訪問并提供WebCamTexture和關于攝像頭的元數據。請注意,這個預制件中同時有一個權限管理器,它將詢問用戶使用攝像頭和使用場景數據的權限。如果需要,可以使用這個腳本請求其他運行時權限
在場景中創建一個新的cube
在項目中創建一個新腳本,將其命名為WebcamTextureAssigner,并粘貼以下代碼
將WebcamTextureAssigner腳本分配給cube
using PassthroughCameraSamples;
using System.Collections;
using UnityEngine;
///
/// Assigns the
/// to the
///
[RequireComponent(typeof(Renderer))]
public class WebcamTextureAssigner : MonoBehaviour
{
///
/// Start coroutine
///
///
IEnumerator Start()
{
//references that will be filled by the coroutine
WebCamTextureManager webCamTextureManager = null;
WebCamTexture webCamTexture = null;
//wait until the WebCamTextureManager is found and is ready to provide a texture
do
{
yield return null;
//if the WebCamTextureManager is not found yet, find it
if (webCamTextureManager == null)
{
webCamTextureManager = FindFirstObjectByType
}
//else, if we have it, try to get the texture of the camera of the headset
else
{
webCamTexture = webCamTextureManager.WebCamTexture;
}
} while (webCamTexture == null);
//here we have the texture. Assign it to the main texture of the main material of the renderer
GetComponent
}
}
我們現在必須修復Android Manifest請求的所有必需權限:
在菜單中選擇“Meta -> Tools -> Create store-compatible AndroidManifest.xml”。這將用需要所有正確權限的Manifest替換當前的Manifest
如果要求確認更換Manifest,請確認
現在打開manifest文件,它位于Assets/Plugins/Android/AndroidManifest.xml
在文件末尾有各種標簽的地方,添加:
這將確保提示用戶是否允許訪問攝像頭


保存場景,保存項目
通過USB接到PC
在設備運行(File -> Build And Run)
如果提示Input Handling設置為“both”,選擇Yes繼續(在本示例中,我們不關心,但在Production環境中,你應該使用新的輸入系統)
享受camera-textured cube。你會看到cube的顏色有點暗淡,這是因為我們從場景中刪除了照明。我們使用默認的照明材質。將材質改為unlit應該可以解決這個問題

從WebcamTexture獲取數據
上面介紹了如何使用Meta提供的預制工具獲得WebcamTexture,而你同時應該學習如何獲得實際的像素數據。要做到這一點,你可以使用一些眾所周知的方法。一個是使用GetPixels,另一個是使用AsynGPUReadback。第一個是阻塞調用,但以一種簡單的方式直接提供想要的所有像素數據;第二個不會,但使用起來有點復雜(它在調用中為你提供數據)。你可以看看相關的文檔并決定你想要使用哪一個:
GetPixels
AsynGPUReadback
請記住,將數據從GPU(紋理所在)移動到CPU(可以讀取像素數據的地方)是一個非常緩慢和成本昂貴的操作。所以只在必要的時候執行。

