Khai thác thông tin chuyên sâu trong ứng dụng AR Foundation trên Android

API Độ sâu giúp máy ảnh của thiết bị hiểu được kích thước và hình dạng của đối tượng thực trong cảnh. Tính năng này sử dụng máy ảnh để tạo hình ảnh có chiều sâu hoặc bản đồ độ sâu, từ đó thêm một lớp hiện thực AR vào ứng dụng của bạn. Bạn có thể sử dụng thông tin mà hình ảnh chiều sâu cung cấp để khiến các vật thể ảo xuất hiện chính xác phía trước hoặc phía sau vật thể trong thế giới thực, mang lại trải nghiệm sống động và chân thực cho người dùng.

Thông tin về độ sâu được tính toán từ chuyển động và có thể kết hợp với thông tin từ cảm biến độ sâu phần cứng, chẳng hạn như cảm biến thời gian bay (ToF), nếu có. Thiết bị không cần có cảm biến ToF để hỗ trợ Depth API.

Điều kiện tiên quyết

Hãy đảm bảo bạn hiểu rõ các khái niệm cơ bản về AR và cách định cấu hình một phiên ARCore trước khi tiếp tục.

Định cấu hình ứng dụng của bạn thành Depth Required hoặc Depth Optional (chỉ Android)

Nếu ứng dụng của bạn yêu cầu hỗ trợ Deep API (API chiều sâu), một phần cốt lõi của trải nghiệm AR dựa vào chiều sâu, hoặc vì không có phương án dự phòng phù hợp cho các phần của ứng dụng sử dụng chiều sâu, thì bạn có thể chọn giới hạn việc phân phối ứng dụng trong Cửa hàng Google Play cho thiết bị hỗ trợ Depth API.

Biến ứng dụng thành Depth Required

Chuyển đến Edit > Project Settings > XR Plug-in Management > ARCore.

Theo mặc định, Depth được đặt thành Required.

Biến ứng dụng thành Depth Optional

  1. Chuyển đến Edit > Project Settings > XR Plug-in Management > ARCore.

  2. Trong trình đơn thả xuống Depth, hãy chọn Optional để tuỳ ý đặt một ứng dụng thành Chiều sâu.

Bật độ sâu

Để tiết kiệm tài nguyên, theo mặc định, ARCore không bật Depth API. Để tận dụng độ sâu trên các thiết bị được hỗ trợ, bạn phải tự thêm thành phần AROcclusionManager vào đối tượng trò chơi Máy ảnh AR bằng thành phần CameraARCameraBackground. Hãy xem phần Tự động che khuất trong tài liệu của Unity để biết thêm thông tin.

Trong phiên ARCore mới, hãy kiểm tra xem thiết bị của người dùng có hỗ trợ chiều sâu và API độ sâu hay không, như sau:

// Reference to AROcclusionManager that should be added to the AR Camera
// game object that contains the Camera and ARCameraBackground components.
var occlusionManager = …

// Check whether the user's device supports the Depth API.
if (occlusionManager.descriptor?.supportsEnvironmentDepthImage)
{
    // If depth mode is available on the user's device, perform
    // the steps you want here.
}

Thu thập hình ảnh có chiều sâu

Nhận hình ảnh chiều sâu môi trường mới nhất từ AROcclusionManager.

// Reference to AROcclusionManager that should be added to the AR Camera
// game object that contains the Camera and ARCameraBackground components.
var occlusionManager = …

if (occlusionManager.TryAcquireEnvironmentDepthCpuImage(out XRCpuImage image))
{
    using (image)
    {
        // Use the texture.
    }
}

Bạn có thể chuyển đổi hình ảnh CPU thô thành RawImage để linh hoạt hơn. Bạn có thể xem ví dụ về cách thực hiện việc này trong mẫu ARFoundation của Unity.

Tìm hiểu giá trị độ sâu

