استخدام واجهة برمجة التطبيقات ARCore Depth API للحصول على تجارب الواقع المعزّز الغامرة

1. قبل البدء

ARCore هي منصة لإنشاء تطبيقات الواقع المعزّز (AR) على الأجهزة الجوّالة. وباستخدام واجهات برمجة تطبيقات مختلفة، يتيح ARCore لجهاز المستخدم مراقبة المعلومات حول بيئتها واستلامها والتفاعل مع تلك المعلومات.

في هذا الدرس التطبيقي حول الترميز، ستتعرَّف على عملية إنشاء تطبيق بسيط مزوّد بتقنية الواقع المعزّز ويستخدم واجهة ARCore Depth API.

المتطلبات الأساسية

تمت كتابة هذا الدرس التطبيقي حول الترميز للمطوِّرين الذين لديهم معرفة بمفاهيم الواقع المعزّز الأساسية.

ما الذي ستنشئه

1a0236e93212210c.gif

ستنشئ تطبيقًا يستخدم صورة العمق لكل إطار لتصوير هندسة المشهد وتطبيق التظليل على الأصول الافتراضية الموضوعة. سوف تمر عبر الخطوات المحددة الخاصة بما يلي:

  • جارٍ البحث عن دعم Depth API على الهاتف
  • استرداد صورة العمق لكل إطار
  • تصور معلومات العمق بطرق متعددة (انظر الصورة المتحركة أعلاه)
  • استخدام العمق لإضفاء الحيوية على التطبيقات بطريقة واقعية
  • التعرّف على كيفية التعامل مع الهواتف التي لا تتوافق مع Depth API بسلاسة

المتطلبات

متطلبات الأجهزة

  • جهاز ARCore متوافق ومتصل بجهاز التطوير عبر كابل USB. يجب أن يتيح هذا الجهاز أيضًا استخدام واجهة برمجة التطبيقات Depth API. يُرجى الاطّلاع على قائمة الأجهزة المتوافقة هذه. لا تتوفّر واجهة Depth API إلا على أجهزة Android.
  • تفعيل تصحيح أخطاء USB لهذا الجهاز.

متطلبات البرامج

2. ARCore وDepth API

تستخدم واجهة Depth API كاميرا RGB متوافقة مع جهاز متوافق لإنشاء خرائط بالعمق (تُعرف أيضًا باسم صور العمق). يمكنك استخدام المعلومات التي تقدّمها خريطة العمق لإظهار الكائنات الافتراضية بدقة أمام أو خلف كائنات من العالم الحقيقي، ما يتيح للمستخدمين تجربة غامرة وواقعية.

توفّر واجهة برمجة التطبيقات ARCore Depth API إمكانية الوصول إلى صور متعمقة مطابقة لكل إطار توفّره جلسة ARCore. توفِّر كل وحدة بكسل قياسًا للمسافة بين الكاميرا والبيئة المحيطة بك، ما يحسّن الواقع الافتراضي لتطبيق الواقع المعزّز.

إحدى الإمكانات الرئيسية التي تكمن وراء واجهة برمجة التطبيقات Depth API هي ميزة التظليل، وهي قدرة العناصر الرقمية على الظهور بدقة بالنسبة إلى عناصر العالم الحقيقي. وهذا يجعل الكائنات تبدو كما لو كانت بالفعل في البيئة مع المستخدم.

سيرشدك هذا الدرس التطبيقي في إطار عملية إنشاء تطبيق بسيط مزوّد بتقنية الواقع المعزّز يستخدم صورًا متعمقة لإخفاء الأجسام الافتراضية خلف أسطح حقيقية وتوضيح الشكل الهندسي المكتشف للفضاء.

3- الإعداد

إعداد جهاز التطوير

  1. وصِّل جهاز ARCore بجهاز الكمبيوتر باستخدام كابل USB. تأكد من أن جهازك يسمح بتصحيح أخطاء USB.
  2. افتح نافذة طرفية وشغِّل adb devices، كما هو موضّح أدناه:
adb devices

List of devices attached
<DEVICE_SERIAL_NUMBER>    device

ستكون <DEVICE_SERIAL_NUMBER> سلسلة فريدة لجهازك. تأكّد من ظهور جهاز واحد بالضبط قبل المتابعة.

نزِّل Code وثبِّته.

  1. يمكنك استنساخ المستودع:
git clone https://github.com/googlecodelabs/arcore-depth

أو يمكنك تنزيل ملف ZIP واستخراجه:

  1. افتح "استوديو Android" وانقر على فتح مشروع حالي في "استوديو Android".
  2. ابحث عن الدليل الذي استخرجت فيه ملف ZIP الذي تم تنزيله أعلاه، وافتح الدليل depth_codelab_io2020.

وهذا مشروع واحد من مشروع Gradle الذي يحتوي على وحدات متعددة. إذا لم يكن جزء "المشروع" في أعلى يمين "استوديو Android" معروضًا حاليًا في لوحة "المشروع"، انقر على المشاريع من القائمة المنسدلة.

يُفترض أن تبدو النتيجة على النحو التالي:

يحتوي هذا المشروع على الوحدات التالية:

  • part0_work: تطبيق إجراء التفعيل ويجب إجراء تعديلات على هذه الوحدة عند القيام بهذا الدرس التطبيقي حول الترميز.
  • part1: رمز مرجعي لما يجب أن تبدو عليه تعديلاتك عند إكمال الجزء الأول.
  • part2: الرمز المرجعي عند إكمال الجزء الثاني.
  • part3: الرمز المرجعي عند إكمال الجزء الثالث.
  • part4_completed: الإصدار النهائي من التطبيق يمكنك إنشاء رمز مرجعي عند إكمال "الجزء الرابع" وهذا الدرس التطبيقي حول الترميز.

يمكنك استخدام وحدة "part0_work". هناك أيضًا حلول كاملة لكل جزء من الدرس التطبيقي حول الترميز. كل وحدة هي تطبيق قابل للإنشاء.

4. تشغيل تطبيق إجراء التفعيل

  1. انقر على تشغيل > تشغيل... &gt; "part0_work" (العمل_القطع). في مربّع الحوار اختيار هدف النشر الذي يظهر، يجب أن يكون جهازك مدرجًا ضمن الأجهزة المتصلة.
  2. اختَر جهازك وانقر على حسنًا. سينشئ Android Studio التطبيق الأولي ويشغِّله على جهازك.
  3. سيطلب التطبيق أذونات الوصول إلى الكاميرا. انقر على سماح للمتابعة.

