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

Depth API giúp máy ảnh của thiết bị hiểu được kích thước và hình dạng của các vật thể thực trong một cảnh. Công cụ này sử dụng máy ảnh để tạo hình ảnh độ sâu hoặc bản đồ độ sâu, nhờ đó thêm một lớp thực tế AR vào ứng dụng của bạn. Bạn có thể sử dụng thông tin do hình ảnh độ sâu cung cấp để làm cho các đối tượng ảo xuất hiện chính xác ở phía trước hoặc phía sau các đối tượng trong thế giới thực, mang đến 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ể được 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ảm biến ToF để hỗ trợ Depth API.

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

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

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

Nếu ứng dụng của bạn yêu cầu hỗ trợ Depth API (API Độ sâu) vì một phần cốt lõi của trải nghiệm AR dựa vào độ 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 độ sâu, thì bạn có thể chọn hạn chế việc phân phối ứng dụng của mình trong Cửa hàng Google Play cho những thiết bị hỗ trợ Depth API.

Tạo ứng dụng Depth Required

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

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

Tạo ứng dụng 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 để đặt một ứng dụng thành Depth (Không bắt buộc).

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 thêm thành phần AROcclusionManager vào đối tượng trò chơi Máy ảnh AR theo cách thủ công bằng thành phần CameraARCameraBackground. Hãy xem phần Che khuất tự động trong tài liệu về Unity để biết thêm thông tin.

Trong một phiên ARCore mới, hãy kiểm tra xem thiết bị của người dùng có hỗ trợ độ sâu và Depth API 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 ảnh độ sâu

Nhận hình ảnh độ 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 các mẫu ARFoundation của Unity.

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

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

Che khuất các đối tượng ảo và trực quan hoá dữ liệu chiều sâu

Hãy xem bài đăng trên blog của Unity để biết thông tin tổng quan cấp cao về dữ liệu chiề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 hoạ việc che khuất hình ảnh ảo và trực quan hoá dữ liệu độ sâu.

Bạn có thể kết xuất vùng che khuất bằng cách kết xuất hai lần hoặc kết xuất từng đối tượng, kết xuất theo hướng 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 và các yếu tố cần cân nhắc khác dành riêng cho ứng dụng.

Kết xuất chuyển tiếp theo từng đối tượng

Quá trình kết xuất từng đối tượng, chuyển tiếp xác định mức che khuất của từng pixel của đối tượng trong chương trình đổ bóng chất liệu. Nếu không hiển thị, các pixel sẽ bị cắt, thường là thông qua tính năng kết hợp alpha, do đó mô phỏng tình trạng che khuất trên thiết bị của người dùng.

Kết xuất hai lần

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

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

Để sử dụng Depth API 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 chiều sâu, hãy trích xuất thông tin từ hình ảnh chiều 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