שימוש ב'עומק' באפליקציית AR Foundation ל-Android

Depth 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 ו-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. דוגמה לכך מופיעה בדוגמאות ל-ARFoundation של Unity.

הסבר על ערכי עומק

בהינתן נקודה A בגיאומטריה של העולם האמיתי שנצפתה ונקודה דו-ממדית a שמייצגת את אותה נקודה בתמונת העומק, הערך שמקבלים מ-Depth API ב-a שווה לאורך של CA שמוצג על הציר הראשי. אפשר גם להתייחס לזה כאל קואורדינטת z של A ביחס למקור המצלמה C. כשעובדים עם Depth API, חשוב להבין שערכי העומק הם לא אורך קרן CA עצמה, אלא הקרנה שלה.

חסימה של אובייקטים וירטואליים והצגה חזותית של נתוני עומק

בפוסט הזה בבלוג של Unity מופיעה סקירה כללית על נתוני עומק ועל האופן שבו אפשר להשתמש בהם כדי להסתיר תמונות וירטואליות. בנוסף, בדוגמאות של ARFoundation ב-Unity מוצגות תמונות וירטואליות חסרות ונתוני עומק מוצגים באופן חזותי.

אפשר לבצע רינדור של חסימות באמצעות רינדור בשני מעברים או רינדור קדימה לכל אובייקט. היעילות של כל גישה תלויה במורכבות הסצנה ובשיקולים אחרים שספציפיים לאפליקציה.

עיבוד נתונים קדימה לכל אובייקט

עיבוד נתונים (render) קדימה לכל אובייקט קובע את החסימה של כל פיקסל של האובייקט בשיחזור החומר שלו. אם הפיקסלים לא גלויים, הם נחתכים, בדרך כלל באמצעות מיזוג אלפא, וכך מתבצעת הדמיה של חסימת ראייה במכשיר של המשתמש.

רינדור בשני מעברים

כשמשתמשים ברינדור בשני מעברים, במעבר הראשון כל התוכן הווירטואלי מומר למאגר ביניים. במעבר השני, הסצינה הווירטואלית מודגשת על הרקע על סמך ההבדל בין העומק בעולם האמיתי לעומק של הסצינה הווירטואלית. הגישה הזו לא דורשת עבודה נוספת על שגיאות ספציפיות לאובייקטים, ובדרך כלל היא מניבה תוצאות אחידות יותר מאשר השיטה של העברה קדימה.

חילוץ המרחק מתמונת עומק

כדי להשתמש ב-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;
}

מה השלב הבא?