Używanie głębi w aplikacji AR Foundation na Androida

Interfejs Depth API pomaga kamerze urządzenia określić rozmiar i kształt rzeczywistych obiektów w scenie. Używa ona aparatu do tworzenia obrazów głębi, czyli map głębi, dzięki czemu dodaje do Twoich aplikacji warstwę realizmu AR. Korzystając z informacji zawartych w zdjęciu głębi, możesz sprawić, że obiekty wirtualne będą dokładnie widoczne przed lub za obiektami rzeczywistymi, co zapewni użytkownikom realistyczne wrażenia.

Informacje o głębi są obliczane na podstawie ruchu i można je łączyć z informacjami z czujnika głębi sprzętowej, takiego jak czujnik czasu przelotu (ToF), jeśli jest dostępny. Aby obsługiwać Depth API, urządzenie nie musi mieć czujnika ToF.

Wymagania wstępne

Zanim przejdziesz dalej, upewnij się, że rozumiesz podstawowe zagadnienia związane z rozszerzoną rzeczywistością oraz że wiesz, jak skonfigurować sesję ARCore.

Skonfiguruj aplikację jako Depth Required lub Depth Optional (tylko na urządzeniach z Androidem)

Jeśli Twoja aplikacja wymaga obsługi interfejsu Depth API, ponieważ główna część funkcji AR opiera się na głębi lub ponieważ nie ma łagodnego rozwiązania zastępczego dla części aplikacji, które wykorzystują głębię, możesz ograniczyć dystrybucję aplikacji w Sklepie Google Play do urządzeń obsługujących Depth API.

Zmień typ aplikacji na Depth Required

Wejdź na Edit > Project Settings > XR Plug-in Management > ARCore.

Domyślnie Depth jest ustawione na Required.

Utwórz aplikację Depth Optional

  1. Wejdź na Edit > Project Settings > XR Plug-in Management > ARCore.

  2. Aby ustawić opcję Depth na opcjonalną, w menu Depth kliknij Optional.

Włączanie opcji Głębia

Aby oszczędzać zasoby, ARCore nie włącza domyślnie interfejsu Depth API. Aby korzystać z głębi na obsługiwanych urządzeniach, musisz ręcznie dodać komponent AROcclusionManager do obiektu gry AR Camera za pomocą komponentów CameraARCameraBackground. Więcej informacji znajdziesz w dokumentacji Unity na temat automatycznego zaciemniania.

nowej sesji ARCore sprawdź, czy urządzenie użytkownika obsługuje głębię i interfejs 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.
}

Pobieranie obrazów głębi

Pobierz najnowszy obraz głębi środowiska z 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.
    }
}

Aby uzyskać większą elastyczność, możesz przekonwertować obraz w formacie RAW na RawImage. Przykład tego, jak to zrobić, znajdziesz w próbkach ARFoundation w Unity.

Wartości głębokości

Dla punktu A w obserwowanej geometrii świata rzeczywistego i punktu 2D a reprezentującego ten sam punkt na obrazie głębi wartość zwracana przez Depth API w punkcie a jest równa długości CA rzutowanej na oś główną. Może być to też współrzędna z elementu A względem punktu początkowego kamery C. Podczas pracy z interfejsem Depth API należy pamiętać, że wartości głębi to nie długość promienia CA, ale jego projekcja.

zaciemniać wirtualne obiekty i wizualizować dane głębi;

Aby uzyskać ogólne informacje na temat danych o głębi i sposobów ich wykorzystania do zasłaniania obrazów wirtualnych, przeczytaj ten post na blogu. Dodatkowo w przykładach ARFoundation w Unity znajdziesz przykłady zasłaniania obrazów wirtualnych i wizualizacji danych głębi.

Możesz renderować zasłonięcie, korzystając z renderowania w 2 przelotach lub renderowania do przodu dla poszczególnych obiektów. Skuteczność każdego podejścia zależy od złożoności sceny i innych czynników związanych z aplikacją.

Rendering w kierunku do przodu dla poszczególnych obiektów

Rendering w przód dla poszczególnych obiektów określa zasłonięcie każdego piksela obiektu w jego shaderze materiału. Jeśli piksele są niewidoczne, są one przycinane, zwykle przez mieszanie alfa, co symuluje zasłonięcie na urządzeniu użytkownika.

Renderowanie w 2 przelotach

W przypadku renderowania w 2 przelotach pierwszy przelot renderuje całą zawartość wirtualną do pośredniego bufora. Drugi przejazd łączy wirtualną scenę z tłem na podstawie różnicy między rzeczywistą głębią a głębią wirtualnej sceny. To podejście nie wymaga dodatkowej pracy nad shaderami dla poszczególnych obiektów i zwykle daje bardziej jednolite wyniki niż metoda przejść bezpośrednich.

Wyodrębnianie odległości z obrazu głębi

Aby używać Depth API do celów innych niż zasłanianie wirtualnych obiektów lub wizualizacja danych głębi, wyodrębnij informacje z obrazu głębi.

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

Co dalej?

  • Umożliwia dokładniejsze wykrywanie za pomocą interfejsu Raw Depth API.
  • Zapoznaj się z ARCore Depth Lab, w którym znajdziesz różne sposoby uzyskiwania dostępu do danych głębi.