c5ef65f7a1da0d9.png

كيفية استخدام التطبيق

  1. حرِّك الجهاز لمساعدة التطبيق في العثور على طائرة. تشير الرسالة في أسفل الصفحة إلى الوقت المناسب لمواصلة التحرك.
  2. انقر على مكان على الطائرة لوضع ارتساء. وسيتم رسم شكل Android في مكان وضع المرساء. لا يتيح لك هذا التطبيق سوى وضع إعلان ثابت واحد في المرة الواحدة.
  3. حرِّك الجهاز. من المفترض أن يظل الشكل في المكان نفسه على الرغم من تحرك الجهاز.

تطبيقك حاليًا بسيط جدًا ولا يعرف الكثير عن هندسة المشهد في العالم الحقيقي.

فمثلاً، إذا وضعت جهاز Android خلف كرسي، سيظهر العرض وكأنّه يتحرك في الأمام، لأنّ التطبيق لا يعرف أنّ الكرسي موجود ويجب أن يخفي جهاز Android.

6182cf62be13cd97.png beb0d327205f80ee.png e4497751c6fad9a7.png

لحلّ هذه المشكلة، سنستخدم واجهة برمجة التطبيقات Depth API لتحسين مستوى التجربة الشمولية والواقعية في هذا التطبيق.

5- التحقّق مما إذا كانت واجهة برمجة التطبيقات Depth API متوافقة (الجزء 1)

لا تعمل واجهة برمجة التطبيقات ARCore Depth API إلا على مجموعة فرعية من الأجهزة المتوافقة. قبل دمج الوظائف في أحد التطبيقات باستخدام هذه الصور المتعمقة، يجب أولاً التأكد من أن التطبيق يعمل على جهاز متوافق.

إضافة عضو خاص جديد إلى DepthCodelabActivity والذي يعمل كعلامة تخزِّن ما إذا كان الجهاز الحالي يوفّر بيانات العمق:

private boolean isDepthSupported;

يمكننا تعبئة هذه العلامة من داخل الدالة onResume()، حيث يتم إنشاء جلسة جديدة.

ابحث عن الرمز الحالي:

// Creates the ARCore session.
session = new Session(/* context= */ this);

تعديل الرمز إلى:

// Creates the ARCore session.
session = new Session(/* context= */ this);
Config config = session.getConfig();
isDepthSupported = session.isDepthModeSupported(Config.DepthMode.AUTOMATIC);
if (isDepthSupported) {
  config.setDepthMode(Config.DepthMode.AUTOMATIC);
} else {
  config.setDepthMode(Config.DepthMode.DISABLED);
}
session.configure(config);

الآن، تم ضبط جلسة الواقع المعزّز بشكل مناسب، ويعرف تطبيقك ما إذا كان يمكنه استخدام الميزات المستنِدة إلى العمق أم لا.

كما يجب عليك إخبار المستخدم بما إذا كان سيتم استخدام العمق لهذه الجلسة أم لا.

أضِف رسالة أخرى إلى Snackbar. سيظهر في أسفل الشاشة:

// Add this line at the top of the file, with the other messages.
private static final String DEPTH_NOT_AVAILABLE_MESSAGE = "[Depth not supported on this device]";

في "onDrawFrame()"، يمكنك تقديم هذه الرسالة حسب الحاجة:

// Add this if-statement above messageSnackbarHelper.showMessage(this, messageToShow).
if (!isDepthSupported) {
  messageToShow += "\n" + DEPTH_NOT_AVAILABLE_MESSAGE;
}

إذا تم تشغيل تطبيقك على جهاز لا يتوافق مع ميزة العمق، ستظهر الرسالة التي أضفتها للتو في أسفل الصفحة:

5c878a7c27833cb2.png

بعد ذلك، يجب تحديث التطبيق لطلب واجهة برمجة التطبيقات Depth API واسترداد صور العمق لكل إطار.

6- استرداد صور العمق (الجزء 2)

تلتقط واجهة برمجة التطبيقات Depth API ملاحظات ثلاثية الأبعاد لبيئة الجهاز وتعرض صورة عمق مع هذه البيانات إلى تطبيقك. تمثل كل وحدة بكسل في الصورة بالعمق قياس مسافة بين كاميرا الجهاز وبيئته الحقيقية.

ستستخدم الآن هذه الصور المتعمقة لتحسين العرض والتصور في التطبيق. تتمثل الخطوة الأولى في استرداد صورة العمق لكل إطار وربط هذا الزخرفة باستخدام وحدة معالجة الرسومات.

عليك أولاً إضافة فئة جديدة إلى المشروع.
DepthTextureHandler يكون المسؤول عن استرداد صورة العمق لإطار ARCore معيّن.
أضف هذا الملف:

be8d14dfe9656551.png

src/main/java/com/google/ar/core/codelab/depth/DepthTextureHandler.java

package com.google.ar.core.codelab.depth;

import static android.opengl.GLES20.GL_CLAMP_TO_EDGE;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
import static android.opengl.GLES20.GL_TEXTURE_WRAP_S;
import static android.opengl.GLES20.GL_TEXTURE_WRAP_T;
import static android.opengl.GLES20.GL_UNSIGNED_BYTE;
import static android.opengl.GLES20.glBindTexture;
import static android.opengl.GLES20.glGenTextures;
import static android.opengl.GLES20.glTexImage2D;
import static android.opengl.GLES20.glTexParameteri;
import static android.opengl.GLES30.GL_LINEAR;
import static android.opengl.GLES30.GL_RG;
import static android.opengl.GLES30.GL_RG8;

import android.media.Image;
import com.google.ar.core.Frame;
import com.google.ar.core.exceptions.NotYetAvailableException;

/** Handle RG8 GPU texture containing a DEPTH16 depth image. */
public final class DepthTextureHandler {

  private int depthTextureId = -1;
  private int depthTextureWidth = -1;
  private int depthTextureHeight = -1;

