פיתוח באמצעות קונטיינרים מרחביים

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

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

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

מידע על קונטיינרים מרחביים

קונטיינר מרחבי הוא אזור תלת-ממדי מוגבל או לא מוגבל שמנוהל על ידי מערכת Android XR ומעובד על ידי אפליקציית OpenXR:

  • מצב מוגבל: האפליקציה מציעה מידות תלת-ממדיות (רוחב, גובה ועומק במטרים), והמערכת מקצה תיבה תוחמת תלת-ממדית בסביבה של המשתמש. האפליקציה מעבדת תוכן תלת-ממדי סטריאוסקופי בתוך הנפח, והמערכת חותכת את התוכן המעובד לפי הגבולות של התיבה התלת-ממדית.
  • מצב אימרסיבי: הקונטיינר המרחבי מתרחב לגבולות אינסופיים, ומעניק לאפליקציה את כל שדה הראייה של המשתמש, כמו סשן OpenXR מסורתי במסך מלא.
שלושה קונטיינרים מרחביים שפועלים בו-זמנית במצב מרחב מלא: קונטיינר כתום של שטח תלת-ממדי שנעול לרצפה, מודל תלת-ממדי כחול של ברווז עם ממשק משתמש דו-ממדי מצורף וקונטיינר ירוק של ממשק משתמש דו-ממדי.
תרשים 1. כמה קונטיינרים מרחביים במצב מרחב מלא: נפח שטח תלת-ממדי נעול לרצפה (משמאל), נפח ברווז תלת-ממדי בשילוב עם ממשק משתמש דו-ממדי (במרכז) וקונטיינר של ממשק משתמש דו-ממדי (מימין).

איך המשתמשים מבצעים אינטראקציה עם קונטיינרים מרחביים

מערכת Android XR מנהלת את פקדי החלונות של הקונטיינר המרחבי ואת המיקום שלו במרחב הפיזי, ומספקת מודל אינטראקציה עקבי בכל האפליקציות:

  • הזזה ושינוי מיקום: המשתמשים יכולים להזיז את המכל המרחבי באמצעות תנועות ידיים (כמו צביטה) או בקרי תנועה כדי לשנות את המיקום שלו במרחב התלת-ממדי.
  • מזעור וסגירה: פקדי החלונות של המערכת מאפשרים למשתמשים למזער או לסגור קונטיינרים מרחביים ספציפיים. כשהמשתמש סוגר קונטיינר, המערכת מודיעה לאפליקציה כדי שהיא תוכל לפנות משאבים.
  • נעילה לרצפה באמצעות הבנת הסצנה: כשהאפליקציה מצהירה על ההרשאה android.permission.SCENE_UNDERSTANDING_COARSE ומבקשת אותה, המערכת מפעילה באופן אוטומטי את ההצמדה למשטח כדי שהמשתמשים יוכלו למקם ולנעול קונטיינר מרחבי לרצפה הפיזית בלי הגדרה נוספת של האפליקציה, כפי שמוצג בקונטיינר הכתום של השטח באיור 1.
  • אינטראקציה ישירה בתלת-ממד: כשהמיכל ממוקד ואינטראקטיבי, המשתמשים יכולים ליצור אינטראקציה ישירות עם התוכן התלת-ממדי בתוך הנפח באמצעות מעקב ידיים, מבט או בקרי 6DoF.
משתמש שמבצע אינטראקציה עם ברווז תלת-ממדי בתוך קונטיינר מרחבי כחול מוגבל באמצעות מעקב ידיים.
איור 2. משתמש באינטראקציה עם תוכן תלת-ממדי בתוך קונטיינר מרחבי מוגבל באמצעות מעקב ידיים.
  • מעבר בין מצב מוגבל למצב אימרסיבי: המשתמש או האפליקציה יכולים להעביר קונטיינר בין תיבת תלת-ממד מוגבלת לבין תצוגה אימרסיבית במסך מלא ללא הגבלה, בלי להפעיל מחדש את סשן OpenXR.
סצנת הברווז בתלת-ממד אחרי המעבר ממכל מרחבי מוגבל לתצוגה מציאותית במסך מלא ללא הגבלה.
תרשים 3. הקונטיינר המרחבי אחרי המעבר ממצב מוגבל למצב אימרסיבי, שממלא את שדה הראייה של המשתמש.
  • מצב מעקב תוך כדי תנועה: המערכת שולטת במיקום של כל מרחב קואורדינטות של קונטיינר מרחבי (xrCreateSpatialContainerSpaceEXT), ולכן המשתמשים יכולים להפעיל את מצב המעקב דרך פקדי החלונות של המערכת. כך קונטיינר תלת-ממדי מוגבל עוקב אחריהם באופן אוטומטי ונשאר בשדה הראייה שלהם בזמן שהם מסתובבים, בלי צורך בהגדרות נוספות באפליקציה.

