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 mogą być łączone z informacjami ze sprzętowego czujnika głębokości, np. z czujnika czasu lotu (ToF), jeśli jest dostępny. Urządzenie nie potrzebuje czujnika ToF do obsługi interfejsu Depth API.

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 zależy od głębi lub nie ma płynnego działania zastępczej dla części aplikacji, które wymagają głębi, możesz ograniczyć rozpowszechnianie aplikacji w Sklepie Google Play na urządzenia, które obsługują interfejs Depth API.

Zmień wersję aplikacji na Depth Required

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

Depth ma domyślną wartość Required.

Zmień wersję aplikacji na 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 domyślnie nie włącza 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 zaciemnienia.

W nowej sesji ARCore sprawdź, czy urządzenie użytkownika obsługuje głębokość i interfejs Depth API, wykonując te czynności:

// 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 dwuwymiarowego a reprezentującego ten sam punkt na obrazie głębi wartość zwracana przez Depth API w punkcie a jest równa długości wektora CA rzutowanego na oś główną. Można ją też określić jako współrzędną Z zmiennej A względem źródła 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 dwuprzebiegowe

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 bezpośredniego renderowania.

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

Aby używać interfejsu Depth API do celów innych niż zakrywanie wirtualnych obiektów lub wizualizowanie 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.