  /**
   * Creates and initializes the depth texture. This method needs to be called on a
   * thread with a EGL context attached.
   */
  public void createOnGlThread() {
    int[] textureId = new int[1];
    glGenTextures(1, textureId, 0);
    depthTextureId = textureId[0];
    glBindTexture(GL_TEXTURE_2D, depthTextureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  }

  /**
   * Updates the depth texture with the content from acquireDepthImage16Bits().
   * This method needs to be called on a thread with an EGL context attached.
   */
  public void update(final Frame frame) {
    try {
      Image depthImage = frame.acquireDepthImage16Bits();
      depthTextureWidth = depthImage.getWidth();
      depthTextureHeight = depthImage.getHeight();
      glBindTexture(GL_TEXTURE_2D, depthTextureId);
      glTexImage2D(
          GL_TEXTURE_2D,
          0,
          GL_RG8,
          depthTextureWidth,
          depthTextureHeight,
          0,
          GL_RG,
          GL_UNSIGNED_BYTE,
          depthImage.getPlanes()[0].getBuffer());
      depthImage.close();
    } catch (NotYetAvailableException e) {
      // This normally means that depth data is not available yet.
    }
  }

  public int getDepthTexture() {
    return depthTextureId;
  }

  public int getDepthWidth() {
    return depthTextureWidth;
  }

  public int getDepthHeight() {
    return depthTextureHeight;
  }
}

ستضيف الآن مثيلاً لهذه الفئة إلى DepthCodelabActivity، لضمان الحصول على نسخة يسهل الوصول إليها من صورة العمق لكل إطار.

في DepthCodelabActivity.java، أضِف مثيلاً للفئة الجديدة كمتغيّر عضو خاص:

private final DepthTextureHandler depthTexture = new DepthTextureHandler();

بعد ذلك، عليك تعديل طريقة onSurfaceCreated() لإعداد هذه الزخرفة كي تصبح قابلة للاستخدام من خلال أدوات تظليل وحدة معالجة الرسومات:

// Put this at the top of the "try" block in onSurfaceCreated().
depthTexture.createOnGlThread();

أخيرًا، تريد تعبئة هذا الزخرفة في كل إطار بأحدث صورة بعمق أكبر، ويمكن القيام بذلك من خلال استدعاء طريقة update() التي أنشأتها أعلاه على أحدث إطار تم استرداده من session.
بما أنّ استخدام ميزة التعرّف على العمق اختياري لهذا التطبيق، يُرجى عدم استخدام هذه المكالمة إلا إذا كنت تستخدم ميزة التعرّف على العمق.

// Add this just after "frame" is created inside onDrawFrame().
if (isDepthSupported) {
  depthTexture.update(frame);
}

أصبحت لديك الآن صورة بعمق يتم تحديثها مع كل إطار. بإمكانك الآن استخدام أدوات التظليل.

ومع ذلك، لم يحدث أي تغيير في سلوك التطبيق بعد. ستستخدم الآن صورة العمق لتحسين تطبيقك.

7. عرض صورة العمق (الجزء 3)

الآن لديك صورة عميقة لتلعب بها، ستحتاج إلى معرفة الشكل الذي تبدو عليه. وفي هذا القسم، ستضيف زرًا إلى التطبيق لعرض عمق كل إطار.

إضافة أدوات تظليل جديدة

هناك العديد من الطرق لعرض صورة بعمق. توفر أدوات التظليل التالية تصورًا بسيطًا لرسم خرائط الألوان.

إضافة أداة تظليل .فير

في "استوديو Android"، اتّبِع الخطوات التالية:

