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。需要確保這些函式庫的更新邏輯與自訂邏輯不會衝突,否則可能會導致不可預期的結果。

相關主題