Table of Contents

獲取 session 的運行結果

session 運行過程中會修改場景中部分物體的 transform,以及修改攝像機的畫面等。有些時候,這些修改還不滿足應用的使用需要,可能需要獲取 session 每幀的運行結果,並對這些數據進行二次處理。本文介紹瞭如何獲取和使用這些結果數據。

開始之前

獲取 InputFrame 更新

可以使用 InputFrameUpdate 事件獲取 InputFrame 的更新。這個事件僅在 session 每幀輸出數據中 InputFrame 產生變化時觸發。

附註

InputFrameUpdate 只在由 EasyAR 進行畫面繪製的 session 中有效。一般來說,使用 AR Foundation 或頭顯時是無效的,這時需要使用這些第三方庫提供的方法獲取數據更新。

使用 InputFrame 可以獲取物理相機圖像、相機參數、時間戳、物理相機相對於世界座標系的變換和跟蹤狀態等。不過由於相機變換已經被 session 應用到虛擬攝像機和其它物體上,所以通常不需要通過 InputFrame 獲取相機變換。

獲取當前幀的物理相機圖像

可以使用 InputFrame.image() 方法獲取 Image 類型的物理相機圖像數據。

例如,下面這段代碼可以在 InputFrame 更新時獲取物理相機圖像:

Session.InputFrameUpdate += (inputFrame) => {
    using (var image = inputFrame.image())
    {
    }
};
注意

使用 Image 類型數據以及從它獲取的其它 class 類型數據時,必需保證 Dispose() 被正確調用(上面代碼中的 using 語句保證了這一點),否則會出現內存泄漏甚至畫面停止更新等問題。

如需保留 InputFrameImage 到下一幀使用,需要根據保留的數據量增加 ARAssembly.ExtraBufferCapacity 的數值,否則可能會因爲緩衝區不足而導致數據獲取失敗。

如需保留 InputFrame ,還需要調用 Clone() 方法創建一個引用副本,然後在不需要時對副本調用 Dispose()

由於物理相機的幀率通常低於渲染幀率,所以並不是每個渲染幀都能收到 InputFrameUpdate 事件,但同樣的,物理相機畫面渲染也並不是每個渲染幀都更新的。在 InputFrameUpdate 下次事件觸發之前的所有渲染幀的畫面內容都與當前 InputFrame 的圖像一致。

附註

InputFrame 中的圖像一定是與當前幀虛擬攝像機背景畫面一致的,但是背景畫面渲染時可能經過縮放和裁切,所以獲取的畫面大小或比例與屏幕上顯示的不一致是正常的。

另外需要注意的是,InputFrame.image() 返回的圖像數據是 CPU 可讀的,它不是 GPU 紋理。如果需要在 GPU 上使用圖像數據,需要將圖像數據上傳到 GPU 紋理中,或者通過 CameraImageRenderer.RequestTargetTexture(Action<Camera, RenderTexture>) 接口直接獲取 GPU 紋理。

[可選] 攔截物理相機圖像渲染

可以使用 ARAssembly.CameraImageRenderer 來控制物理相機圖像的繪製。

下面這段代碼可以停止物理相機圖像的繪製:

if (Session.Assembly != null && Session.Assembly.CameraImageRenderer.OnSome)
{
    Session.Assembly.CameraImageRenderer.Value.enabled = false;
}

需要注意的是,這裏需要先判斷 ARAssembly.CameraImageRenderer 是否存在。

附註

只在由 EasyAR 進行畫面繪製的 session 中,才能通過上面的方法停止畫面更新。一般來說,使用 AR Foundation 或頭顯時是無效的,這時需要使用這些第三方庫提供的方法來實現相應的功能。

停止物理相機圖像繪製後,應用可以通過 InputFrame 獲取物理相機圖像數據,並使用這些數據進行自定義的繪製。

獲取 transform 更新

可以通過 PostSessionUpdate 事件獲取 session 每幀更新後場景中物體的 transform 數據。

附註

對於部分功能(比如 Mega),即使圖像沒有變化也沒有顯示地請求服務更新,AR 計算也是每個渲染幀都在運行的。因此如果需要獲取所有的 transform 變化,則必需每幀獲取 transform 數據,而不能只在某些幀獲取。

獲取虛擬攝像機的 transform

可以通過 ARAssembly.Camera 獲取場景中攝像機的 transform。