  1. أولاً، عليك إضافة أدوات تظليل .vert و.frag جديدة إلى الدليل src/main/assets/shaders/.
  2. انقر بزر الماوس الأيمن على دليل أدوات التظليل.
  3. اختَر "جديد" -> الملف
  4. تسمية المكان "background_show_depth_map.vert"
  5. اضبطه كملف نصي.

في الملف الجديد، أضِف الرمز التالي:

src/main/assets/shaders/background_show_depth_map.vert

attribute vec4 a_Position;
attribute vec2 a_TexCoord;

varying vec2 v_TexCoord;

void main() {
   v_TexCoord = a_TexCoord;
   gl_Position = a_Position;
}

كرِّر الخطوات أعلاه لإنشاء برنامج تظليل الأجزاء في الدليل نفسه، وأدخِل اسمًا له background_show_depth_map.frag.

أضِف الرمز التالي إلى هذا الملف الجديد:

src/main/assets/shaders/background_show_depth_map.frag

precision mediump float;
uniform sampler2D u_Depth;
varying vec2 v_TexCoord;
const highp float kMaxDepth = 20000.0; // In millimeters.

float GetDepthMillimeters(vec4 depth_pixel_value) {
  return 255.0 * (depth_pixel_value.r + depth_pixel_value.g * 256.0);
}

// Returns an interpolated color in a 6 degree polynomial interpolation.
vec3 GetPolynomialColor(in float x,
  in vec4 kRedVec4, in vec4 kGreenVec4, in vec4 kBlueVec4,
  in vec2 kRedVec2, in vec2 kGreenVec2, in vec2 kBlueVec2) {
  // Moves the color space a little bit to avoid pure red.
  // Removes this line for more contrast.
  x = clamp(x * 0.9 + 0.03, 0.0, 1.0);
  vec4 v4 = vec4(1.0, x, x * x, x * x * x);
  vec2 v2 = v4.zw * v4.z;
  return vec3(
    dot(v4, kRedVec4) + dot(v2, kRedVec2),
    dot(v4, kGreenVec4) + dot(v2, kGreenVec2),
    dot(v4, kBlueVec4) + dot(v2, kBlueVec2)
  );
}

// Returns a smooth Percept colormap based upon the Turbo colormap.
vec3 PerceptColormap(in float x) {
  const vec4 kRedVec4 = vec4(0.55305649, 3.00913185, -5.46192616, -11.11819092);
  const vec4 kGreenVec4 = vec4(0.16207513, 0.17712472, 15.24091500, -36.50657960);
  const vec4 kBlueVec4 = vec4(-0.05195877, 5.18000081, -30.94853351, 81.96403246);
  const vec2 kRedVec2 = vec2(27.81927491, -14.87899417);
  const vec2 kGreenVec2 = vec2(25.95549545, -5.02738237);
  const vec2 kBlueVec2 = vec2(-86.53476570, 30.23299484);
  const float kInvalidDepthThreshold = 0.01;
  return step(kInvalidDepthThreshold, x) *
         GetPolynomialColor(x, kRedVec4, kGreenVec4, kBlueVec4,
                            kRedVec2, kGreenVec2, kBlueVec2);
}

void main() {
  vec4 packed_depth = texture2D(u_Depth, v_TexCoord.xy);
  highp float depth_mm = GetDepthMillimeters(packed_depth);
  highp float normalized_depth = depth_mm / kMaxDepth;
  vec4 depth_color = vec4(PerceptColormap(normalized_depth), 1.0);
  gl_FragColor = depth_color;
}

بعد ذلك، يجب تعديل فئة BackgroundRenderer لاستخدام أدوات التظليل الجديدة المتوفرة في src/main/java/com/google/ar/core/codelab/common/rendering/BackgroundRenderer.java.

إضافة مسارات الملفات إلى أدوات التظليل في أعلى الفئة:

// Add these under the other shader names at the top of the class.
private static final String DEPTH_VERTEX_SHADER_NAME = "shaders/background_show_depth_map.vert";
private static final String DEPTH_FRAGMENT_SHADER_NAME = "shaders/background_show_depth_map.frag";

أضِف المزيد من متغيّرات الأعضاء إلى الفئة BackgroundRenderer، لأنّها سيتم تشغيل برنامجَي تظليل:

// Add to the top of file with the rest of the member variables.
private int depthProgram;
private int depthTextureParam;
private int depthTextureId = -1;
private int depthQuadPositionParam;
private int depthQuadTexCoordParam;

أضِف طريقة جديدة لتعبئة هذه الحقول:

// Add this method below createOnGlThread().
public void createDepthShaders(Context context, int depthTextureId) throws IOException {
  int vertexShader =
      ShaderUtil.loadGLShader(
          TAG, context, GLES20.GL_VERTEX_SHADER, DEPTH_VERTEX_SHADER_NAME);
  int fragmentShader =
      ShaderUtil.loadGLShader(
          TAG, context, GLES20.GL_FRAGMENT_SHADER, DEPTH_FRAGMENT_SHADER_NAME);

  depthProgram = GLES20.glCreateProgram();
  GLES20.glAttachShader(depthProgram, vertexShader);
  GLES20.glAttachShader(depthProgram, fragmentShader);
  GLES20.glLinkProgram(depthProgram);
  GLES20.glUseProgram(depthProgram);
  ShaderUtil.checkGLError(TAG, "Program creation");

  depthTextureParam = GLES20.glGetUniformLocation(depthProgram, "u_Depth");
  ShaderUtil.checkGLError(TAG, "Program parameters");

  depthQuadPositionParam = GLES20.glGetAttribLocation(depthProgram, "a_Position");
  depthQuadTexCoordParam = GLES20.glGetAttribLocation(depthProgram, "a_TexCoord");

  this.depthTextureId = depthTextureId;
}

أضف هذه الطريقة، التي تُستخدم للرسم باستخدام أدوات التظليل هذه على كل إطار:

// Put this at the bottom of the file.
public void drawDepth(@NonNull Frame frame) {
  if (frame.hasDisplayGeometryChanged()) {
    frame.transformCoordinates2d(
        Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
        quadCoords,
        Coordinates2d.TEXTURE_NORMALIZED,
        quadTexCoords);
  }

  if (frame.getTimestamp() == 0 || depthTextureId == -1) {
    return;
  }

  // Ensure position is rewound before use.
  quadTexCoords.position(0);

  // No need to test or write depth, the screen quad has arbitrary depth, and is expected
  // to be drawn first.
  GLES20.glDisable(GLES20.GL_DEPTH_TEST);
  GLES20.glDepthMask(false);

  GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
  GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, depthTextureId);
  GLES20.glUseProgram(depthProgram);
  GLES20.glUniform1i(depthTextureParam, 0);

  // Set the vertex positions and texture coordinates.
  GLES20.glVertexAttribPointer(
        depthQuadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadCoords);
  GLES20.glVertexAttribPointer(
        depthQuadTexCoordParam, TEXCOORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadTexCoords);

  // Draws the quad.
  GLES20.glEnableVertexAttribArray(depthQuadPositionParam);
  GLES20.glEnableVertexAttribArray(depthQuadTexCoordParam);
  GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
  GLES20.glDisableVertexAttribArray(depthQuadPositionParam);
  GLES20.glDisableVertexAttribArray(depthQuadTexCoordParam);

  // Restore the depth state for further drawing.
  GLES20.glDepthMask(true);
  GLES20.glEnable(GLES20.GL_DEPTH_TEST);

  ShaderUtil.checkGLError(TAG, "BackgroundRendererDraw");
}

إضافة زر الإيقاف/التفعيل

الآن بعد أن أصبح لديك القدرة على عرض خريطة العمق، استخدمها! أضِف زرًا لتفعيل هذا العرض وإيقافه.

في أعلى ملف DepthCodelabActivity، أضِف عملية استيراد للزر كي تتمكّن من استخدامه:

import android.widget.Button;

عدِّل الفئة لإضافة عنصر منطقي يشير إلى ما إذا كان العرض العميق مفعَّلاً: (يكون متوقفًا تلقائيًا):

private boolean showDepthMap = false;

بعد ذلك، أضِف الزر الذي يتحكّم في القيمة المنطقية showDepthMap إلى نهاية طريقة onCreate():

final Button toggleDepthButton = (Button) findViewById(R.id.toggle_depth_button);
    toggleDepthButton.setOnClickListener(
        view -> {
          if (isDepthSupported) {
            showDepthMap = !showDepthMap;
            toggleDepthButton.setText(showDepthMap ? R.string.hide_depth : R.string.show_depth);
          } else {
            showDepthMap = false;
            toggleDepthButton.setText(R.string.depth_not_available);
          }
        });

أضِف هذه السلاسل إلى res/values/strings.xml:

<string translatable="false" name="show_depth">Show Depth</string>
<string translatable="false" name="hide_depth">Hide Depth</string>
<string translatable="false" name="depth_not_available">Depth Not Available</string>

أضف هذا الزر إلى الجزء السفلي من تنسيق التطبيق في res/layout/activity_main.xml:

<Button
    android:id="@+id/toggle_depth_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="20dp"
    android:gravity="center"
    android:text="@string/show_depth"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"/>

يتحكّم الزر الآن في قيمة القيمة المنطقية showDepthMap. استخدم هذه العلامة للتحكم في ما إذا كان سيتم عرض خريطة العمق أم لا.

