Используйте Depth в своем приложении AR Foundation для Android

API глубины помогает камере устройства понять размер и форму реальных объектов в сцене. Он использует камеру для создания изображений глубины или карт глубины, тем самым добавляя уровень реализма AR в ваши приложения. Вы можете использовать информацию, предоставляемую изображением глубины, чтобы виртуальные объекты точно отображались перед объектами реального мира или позади них, обеспечивая захватывающий и реалистичный пользовательский опыт.

Информация о глубине рассчитывается на основе движения и может быть объединена с информацией от аппаратного датчика глубины, такого как датчик времени полета (ToF), если таковой имеется. Устройству не требуется датчик ToF для поддержки Depth API .

Предварительные условия

Прежде чем продолжить, убедитесь, что вы понимаете фундаментальные концепции AR и то, как настроить сеанс ARCore .

Настройте свое приложение так, чтобы оно было Depth Required или Depth Optional (только для Android)

Если вашему приложению требуется поддержка Depth API , либо потому, что основная часть AR-интерфейса зависит от глубины, либо потому, что для частей приложения, использующих глубину, не существует корректного резервного варианта, вы можете ограничить распространение своего приложения в Google Play. Сохраняйте на устройствах, поддерживающих Depth API .

Сделайте ваше приложение Depth Required

Перейдите в Edit > Project Settings > XR Plug-in Management > ARCore .

По умолчанию для Depth установлено значение Required .

Сделайте Depth Optional

  1. Перейдите в Edit > Project Settings > XR Plug-in Management > ARCore .

  2. В раскрывающемся меню Depth выберите Optional , чтобы установить для приложения параметр «Глубина» необязательно.

Включить глубину

Для экономии ресурсов ARCore по умолчанию не включает Depth API. Чтобы воспользоваться преимуществами глубины на поддерживаемых устройствах, необходимо вручную добавить компонент AROcclusionManager к игровому объекту AR Camera с помощью компонентов Camera и ARCameraBackground . Дополнительную информацию см. в разделе «Автоматическое перекрытие» в документации Unity.

В новом сеансе ARCore проверьте, поддерживает ли устройство пользователя глубину и Depth API, следующим образом:

// 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.
}

Получение изображений глубины

Получите последнее изображение глубины среды из 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.
    }
}

Вы можете преобразовать необработанный образ ЦП в RawImage для большей гибкости. Пример того, как это сделать, можно найти в примерах Unity ARFoundation .

Понимание значений глубины

Учитывая точку A на наблюдаемой реальной геометрии и двумерную точку a представляющую ту же точку на изображении глубины, значение, заданное Depth API в a , равно длине CA , проецируемой на главную ось. Это также можно назвать координатой Z A относительно начала координат камеры C . При работе с Depth API важно понимать, что значения глубины — это не длина самого CA луча, а его проекция .

Закрывайте виртуальные объекты и визуализируйте данные о глубине

Прочтите сообщение в блоге Unity, чтобы получить общий обзор данных глубины и того, как их можно использовать для перекрытия виртуальных изображений. Кроме того, образцы Unity ARFoundation демонстрируют перекрытие виртуальных изображений и визуализацию данных о глубине.

Вы можете визуализировать окклюзию, используя двухпроходный рендеринг или прямой рендеринг для каждого объекта. Эффективность каждого подхода зависит от сложности сцены и других особенностей приложения.

Пообъектный, прямой рендеринг

Для каждого объекта прямой рендеринг определяет перекрытие каждого пикселя объекта в его шейдере материала. Если пиксели не видны, они обрезаются, обычно посредством альфа-смешивания, имитируя таким образом окклюзию на устройстве пользователя.

Двухпроходный рендеринг

При двухпроходном рендеринге первый проход рендерит весь виртуальный контент в промежуточный буфер. Второй проход смешивает виртуальную сцену с фоном на основе разницы между глубиной реального мира и глубиной виртуальной сцены. Этот подход не требует дополнительной работы с шейдером, специфичным для объекта, и обычно дает более однородные результаты, чем метод прямого прохода.

Извлечь расстояние из изображения глубины

Чтобы использовать Depth API для целей, отличных от перекрытия виртуальных объектов или визуализации данных о глубине, извлеките информацию из изображения глубины.

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;
}

Что дальше

  • Обеспечьте более точное измерение с помощью API Raw Depth .
  • Посетите лабораторию ARCore Depth Lab , где демонстрируются различные способы доступа к данным о глубине.