איך משתמשים בקונטיינרים מרחביים ב-Android XR

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

התאמת תוכן תלת-ממדי לגבולות של מאגר תגים

במצב מוגבל, נקודת המוצא (0, 0, 0) של המרחב של מאגר המידע המרחבי (xrCreateSpatialContainerSpaceEXT) נמצאת במרכז התיבה התוחמת (bounding box) התלת-ממדית. המערכת מנהלת את המיקום של מרחב המאגר בסביבה של המשתמש, ולכן האפליקציה מעבדת את התוכן התלת-ממדי ביחס ל-(0, 0, 0), והמערכת מטפלת בשינוי המיקום של המשתמש ובמצב המעקב. המערכת חותכת כל גיאומטריה שמוצגת מחוץ לרוחב, לגובה או לעומק של התיבה, כדי שהתוכן יישאר בנפח שהוקצה לו.

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

תגובה לשינויים במצב המאגר

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

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

שימוש בשקיפות עם מיזוג אלפא במצב מוגבל

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

בדיקת הזמינות של התכונה והגדרת המניפסט

כדי להשתמש ב-Spatial Containers, צריך מכשיר Android XR שתומך ב-Spatial API ברמה 4 ומעלה. במקום לבדוק ישירות את רמת ה-API של Android, שולחים שאילתה לזמן הריצה של OpenXR כדי לקבוע אם יש תמיכה במאגרים מרחביים. כדי לעשות את זה, מפעילים את xrEnumerateInstanceExtensionProperties כדי לוודא ש-XR_EXT_spatial_container ו-XR_EXT_spatial_container_self_rendering זמינים, ומפעילים את xrGetSystemProperties עם XrSystemSpatialContainerPropertiesEXT כדי לבדוק את supportsBounded ואת supportsImmersive.

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

הפעלת מצב "מרחב מלא" בקובץ AndroidManifest.xml

כדי להשתמש בקונטיינרים מרחביים, צריך להפעיל את מצב "מרחב מלא מנוהל", גם באפליקציות OpenXR עצמאיות. אפליקציות OpenXR רגילות במסך מלא מצהירות על XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED כדי לקבל שליטה בלעדית על התצוגה בלי ניהול חלונות המערכת. לעומת זאת, אפליקציה שמשתמשת בקונטיינרים מרחביים מסתמכת על הקומפוזיטור של מערכת Android XR כדי לנהל נפחים מוגבלים בתלת-ממד, פקדי חלונות ומיקום מרחבי במצב "מרחב מלא", וצריך להגדיר את android.window.PROPERTY_XR_ACTIVITY_START_MODE לערך XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application ...>
        <!-- Spatial Containers require Full Space Managed mode -->
        <property
            android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
            android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED" />

        <activity android:name=".MainActivity" ...>
            <!-- Optional hint declaring which bounds modes the activity uses -->
            <property
                android:name="org.khronos.openxr.PROPERTY_ACTIVITY_HINT_XR_SPATIAL_CONTAINER_USED_BOUNDS_MODES"
                android:value="XR_SPATIAL_CONTAINER_BOUNDS_MODE_BOUNDED_EXT|XR_SPATIAL_CONTAINER_BOUNDS_MODE_IMMERSIVE_EXT" />
        </activity>
    </application>
</manifest>

הצהרה על הרשאות ספציפיות לתכונות ובקשת הרשאות כאלה

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

  • כדי לנעול ולעגן קונטיינר מרחבי לרצפה הפיזית באמצעות הבנת הסצנה, צריך להצהיר על ההרשאה המסוכנת android.permission.SCENE_UNDERSTANDING_COARSE ולבקש אותה בזמן הריצה.
  • כדי לעקוב אחרי תנוחת הראש של המשתמש ב-OpenXR, צריך להצהיר על android.permission.HEAD_TRACKING ולבקש אותה.
<uses-permission android:name="android.permission.HEAD_TRACKING" />
<uses-permission android:name="android.permission.SCENE_UNDERSTANDING_COARSE" />

במאמר הסבר על הרשאות ל-XR מוסבר איך להצהיר על הרשאות XR מסוכנות ולבקש אותן בזמן הריצה.

הפעלת קונטיינרים מרחביים בפרויקט

אתם יכולים ליצור קונטיינרים מרחביים באמצעות Unity,‏ Godot או ישירות באמצעות OpenXR ב-C++‎. בחרו כרטיסייה כדי לראות את הדרישות המוקדמות, הגדרת העורך ודוגמת הקוד לסביבה המועדפת עליכם:

Unity

דרישות מוקדמות