بالعودة إلى الطريقة onDrawFrame() في DepthCodelabActivity، أضِف:

// Add this snippet just under backgroundRenderer.draw(frame);
if (showDepthMap) {
  backgroundRenderer.drawDepth(frame);
}

مرِّر زخرفة العمق إلى backgroundRenderer من خلال إضافة السطر التالي في onSurfaceCreated():

// Add to onSurfaceCreated() after backgroundRenderer.createonGlThread(/*context=*/ this);
backgroundRenderer.createDepthShaders(/*context=*/ this, depthTexture.getDepthTexture());

يمكنك الآن مشاهدة صورة عمق كل إطار عن طريق الضغط على الزر في أعلى يمين الشاشة.

التشغيل بدون دعم Depth API

العمل من خلال دعم Depth API

[اختياري] صور متحركة بعمق كبير

يعرض التطبيق حاليًا خريطة العمق مباشرةً. تمثل وحدات البكسل الحمراء المناطق القريبة. تمثل وحدات البكسل الزرقاء مناطق بعيدة.

هناك العديد من الطرق لنقل المعلومات التفصيلية. في هذا القسم، ستحتاج إلى تعديل أداة التظليل ليصبح عمقها بشكل دوري من خلال تعديل أداة التظليل بحيث لا تظهر سوى العمق داخل الأساور التي تبتعد عن الكاميرا بشكل متكرر.

ابدأ بإضافة هذه المتغيّرات إلى أعلى background_show_depth_map.frag:

uniform float u_DepthRangeToRenderMm;
const float kDepthWidthToRenderMm = 350.0;
  • بعد ذلك، استخدِم هذه القيم لفلترة وحدات البكسل المطلوب تغطيتها بقيم العمق في دالة main() في أداة التظليل:
// Add this line at the end of main().
gl_FragColor.a = clamp(1.0 - abs((depth_mm - u_DepthRangeToRenderMm) / kDepthWidthToRenderMm), 0.0, 1.0);

بعد ذلك، عليك تعديل BackgroundRenderer.java للإبقاء على مَعلمات أداة التظليل هذه. أضِف الحقول التالية إلى أعلى الفئة:

private static final float MAX_DEPTH_RANGE_TO_RENDER_MM = 20000.0f;
private float depthRangeToRenderMm = 0.0f;
private int depthRangeToRenderMmParam;

في طريقة createDepthShaders()، أضِف ما يلي لمطابقة هذه المَعلمات مع برنامج التظليل:

depthRangeToRenderMmParam = GLES20.glGetUniformLocation(depthProgram, "u_DepthRangeToRenderMm");
  • وأخيرًا، يمكنك التحكّم في هذا النطاق بمرور الوقت من خلال طريقة drawDepth(). أضف التعليمة البرمجية التالية، التي تزيد من هذا النطاق في كل مرة يتم فيها رسم إطار:
// Enables alpha blending.
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

// Updates range each time draw() is called.
depthRangeToRenderMm += 50.0f;
if (depthRangeToRenderMm > MAX_DEPTH_RANGE_TO_RENDER_MM) {
  depthRangeToRenderMm = 0.0f;
}

// Passes latest value to the shader.
GLES20.glUniform1f(depthRangeToRenderMmParam, depthRangeToRenderMm);

يتمّ الآن تصور العمق على شكل نبض متحرك يتدفق من خلال المشهد.

b846e4365d7b69b1.gif

لا تتردد في تغيير القيم المتوفرة هنا لجعل النبض أبطأ وأسرع وأوسع وأضيق وما إلى ذلك. يمكنك أيضًا تجربة طرق جديدة لتغيير أداة التظليل لعرض معلومات العمق.

8. استخدام واجهة برمجة التطبيقات Depth API لميزة "التظليل" (الجزء 4)

الآن ستتعامل مع إظلام الكائنات في تطبيقك.

يشير التظليل إلى ما يحدث عندما يتعذّر عرض الكائن الافتراضي بالكامل بسبب وجود عناصر حقيقية بين الكائن الافتراضي والكاميرا. إنّ إدارة التظليل ضرورية لجعل تجارب الواقع المعزّز غامرة.

إنّ العرض الصحيح للعناصر الافتراضية في الوقت الفعلي يعزّز واقعية وموثوقية المشهد المعزز. للاطّلاع على المزيد من الأمثلة، يُرجى مشاهدة الفيديو حول دمج الواقع مع واجهة برمجة التطبيقات Depth API.

في هذا القسم، سيتم تحديث تطبيقك لتضمين العناصر الافتراضية فقط في حال توفّر العمق.

إضافة أدوات تظليل الكائنات الجديدة

كما هو الحال في الأقسام السابقة، ستضيف أدوات تظليل جديدة لدعم المعلومات التفصيلية. يمكنك هذه المرة نسخ أدوات تظليل العناصر الحالية وإضافة وظيفة التظليل.

من المهم الاحتفاظ بكلا الإصدارَين من أدوات تظليل العناصر، ليتمكّن التطبيق من تحديد ما إذا كان ينبغي توفير العمق أم لا.

يمكنك إنشاء نُسخ من ملفَّي أداة التظليل object.vert وobject.frag في الدليل src/main/assets/shaders.

  • نسخ object.vert إلى ملف الوجهة src/main/assets/shaders/occlusion_object.vert
  • نسخ object.frag إلى ملف الوجهة src/main/assets/shaders/occlusion_object.frag

داخل occlusion_object.vert، أضِف المتغيّر التالي أعلى main():

varying vec3 v_ScreenSpacePosition;

اضبط هذا المتغيّر في أسفل main():

v_ScreenSpacePosition = gl_Position.xyz / gl_Position.w;

يمكنك تعديل occlusion_object.frag من خلال إضافة هذه المتغيّرات أعلى main() في أعلى الملف:

varying vec3 v_ScreenSpacePosition;

uniform sampler2D u_Depth;
uniform mat3 u_UvTransform;
uniform float u_DepthTolerancePerMm;
uniform float u_OcclusionAlpha;
uniform float u_DepthAspectRatio;
  • أضِف الدوال المساعدة هذه أعلاه main() في أداة التظليل لتسهيل التعامل مع معلومات العمق:
float GetDepthMillimeters(in vec2 depth_uv) {
  // Depth is packed into the red and green components of its texture.
  // The texture is a normalized format, storing millimeters.
  vec3 packedDepthAndVisibility = texture2D(u_Depth, depth_uv).xyz;
  return dot(packedDepthAndVisibility.xy, vec2(255.0, 256.0 * 255.0));
}

// Returns linear interpolation position of value between min and max bounds.
// E.g., InverseLerp(1100, 1000, 2000) returns 0.1.
float InverseLerp(in float value, in float min_bound, in float max_bound) {
  return clamp((value - min_bound) / (max_bound - min_bound), 0.0, 1.0);
}

// Returns a value between 0.0 (not visible) and 1.0 (completely visible)
// Which represents how visible or occluded is the pixel in relation to the
// depth map.
float GetVisibility(in vec2 depth_uv, in float asset_depth_mm) {
  float depth_mm = GetDepthMillimeters(depth_uv);

  // Instead of a hard z-buffer test, allow the asset to fade into the
  // background along a 2 * u_DepthTolerancePerMm * asset_depth_mm
  // range centered on the background depth.
  float visibility_occlusion = clamp(0.5 * (depth_mm - asset_depth_mm) /
    (u_DepthTolerancePerMm * asset_depth_mm) + 0.5, 0.0, 1.0);

  // Depth close to zero is most likely invalid, do not use it for occlusions.
  float visibility_depth_near = 1.0 - InverseLerp(
      depth_mm, /*min_depth_mm=*/150.0, /*max_depth_mm=*/200.0);

  // Same for very high depth values.
  float visibility_depth_far = InverseLerp(
      depth_mm, /*min_depth_mm=*/17500.0, /*max_depth_mm=*/20000.0);

  float visibility =
    max(max(visibility_occlusion, u_OcclusionAlpha),
      max(visibility_depth_near, visibility_depth_far));

  return visibility;
}

يمكنك الآن تعديل "main()" في "occlusion_object.frag" للتعرّف على مزيد من التفاصيل وتطبيق ميزة "التظليل". أضِف الأسطر التالية في أسفل الملف:

const float kMToMm = 1000.0;
float asset_depth_mm = v_ViewPosition.z * kMToMm * -1.;
vec2 depth_uvs = (u_UvTransform * vec3(v_ScreenSpacePosition.xy, 1)).xy;
gl_FragColor.a *= GetVisibility(depth_uvs, asset_depth_mm);

والآن بعد أن أصبح لديك إصدار جديد من أدوات تظليل الكائنات، يمكنك تعديل رمز العارض.

عرض إطباق العناصر

يمكنك إنشاء نسخة من الصف ObjectRenderer التالي في "src/main/java/com/google/ar/core/codelab/common/rendering/ObjectRenderer.java".

  • اختيار صف واحد (ObjectRenderer)
  • النقر بزر الماوس الأيمن > نسخ
  • اختَر مجلد العرض
  • النقر بزر الماوس الأيمن > لصق

7487ece853690c31.png

  • إعادة تسمية الصف إلى "OcclusionObjectRenderer"

760a4c80429170c2.png

من المفترض أن يظهر الآن الصف الجديد الذي تمت إعادة تسميته في المجلد نفسه:

9335c373dc60cd17.png

افتح OcclusionObjectRenderer.java الذي تم إنشاؤه حديثًا وغيِّر مسارات التظليل في أعلى الملف:

private static final String VERTEX_SHADER_NAME = "shaders/occlusion_object.vert";
private static final String FRAGMENT_SHADER_NAME = "shaders/occlusion_object.frag";
  • أضف متغيرات الأعضاء المتعلقة بالعمق مع المتغيرات الأخرى في أعلى الفئة. وستعمل المتغيّرات على ضبط حدّة حدّ التظليل.
// Shader location: depth texture
private int depthTextureUniform;

// Shader location: transform to depth uvs
private int depthUvTransformUniform;

// Shader location: depth tolerance property
private int depthToleranceUniform;

// Shader location: maximum transparency for the occluded part.
private int occlusionAlphaUniform;

private int depthAspectRatioUniform;

private float[] uvTransform = null;
private int depthTextureId;

أنشئ متغيرات العضو هذه باستخدام القيم التلقائية في أعلى الفئة:

// These values will be changed each frame based on the distance to the object.
private float depthAspectRatio = 0.0f;
private final float depthTolerancePerMm = 0.015f;
private final float occlusionsAlpha = 0.0f;

يمكنك إعداد المَعلمات الموحَّدة لأداة التظليل بالطريقة createOnGlThread():

// Occlusions Uniforms.  Add these lines before the first call to ShaderUtil.checkGLError
// inside the createOnGlThread() method.
depthTextureUniform = GLES20.glGetUniformLocation(program, "u_Depth");
depthUvTransformUniform = GLES20.glGetUniformLocation(program, "u_UvTransform");
depthToleranceUniform = GLES20.glGetUniformLocation(program, "u_DepthTolerancePerMm");
occlusionAlphaUniform = GLES20.glGetUniformLocation(program, "u_OcclusionAlpha");
depthAspectRatioUniform = GLES20.glGetUniformLocation(program, "u_DepthAspectRatio");
  • تأكَّد من تعديل هذه القيم في كل مرة يتم فيها رسمها من خلال تعديل طريقة draw():
// Add after other GLES20.glUniform calls inside draw().
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, depthTextureId);
GLES20.glUniform1i(depthTextureUniform, 1);
GLES20.glUniformMatrix3fv(depthUvTransformUniform, 1, false, uvTransform, 0);
GLES20.glUniform1f(depthToleranceUniform, depthTolerancePerMm);
GLES20.glUniform1f(occlusionAlphaUniform, occlusionsAlpha);
GLES20.glUniform1f(depthAspectRatioUniform, depthAspectRatio);

أضِف الأسطر التالية ضمن draw() لتفعيل وضع المزج في العرض حتى يمكن تطبيق الشفافية على العناصر الافتراضية عند حجبها:

// Add these lines just below the code-block labeled "Enable vertex arrays"
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
// Add these lines just above the code-block labeled "Disable vertex arrays"
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDepthMask(true);
  • يمكنك إضافة الطرق التالية حتى يتمكّن المتصلون بـ OcclusionObjectRenderer من توفير معلومات العمق:
// Add these methods at the bottom of the OcclusionObjectRenderer class.
public void setUvTransformMatrix(float[] transform) {
  uvTransform = transform;
}

public void setDepthTexture(int textureId, int width, int height) {
  depthTextureId = textureId;
  depthAspectRatio = (float) width / (float) height;
}

التحكّم في تظليل العناصر

بعد أن أصبح لديك OcclusionObjectRenderer جديد، يمكنك إضافته إلى DepthCodelabActivity واختيار وقت وطريقة استخدام العرض الإطباق.

يمكنك تفعيل هذا المنطق عن طريق إضافة مثيل من OcclusionObjectRenderer إلى النشاط، بحيث يكون كل من ObjectRenderer وOcclusionObjectRenderer أعضاء في DepthCodelabActivity:

// Add this include at the top of the file.
import com.google.ar.core.codelab.common.rendering.OcclusionObjectRenderer;
// Add this member just below the existing "virtualObject", so both are present.
private final OcclusionObjectRenderer occludedVirtualObject = new OcclusionObjectRenderer();
  • يمكنك بعد ذلك التحكّم في وقت استخدام occludedVirtualObject استنادًا إلى ما إذا كان الجهاز الحالي متوافقًا مع Depth API. أضِف هذه الأسطر داخل طريقة onSurfaceCreated، أدناه حيث يتم إعداد virtualObject:
if (isDepthSupported) {
  occludedVirtualObject.createOnGlThread(/*context=*/ this, "models/andy.obj", "models/andy.png");
  occludedVirtualObject.setDepthTexture(
     depthTexture.getDepthTexture(),
     depthTexture.getDepthWidth(),
     depthTexture.getDepthHeight());
  occludedVirtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);
}

على الأجهزة التي لا تتوفّر فيها ميزة العمق، يتم إنشاء مثيل occludedVirtualObject ولكن لا يتم استخدامه. في الهواتف ذات العمق، يتم تهيئة كلا الإصدارين، ويتم اتخاذ قرار وقت التشغيل بشأن إصدار العارض المراد استخدامه عند الرسم.

ابحث داخل الطريقة onDrawFrame() عن الرمز الحالي:

virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, OBJECT_COLOR);

استبدل هذه التعليمة البرمجية بما يلي:

if (isDepthSupported) {
  occludedVirtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
  occludedVirtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, OBJECT_COLOR);
} else {
  virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
  virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, OBJECT_COLOR);
}

وأخيرًا، تأكَّد من ربط صورة العمق بشكل صحيح بعرض النتائج. ونظرًا لأن دقة صورة العمق مختلفة، وقد تكون نسبة عرض إلى ارتفاع مختلفة عن الشاشة، فقد تكون إحداثيات الزخرفة مختلفة بين نفسها وصورة الكاميرا.

  • أضِف الطريقة المساعدة getTextureTransformMatrix() إلى أسفل الملف. تعرض هذه الطريقة مصفوفة تحويل تجعل الأشعة فوق البنفسجية لمساحة الشاشة تتطابق بشكل صحيح مع إحداثيات الشكل الرباعي المستخدمة لعرض خلاصة الكاميرا عند تطبيقها. ويأخذ في الاعتبار أيضًا اتجاه الجهاز.
private static float[] getTextureTransformMatrix(Frame frame) {
  float[] frameTransform = new float[6];
  float[] uvTransform = new float[9];
  // XY pairs of coordinates in NDC space that constitute the origin and points along the two
  // principal axes.
  float[] ndcBasis = {0, 0, 1, 0, 0, 1};

  // Temporarily store the transformed points into outputTransform.
  frame.transformCoordinates2d(
      Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
      ndcBasis,
      Coordinates2d.TEXTURE_NORMALIZED,
      frameTransform);

  // Convert the transformed points into an affine transform and transpose it.
  float ndcOriginX = frameTransform[0];
  float ndcOriginY = frameTransform[1];
  uvTransform[0] = frameTransform[2] - ndcOriginX;
  uvTransform[1] = frameTransform[3] - ndcOriginY;
  uvTransform[2] = 0;
  uvTransform[3] = frameTransform[4] - ndcOriginX;
  uvTransform[4] = frameTransform[5] - ndcOriginY;
  uvTransform[5] = 0;
  uvTransform[6] = ndcOriginX;
  uvTransform[7] = ndcOriginY;
  uvTransform[8] = 1;

  return uvTransform;
}

تتطلب الدالة getTextureTransformMatrix() الاستيراد التالي في أعلى الملف:

import com.google.ar.core.Coordinates2d;

وتريد حساب التحويل بين إحداثيات هذه الهيئة كلما تغيرت زخرفة الشاشة (على سبيل المثال، عند تدوير الشاشة). هذه الوظيفة غير متاحة.

أضِف العلامة التالية في أعلى الملف:

// Add this member at the top of the file.
private boolean calculateUVTransform = true;
  • في "onDrawFrame()"، تحقَّق مما إذا كانت عملية التحويل المخزّنة بحاجة إلى إعادة حساب البيانات بعد إنشاء الإطار والكاميرا:
// Add these lines inside onDrawFrame() after frame.getCamera().
if (frame.hasDisplayGeometryChanged() || calculateUVTransform) {
  calculateUVTransform = false;
  float[] transform = getTextureTransformMatrix(frame);
  occludedVirtualObject.setUvTransformMatrix(transform);
}

بعد تطبيق هذه التغييرات، يمكنك تشغيل التطبيق باستخدام ميزة "تظليل العناصر الافتراضية"!

من المفترض أن يعمل تطبيقك الآن بشكل سلس على جميع الهواتف، وأن يستخدم تلقائيًا ميزة "تعتيم الشاشة" عندما يكون متوافقًا.

تشغيل تطبيق يتوافق مع Depth API

تشغيل التطبيق بدون دعم Depth API

9. [اختياري] تحسين جودة التظليل

إنّ طريقة التظليل المستندة إلى العمق، التي تم تطبيقها أعلاه، توفّر التظليل بحدود حادة. مع ابتعاد الكاميرا عن الجسم المُراد قياسه، قد تصبح قياسات العمق أقل دقة، ما قد يؤدي إلى ظهور عناصر مرئية.

يمكننا الحدّ من هذه المشكلة من خلال إضافة تمويه إضافي إلى اختبار التظليل، ما ينتج عنه حافة أكثر سلاسة للكائنات الافتراضية المخفية.