Session.PostSessionUpdate += () =>
{
    var position = Session.Assembly.Camera.transform.position;
    var rotation = Session.Assembly.Camera.transform.rotation;
};

獲取 target 的 transform

可以通過在使用的具體 target 對象獲取場景中 target 的 transform。比如,對於圖像跟蹤來說,這個 target 就是 ImageTargetController 組件所在的物體。

Session.PostSessionUpdate += () =>
{
    var position = target.transform.position;
    var rotation = target.transform.rotation;
};

[可選] 獲取 pose

pose 是一種描述物體位置和朝向的數據結構,通常由 position 和 rotation 兩部分組成。在 AR 應用中,pose 通常用於描述物理相機或跟蹤目標相對於某個參考系的位置和朝向。

Unity 中不提供原始的 pose 數據,因爲pose 一般用於驅動場景中的物體運動,而這正是 session 自動完成的工作。對於內容計算和渲染來說,只需要 transform 就足夠了。

重要事項

在閱讀下面的方法之前,請再思考一下,場景中攝像機、跟蹤目標等物體的 transform 數據,是否已經滿足需求?通常來說,額外的 pose 數據並不是必需的。

如果確實出於某種原因需要 pose 數據,可以在 PostSessionUpdate 事件中通過 transform 計算得到所需的 pose 數值。通常來說, PostSessionUpdate 中獲取到 target 與 camera 的相對 transform 就是 pose。

下面這段代碼展示瞭如何獲取 camera 和 target 的 transform,並計算它們之間的相對 pose:

Session.PostSessionUpdate += () =>
{
    Pose cameraToWorld = new(Session.Assembly.Camera.transform.position, Session.Assembly.Camera.transform.rotation);
    Pose targetToWorld = new(target.transform.position, target.transform.rotation);
    Pose worldToTarget = new()
    {
        position = Quaternion.Inverse(targetToWorld.rotation) * (-targetToWorld.position),
        rotation = Quaternion.Inverse(targetToWorld.rotation)
    };
    Pose cameraToTarget = cameraToWorld.GetTransformedBy(worldToTarget);
};
注意

如果您同時在使用 AR Foundation、頭顯或其它第三方庫也在運行,這些庫可能也會修改場景中攝像機的 transform。需要在確保這些庫的更新邏輯完成之後再進行相關 pose 計算,否則計算結果可能不正確。在這樣的場景下, PostSessionUpdate 中 target 和 origin 的相對 pose 仍然是準確的。

[可選] 攔截 transform 更新

AR 功能運行時,Unity 中的攝像機、跟蹤目標等物體 transform 通常會被 session 自動更新。這些更新過程保證了 AR 渲染的正確性和一致性,所以沒有任何方法可用攔截這些更新。

但是如果您需要自定義物體的 transform 更新邏輯,可以通過監聽 PostSessionUpdate 事件來實現。這裏需要使用一個比較繁瑣的方法:

  1. 雖然通常情況下,應該把渲染內容以子節點或附加組件的形式掛載在 session 控制的物體下,但是如果需要自定義更新物體的 transform,就需要把這些物體從 session 控制的物體層級中移除。也就是說,這些物體不應該是 session 控制物體的子節點。
  2. PostSessionUpdate 事件中,記錄下想要自定義更新的物體的 transform。
  3. 最後,在 PostSessionUpdate 事件中,根據 session 提供的數據,使用自定義邏輯更新這些物體的 transform。
附註

使用 PostSessionUpdate 事件是必需的,因爲只有在這個時間之後,session 纔不會操作場景中的物體。

需要注意的是,這種方法不能用於修改 camera,需要更加複雜的邏輯來處理攝像機的自定義更新。

另外,這種方法只能用於自定義更新物體的 transform,不能用於修改 session 控制的物體的 transform。如果 session 控制的物體的 transform 被外部修改,session 仍然會在下一幀更新時覆蓋這些修改,進而可能影響一些計算正確性。

注意

使用這種方法需要您保證物體 transform 的正確性,否則可能會導致 AR 渲染錯誤。

如果您同時在使用 AR Foundation、頭顯或其它第三方庫,這些庫可能也會修改場景中物體的 transform。需要確保這些庫的更新邏輯與自定義邏輯不會衝突,否則可能會導致不可預期的結果。

相關主題