הגדרת הפרויקט

  1. עוברים אל Edit > הגדרות הפרויקט > XR Plug-in Management > OpenXR.
  2. לוחצים על Settings (הגדרות) לצד Android XR (Extensions): Session Management (Android XR (תוספים): ניהול סשנים) ומבטלים את הבחירה באפשרות Immersive XR (XR סוחף).
  3. ברשימה OpenXR Feature Groups, מפעילים את האפשרות Spatial Container.

אפשר להשתמש ב-XRSpatialContainerFeature ב-C# ‎ כדי לשלוח שאילתות לגבי גבולות, להגיב לאירועים בקונטיינר ולעבור בין מצב מוגבל למצב אימרסיבי:

void OnEnable()
{
    XRSpatialContainerFeature.OnSpatialContainerBoundsChanged += OnBoundsChanged;
}

private void OnBoundsChanged(Vector3 bounds, bool infiniteBounds)
{
    float minScale = Mathf.Min(bounds.x, bounds.y, bounds.z);
    transform.localScale = infiniteBounds ? Vector3.one : Vector3.one * minScale;
}

public void SetImmersiveMode(bool immersive)
{
    XRSpatialContainerFeature.TryRequestBoundsMode(immersive);
}

במאמר שימוש בקונטיינרים מרחביים ב-Unity מוסבר איך להגדיר את הפרויקט, המניפסט וההרשאות.

Godot

דרישות מוקדמות

הגדרת הפרויקט

  1. עוברים אל Project > הגדרות הפרויקט, מפעילים את Advanced Settings, ועוברים אל General > XR > OpenXR > Extensions.
  2. בסעיף המשנה Spatial Container, בוחרים באפשרות Enabled ומגדירים את Bounds Mode ואת Bounds.
  3. עוברים אל Project > Export...‎ (פרויקט > ייצוא...‎) > Android ובוחרים באפשרות Use Experimental Features בקטע Android XR Features.

משתמשים ב-singleton‏ OpenXRSpatialContainerExtension ב-GDScript כדי להגיב לשינויים בגבולות ובמצב של הקונטיינר:

func _ready() -> void:
    if OpenXRSpatialContainerExtension.is_enabled():
        OpenXRSpatialContainerExtension.spatial_container_bounds_changed.connect(
            _on_bounds_changed
        )

func _on_bounds_changed(_rid: RID, bounds: Vector3) -> void:
    scale = Vector3.ONE * min(bounds.x, bounds.y, bounds.z)

func set_immersive_mode() -> void:
    OpenXRSpatialContainerExtension.request_main_spatial_container_bounds_mode(
        OpenXRSpatialContainerState.IMMERSIVE
    )

כדי לראות את הגדרות הפרויקט המלאות, הרשאות הייצוא ודוגמאות ל-GDScript, קראו את המאמר שימוש בקונטיינרים מרחביים ב-Godot.

OpenXR

דרישות מוקדמות

מפעילים את XR_EXT_spatial_container ואת XR_EXT_spatial_container_self_rendering כשיוצרים את XrInstance, ואז יוצרים ומעבדים את הקונטיינר המרחבי:

// 1. Enable the Spatial Container extensions during xrCreateInstance
const char* enabledExtensions[] = {
    XR_EXT_SPATIAL_CONTAINER_EXTENSION_NAME,
    XR_EXT_SPATIAL_CONTAINER_SELF_RENDERING_EXTENSION_NAME,
};

// 2. Configure and create a self-rendering spatial container
XrSpatialContainerConfigurationSelfRenderingEXT selfRenderingConfig{
    .type = XR_TYPE_SPATIAL_CONTAINER_CONFIGURATION_SELF_RENDERING_EXT,
    .next = nullptr,
    .viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO,
    .suggestedBounds = {1.5f, 1.5f, 1.5f}, // Width, height, depth in meters
};

XrSpatialContainerCreateInfoEXT createInfo{
    .type = XR_TYPE_SPATIAL_CONTAINER_CREATE_INFO_EXT,
    .next = &selfRenderingConfig,
    .spatialContainerConfig =
        reinterpret_cast<const XrSpatialContainerConfigurationBaseHeaderEXT*>(
            &selfRenderingConfig),
};

XrSpatialContainerEXT spatialContainer = XR_NULL_HANDLE;
xrCreateSpatialContainerEXT(session, &createInfo, &spatialContainer);

// 3. Request bounded mode and make the container visible
xrRequestSpatialContainerBoundsModeEXT(
    spatialContainer, XR_SPATIAL_CONTAINER_BOUNDS_MODE_BOUNDED_EXT);
xrRequestSpatialContainerVisibilityEXT(spatialContainer, XR_TRUE);

עוד באותו הקשר


‫OpenXR™‎ והלוגו של OpenXR הם סימנים מסחריים בבעלות The Khronos Group Inc. ‎, והם רשומים כסימנים מסחריים בסין, באיחוד האירופי, ביפן ובבריטניה.