occlusion_object.frag

أضِف المتغيّر الموحَّد التالي في أعلى occlusion_object.frag:

uniform float u_OcclusionBlurAmount;

أضِف هذه الوظيفة المساعدة أعلى main() مباشرةً في أداة التظليل، والتي تطبّق تمويه النواة على عيّنة التظليل:

float GetBlurredVisibilityAroundUV(in vec2 uv, in float asset_depth_mm) {
  // Kernel used:
  // 0   4   7   4   0
  // 4   16  26  16  4
  // 7   26  41  26  7
  // 4   16  26  16  4
  // 0   4   7   4   0
  const float kKernelTotalWeights = 269.0;
  float sum = 0.0;

  vec2 blurriness = vec2(u_OcclusionBlurAmount,
                         u_OcclusionBlurAmount * u_DepthAspectRatio);

  float current = 0.0;

  current += GetVisibility(uv + vec2(-1.0, -2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, -2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-1.0, +2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, +2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-2.0, +1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+2.0, +1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-2.0, -1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+2.0, -1.0) * blurriness, asset_depth_mm);
  sum += current * 4.0;

  current = 0.0;
  current += GetVisibility(uv + vec2(-2.0, -0.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+2.0, +0.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+0.0, +2.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-0.0, -2.0) * blurriness, asset_depth_mm);
  sum += current * 7.0;

  current = 0.0;
  current += GetVisibility(uv + vec2(-1.0, -1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, -1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-1.0, +1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, +1.0) * blurriness, asset_depth_mm);
  sum += current * 16.0;

  current = 0.0;
  current += GetVisibility(uv + vec2(+0.0, +1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-0.0, -1.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(-1.0, -0.0) * blurriness, asset_depth_mm);
  current += GetVisibility(uv + vec2(+1.0, +0.0) * blurriness, asset_depth_mm);
  sum += current * 26.0;

  sum += GetVisibility(uv , asset_depth_mm) * 41.0;

  return sum / kKernelTotalWeights;
}

استبدل هذا السطر الحالي في main():

gl_FragColor.a *= GetVisibility(depth_uvs, asset_depth_mm);

مع هذا السطر:

gl_FragColor.a *= GetBlurredVisibilityAroundUV(depth_uvs, asset_depth_mm);

حدِّث العارض للاستفادة من وظيفة أداة التظليل الجديدة هذه.

OcclusionObjectRenderer.java

أضِف متغيرات العضو التالية في أعلى الفئة:

private int occlusionBlurUniform;
private final float occlusionsBlur = 0.01f;

أضِف ما يلي داخل طريقة createOnGlThread:

// Add alongside the other calls to GLES20.glGetUniformLocation.
occlusionBlurUniform = GLES20.glGetUniformLocation(program, "u_OcclusionBlurAmount");

أضِف ما يلي داخل طريقة draw:

// Add alongside the other calls to GLES20.glUniform1f.
GLES20.glUniform1f(occlusionBlurUniform, occlusionsBlur);

مقارنة مرئية

من المفترض أن تكون حدود التظليل الآن أكثر سلاسة مع هذه التغييرات.

10. Build-Run-Test

إنشاء تطبيقك وتشغيله

  1. وصِّل جهاز Android عبر منفذ USB.
  2. اختر ملف > الإنشاء والتشغيل:
  3. حفظ باسم: ARCodeLab.apk
  4. انتظر حتى يتم إنشاء التطبيق ونشره على جهازك.

في المرة الأولى التي تحاول فيها نشر التطبيق على جهازك:

  • يجب السماح بتصحيح أخطاء الجهاز عبر USB. انقر على "حسنًا" للمتابعة.
  • سيتم سؤالك عمّا إذا كان التطبيق لديه الإذن لاستخدام كاميرا الجهاز. يجب منح إذن الوصول لمواصلة استخدام وظيفة الواقع المعزّز.

اختبار تطبيقك

عند تشغيل تطبيقك، يمكنك اختبار سلوكه الأساسي من خلال حمل جهازك والتحرك في أرجاء المساحة ومسح المنطقة ببطء. حاول جمع 10 ثوانٍ على الأقل من البيانات ومسح المنطقة من عدة اتجاهات قبل الانتقال إلى الخطوة التالية.

تحديد المشاكل وحلّها

إعداد جهاز Android لإجراء التطوير

  1. وصِّل جهازك بجهاز التطوير باستخدام كابل USB. في حال تطوير البرامج باستخدام نظام التشغيل Windows، قد تحتاج إلى تثبيت برنامج تشغيل USB المناسب لجهازك.
  2. نفِّذ الخطوات التالية لتفعيل تصحيح أخطاء الجهاز عبر USB في نافذة خيارات المطوّرين:
  3. افتح تطبيق الإعدادات.
  4. إذا كان جهازك يعمل بالإصدار 8.0 من نظام التشغيل Android أو إصدار أحدث، اختَر النظام. وبخلاف ذلك، يمكنك المتابعة إلى الخطوة التالية.
  5. انتقِل إلى أسفل الصفحة واختَر لمحة عن الهاتف.
  6. انتقِل إلى أسفل الصفحة وانقر على رقم الإصدار 7 مرات.
  7. ارجع إلى الشاشة السابقة وانتقِل إلى أسفل الشاشة وانقر على خيارات المطوّرين.
  8. في نافذة خيارات المطوّرين، انتقِل للأسفل للعثور على خيار تصحيح أخطاء USB وتفعيله.

يمكنك العثور على معلومات أكثر تفصيلاً عن هذه العملية على الموقع الإلكتروني لمطوّري تطبيقات Android من Google.

cfa20a722a68f54f.png

إذا حدث خطأ في الإصدار المتعلّق بالتراخيص (تعذّر تثبيت حِزم حزمة تطوير البرامج (SDK) التالية لنظام التشغيل Android بسبب عدم قبول بعض التراخيص)، يمكنك استخدام الأوامر التالية لمراجعة هذه التراخيص وقبولها:

cd <path to Android SDK>

tools/bin/sdkmanager --licenses

11. تهانينا

تهانينا، لقد أنشأت بنجاح أول تطبيق للواقع المعزّز المستند إلى العمق وقم بتشغيله باستخدام واجهة برمجة التطبيقات ARCore Depth API من Google.

الأسئلة الشائعة