Cho trước điểm A trên hình học thực tế đã quan sát và một điểm 2D a đại diện cho cùng một điểm trong hình ảnh chiều sâu, giá trị do API Độ sâu cung cấp tại a bằng với chiều dài của CA chiếu trên trục chính. Đây cũng có thể được gọi là toạ độ z của A so với C gốc của máy ảnh. Khi làm việc với API Độ sâu, bạn cần hiểu rằng giá trị độ sâu không phải là độ dài của tia CA, mà là phép chiếu của tia đó.

Ẩn các vật thể ảo và trực quan hoá dữ liệu độ sâu

Hãy xem bài đăng trên blog của Unity để biết thông tin tổng quan về dữ liệu độ sâu và cách sử dụng dữ liệu này để che khuất hình ảnh ảo. Ngoài ra, các mẫu ARFoundation của Unity minh họa việc che khuất hình ảnh ảo và trực quan hóa dữ liệu độ sâu.

Bạn có thể kết xuất che khuất bằng cách kết xuất hai lượt hoặc cho mỗi đối tượng, kết xuất chuyển tiếp. Hiệu quả của mỗi phương pháp phụ thuộc vào độ phức tạp của cảnh cũng như những yếu tố khác dành riêng cho ứng dụng.

Kết xuất cho mỗi đối tượng, truyền chuyển tiếp

Tính năng kết xuất theo luồng chuyển tiếp trên mỗi đối tượng xác định việc che khuất từng pixel của đối tượng trong chương trình đổ bóng Material của đối tượng đó. Nếu các pixel không hiển thị, chúng sẽ bị cắt bớt, thường là thông qua pha trộn alpha, do đó mô phỏng sự che khuất trên thiết bị của người dùng.

Kết xuất hai lượt

Với kết xuất hai lượt, lượt đầu tiên kết xuất tất cả nội dung ảo vào một vùng đệm trung gian. Vé thứ hai kết hợp cảnh ảo vào nền dựa trên sự khác biệt giữa chiều sâu trong thế giới thực và chiều sâu của cảnh ảo. Phương pháp này không yêu cầu bạn phải thực hiện thêm thao tác đổ bóng dành riêng cho từng đối tượng và thường tạo ra kết quả đồng nhất hơn so với phương thức chuyển tiếp.

Trích xuất khoảng cách từ hình ảnh đo chiều sâu

Để sử dụng API Độ sâu cho các mục đích khác ngoài việc che khuất các đối tượng ảo hoặc trực quan hoá dữ liệu độ sâu, hãy trích xuất thông tin từ hình ảnh độ sâu.

Texture2D _depthTexture;
short[] _depthArray;

void UpdateEnvironmentDepthImage()
{
  if (_occlusionManager &&
        _occlusionManager.TryAcquireEnvironmentDepthCpuImage(out XRCpuImage image))
    {
        using (image)
        {
            UpdateRawImage(ref _depthTexture, image, TextureFormat.R16);
            _depthWidth = image.width;
            _depthHeight = image.height;
        }
    }
  var byteBuffer = _depthTexture.GetRawTextureData();
  Buffer.BlockCopy(byteBuffer, 0, _depthArray, 0, byteBuffer.Length);
}

// Obtain the depth value in meters at a normalized screen point.
public static float GetDepthFromUV(Vector2 uv, short[] depthArray)
{
    int depthX = (int)(uv.x * (DepthWidth - 1));
    int depthY = (int)(uv.y * (DepthHeight - 1));

    return GetDepthFromXY(depthX, depthY, depthArray);
}

// Obtain the depth value in meters at the specified x, y location.
public static float GetDepthFromXY(int x, int y, short[] depthArray)
{
    if (!Initialized)
    {
        return InvalidDepthValue;
    }

    if (x >= DepthWidth || x < 0 || y >= DepthHeight || y < 0)
    {
        return InvalidDepthValue;
    }

    var depthIndex = (y * DepthWidth) + x;
    var depthInShort = depthArray[depthIndex];
    var depthInMeters = depthInShort * MillimeterToMeter;
    return depthInMeters;
}

Bước tiếp theo