ऑगमेंटेड रिएलिटी (एआर) का इमर्सिव अनुभव पाने के लिए, ARCore depth API का इस्तेमाल करें

1. शुरू करने से पहले

ARCore, मोबाइल डिवाइसों पर ऑगमेंटेड रिएलिटी (एआर) ऐप्लिकेशन बनाने के लिए प्लैटफ़ॉर्म है. अलग-अलग एपीआई का इस्तेमाल करके, ARCore की मदद से उपयोगकर्ता का डिवाइस, एनवायरमेंट के बारे में जानकारी देख सकता है और उसे पा सकता है. साथ ही, यह उस जानकारी से इंटरैक्ट कर सकता है.

इस कोडलैब में, आपको एआर की सुविधा वाला आसान ऐप्लिकेशन बनाने की प्रोसेस के बारे में जानकारी मिलेगी. इसमें ARCore depth API का इस्तेमाल किया जाता है.

ज़रूरी शर्तें

यह कोडलैब उन डेवलपर के लिए बनाया गया है जिन्हें एआर के बुनियादी सिद्धांतों की जानकारी है.

आपको क्या बनाना होगा

1a0236e93212210c.gif

आपको एक ऐसा ऐप्लिकेशन बनाना होगा जो सीन की ज्यामिति को विज़ुअलाइज़ करने और लगाई गई वर्चुअल ऐसेट पर 'अक्लूज़न' परफ़ॉर्म करने के लिए, हर फ़्रेम की डेप्थ इमेज का इस्तेमाल करे. आपको यहां दिए गए खास चरणों को पूरा करना होगा:

  • फ़ोन पर डेप्थ एपीआई की सुविधा देखी जा रही है
  • हर फ़्रेम के लिए, डेप्थ इमेज ली जा रही है
  • गहराई से जुड़ी जानकारी को कई तरीकों से विज़ुअलाइज़ करना (ऊपर दिया गया ऐनिमेशन देखें)
  • डेप्थ की मदद से, ऐप्लिकेशन को बिलकुल असली जैसा अनुभव दिया जा सकता है
  • जो फ़ोन डेप्थ एपीआई के साथ काम नहीं करते उन पर सही तरीके से कार्रवाई करने का तरीका जानें

आपको इन चीज़ों की ज़रूरत होगी

हार्डवेयर की ज़रूरतें

सॉफ़्टवेयर से जुड़ी ज़रूरी शर्तें

  • ARCore SDK 1.31.0 या इसके बाद का वर्शन.
  • Android Studio (v3.0 या इसके बाद के वर्शन) वाली डेवलपमेंट मशीन.

2. ARCore और डेप्थ एपीआई

डेप्थ एपीआई डेप्थ मैप बनाने के लिए, उस डिवाइस के आरजीबी कैमरे का इस्तेमाल करता है जिस पर यह सुविधा काम करती है. इस तरह के मैप को डेप्थ इमेज भी कहा जाता है. डेप्थ मैप में दी गई जानकारी का इस्तेमाल करके, वर्चुअल ऑब्जेक्ट को असल दुनिया की ऑब्जेक्ट के सामने या पीछे दिखाया जा सकता है. इससे लोगों को असली लगने वाला इमर्सिव अनुभव मिलता है.

ARCore depth API की मदद से, आपको ARCore के सेशन के ज़रिए दिए गए हर फ़्रेम से मिलती-जुलती डेप्थ इमेज का ऐक्सेस मिलता है. हर पिक्सल, कैमरे से आस-पास की चीज़ों की दूरी को मापता है. इससे आपके एआर ऐप्लिकेशन को ज़्यादा सटीक जानकारी मिलती है.

डेप्थ एपीआई की एक मुख्य सुविधा अक्लूज़न है: डिजिटल ऑब्जेक्ट, असल दुनिया के ऑब्जेक्ट के सापेक्ष सटीक तौर पर दिखते हैं. इससे ऑब्जेक्ट को ऐसा लगता है जैसे वे उपयोगकर्ता के आस-पास मौजूद हैं.

यह कोडलैब आपको एआर (ऑगमेंटेड रिएलिटी) की सुविधा वाला आसान ऐप्लिकेशन बनाने में मदद करेगा. यह ऐप, डेप्थ इमेज का इस्तेमाल करके, असल दुनिया की सतहों के पीछे मौजूद वर्चुअल ऑब्जेक्ट को छिपाता है और स्पेस की पता लगाई गई ज्यामिति को विज़ुअलाइज़ करता है.

3. सेट अप करें

डेवलपमेंट मशीन सेट अप करना

  1. यूएसबी केबल के ज़रिए ARCore डिवाइस को अपने कंप्यूटर से कनेक्ट करें. पक्का करें कि आपका डिवाइस यूएसबी डीबग करने की अनुमति देता हो.
  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 Studio लॉन्च करें और मौजूदा Android Studio प्रोजेक्ट खोलें पर क्लिक करें.
  2. उस डायरेक्ट्री को ढूंढें जहां से आपने ऊपर डाउनलोड की गई ZIP फ़ाइल निकाली थी और depth_codelab_io2020 डायरेक्ट्री खोलें.

यह कई मॉड्यूल वाला एक Gradle प्रोजेक्ट है. अगर Android Studio के सबसे ऊपर बाईं ओर मौजूद प्रोजेक्ट पैनल, प्रोजेक्ट पैनल में पहले से नहीं दिख रहा है, तो ड्रॉप-डाउन मेन्यू में प्रोजेक्ट पर क्लिक करें.

नतीजा कुछ ऐसा दिखना चाहिए:

इस प्रोजेक्ट में ये मॉड्यूल शामिल हैं:

  • part0_work: स्टार्टर ऐप्लिकेशन. कोडलैब का यह मॉड्यूल करते समय आपको इस मॉड्यूल में बदलाव करने चाहिए.
  • part1: इस बारे में रेफ़रंस कोड कि पहला हिस्सा पूरा करने के बाद, आपने जो बदलाव किए हैं वे कैसे दिखने चाहिए.
  • part2: दूसरा हिस्सा पूरा करने पर, रेफ़रंस कोड
  • part3: तीसरा पार्ट पूरा करने पर रेफ़रंस कोड.
  • part4_completed: ऐप्लिकेशन का फ़ाइनल वर्शन. चौथा पार्ट और इस कोडलैब (कोड बनाना सीखना) पूरा करने पर रेफ़रंस कोड.

आपको part0_work मॉड्यूल में काम करना होगा. कोडलैब के हर हिस्से के लिए पूरा समाधान भी मौजूद है. हर मॉड्यूल एक बिल्डेबल ऐप्लिकेशन होता है.

4. स्टार्टर ऐप्लिकेशन चलाएं

  1. Run > चलाएं... &gt; ‘पार्ट0_work' का इस्तेमाल करें. आपको डिप्लॉयमेंट टारगेट चुनें डायलॉग बॉक्स दिखेगा. इसमें आपका डिवाइस कनेक्ट किए गए डिवाइस में दिखेगा.
  2. अपना डिवाइस चुनें और ठीक है पर क्लिक करें. Android Studio, शुरुआती ऐप्लिकेशन बनाएगा और उसे आपके डिवाइस पर चलाएगा.
  3. ऐप्लिकेशन, कैमरे को ऐक्सेस करने की अनुमतियां मांगेगा. जारी रखने के लिए, अनुमति दें पर टैप करें.

c5ef65f7a1da0d9.png

ऐप्लिकेशन को इस्तेमाल करने का तरीका

  1. प्लेन ढूंढने में ऐप्लिकेशन की मदद करने के लिए, डिवाइस को इधर-उधर घुमाएं. नीचे दिया गया संदेश यह बताता है कि कब आगे बढ़ना है.
  2. लंगर लगाने के लिए हवाई जहाज़ पर कहीं भी टैप करें. जहां ऐंकर रखा गया है वहां एक Android आकृति बनाई जाएगी. इस ऐप्लिकेशन में, एक बार में सिर्फ़ एक ऐंकर कॉन्टेंट रखा जा सकता है.
  3. डिवाइस को इधर-उधर घुमाएं. यह आकृति समान स्थान पर बनी हुई दिखाई देनी चाहिए, भले ही डिवाइस आस-पास हो रहा हो.

फ़िलहाल, आपका ऐप्लिकेशन बहुत आसान है. साथ ही, उसे असल दुनिया की ज्यामिति के बारे में ज़्यादा जानकारी नहीं है.

उदाहरण के लिए, यदि आप किसी Android आकृति को कुर्सी के पीछे रखते हैं, तो रेंडरिंग सामने होवर होती हुई दिखाई देगी, क्योंकि एप्लिकेशन को पता नहीं चलेगा कि कुर्सी वहां है और Android को छिपाना चाहिए.

6182cf62be13cd97.png beb0d327205f80ee.png e4497751c6fad9a7.png

इस समस्या को ठीक करने के लिए, हम डेप्थ एपीआई का इस्तेमाल करेंगे, ताकि इस ऐप्लिकेशन में इमर्सिव मोड और रीयलिज़म को बेहतर बनाया जा सके.

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

अब एआर सेशन को सही तरीके से कॉन्फ़िगर किया गया है. साथ ही, आपके ऐप्लिकेशन को पता है कि वह डेप्थ के हिसाब से उपलब्ध सुविधाओं का इस्तेमाल कर सकता है या नहीं.

आपको उपयोगकर्ता को यह भी बताना चाहिए कि इस सेशन के लिए डेप्थ का इस्तेमाल किया गया है या नहीं.

स्नैकबार में एक और मैसेज जोड़ें. यह स्क्रीन पर सबसे नीचे दिखेगा:

// 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

इसके बाद, डेप्थ एपीआई को कॉल करने और हर फ़्रेम के लिए डेप्थ इमेज हासिल करने के लिए, आपको ऐप्लिकेशन को अपडेट करना होगा.

6. गहराई से बनाई गई इमेज वापस पाना (पार्ट 2)

डेप्थ एपीआई, डिवाइस के एनवायरमेंट के 3D ऑब्ज़र्वेशन को कैप्चर करता है और आपके ऐप्लिकेशन पर उस डेटा के साथ डेप्थ इमेज दिखाता है. डेप्थ इमेज में मौजूद हर पिक्सल, डिवाइस के कैमरे से लेकर असल दुनिया के आस-पास की दूरी को दिखाता है.

अब ऐप्लिकेशन में रेंडरिंग और विज़ुअलाइज़ेशन को बेहतर बनाने के लिए, डेप्थ इमेज का इस्तेमाल किया जाएगा. सबसे पहले, हर फ़्रेम के लिए डेप्थ इमेज को वापस लाएं और जीपीयू से इस्तेमाल किए जाने वाले टेक्सचर को बाइंड करें.

सबसे पहले, अपने प्रोजेक्ट में नई क्लास जोड़ें.
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();

आखिर में, आपको हर फ़्रेम में इस टेक्सचर को सबसे नई डेप्थ इमेज के साथ पॉप्युलेट करना है. ऐसा करने के लिए, session से लिए गए सबसे नए फ़्रेम पर, ऊपर बनाए गए update() तरीके को कॉल करें.
इस ऐप्लिकेशन के लिए डेप्थ डेप्थ की सुविधा ज़रूरी नहीं है. इसलिए, इस कॉल का इस्तेमाल सिर्फ़ तब करें, जब आपके डिवाइस में डेप्थ का इस्तेमाल किया जा रहा हो.

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

अब आपके पास डेप्थ इमेज है, जो हर फ़्रेम के साथ अपडेट होती है. यह आपके शेडर द्वारा उपयोग किए जाने के लिए तैयार है.

हालांकि, ऐप्लिकेशन के काम करने के तरीके में अभी तक कोई बदलाव नहीं हुआ है. अब अपने ऐप्लिकेशन को बेहतर बनाने के लिए, डेप्थ इमेज का इस्तेमाल किया जा सकता है.

7. डेप्थ इमेज को रेंडर करना (पार्ट 3)

अब आपके पास खेलने के लिए एक डेप्थ इमेज है, आप देखना चाहेंगे कि यह कैसा दिखता है. इस सेक्शन में, आपको हर फ़्रेम के लिए डेप्थ रेंडर करने के लिए ऐप्लिकेशन में एक बटन जोड़ना होगा.

नए शेडर जोड़ना

डेप्थ इमेज देखने के कई तरीके हैं. नीचे दिए गए शेडर, आसानी से कलर मैपिंग विज़ुअलाइज़ेशन देते हैं.

नया .बिंदु शेडर जोड़ें

Android Studio में:

  1. सबसे पहले, src/main/assets/shaders/ डायरेक्ट्री में नए .vert और .frag शेडर जोड़ें.
  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;
}

इसके बाद, src/main/java/com/google/ar/core/codelab/common/rendering/BackgroundRenderer.java में मौजूद इन नए शेडर का इस्तेमाल करने के लिए, BackgroundRenderer क्लास को अपडेट करें.

क्लास के सबसे ऊपर, शेडर में फ़ाइल पाथ जोड़ें:

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

इसके बाद, onCreate() तरीके के आखिर में वह बटन जोड़ें जो showDepthMap बूलियन को कंट्रोल करता है:

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 की वैल्यू को कंट्रोल करता है. इस फ़्लैग का इस्तेमाल करके यह तय करें कि गहराई का मैप रेंडर होगा या नहीं.

DepthCodelabActivity में onDrawFrame() तरीके पर वापस जाएं और इसे जोड़ें:

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

onSurfaceCreated() में यह लाइन जोड़कर, backgroundRenderer में डेप्थ टेक्सचर पास करें:

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

अब आप स्क्रीन के ऊपर दाईं ओर बटन दबाकर हर फ़्रेम की डेप्थ इमेज देख सकते हैं.

डेप्थ एपीआई की सुविधा के बिना चल रहा है

डेप्थ एपीआई की सुविधा के साथ काम करना

[ज़रूरी नहीं] फ़ैंसी डेप्थ ऐनिमेशन

फ़िलहाल, ऐप्लिकेशन सीधे गहराई का मैप दिखाता है. लाल पिक्सल, आस-पास की जगहें दिखाते हैं. नीले पिक्सल, दूर की जगहों को दिखाते हैं.

गहराई से जानकारी देने के कई तरीके हैं. इस सेक्शन में, शेडर को समय-समय पर पल्स डेप्थ के हिसाब से बदला जा सकता है. ऐसा करने के लिए, शेडर में बदलाव करना होगा, ताकि वे सिर्फ़ उन बैंड में डेप्थ दिखाएं जो बार-बार कैमरे से दूर चले जाते हैं.

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. पूरी जानकारी न दिखाने के लिए, डेप्थ एपीआई का इस्तेमाल करना (पार्ट 4)

अब ऐप्लिकेशन में ऑब्जेक्ट को रोकने की सुविधा को मैनेज किया जा सकता है.

'अक्लूज़न' का मतलब है कि जब वर्चुअल ऑब्जेक्ट को पूरी तरह से रेंडर नहीं किया जा सकता, तब क्या होता है, क्योंकि वर्चुअल ऑब्जेक्ट और कैमरे के बीच असली ऑब्जेक्ट मौजूद होते हैं. एआर (ऑगमेंटेड रिएलिटी) का बेहतर अनुभव पाने के लिए, ऑक्लूज़न को मैनेज करना ज़रूरी है.

रीयल टाइम में वर्चुअल ऑब्जेक्ट को सही तरीके से रेंडर करने से, ऑगमेंटेड सीन में असली लगने वाले ऑब्जेक्ट और भरोसे को बढ़ाने में मदद मिलती है. ज़्यादा उदाहरणों के लिए, कृपया depth API के साथ वास्तविकताओं को मिलाने पर हमारा वीडियो देखें.

इस सेक्शन में, आपको वर्चुअल ऑब्जेक्ट को शामिल करने के लिए अपने ऐप्लिकेशन को अपडेट करना होगा. हालांकि, ऐसा सिर्फ़ तब करना होगा, जब डेप्थ की जानकारी उपलब्ध हो.

नए ऑब्जेक्ट शेडर जोड़ना

पिछले सेक्शन की तरह ही, इसमें डेप्थ की जानकारी देने के लिए नए शेडर जोड़े जाएंगे. इस बार, मौजूदा ऑब्जेक्ट शेडर को कॉपी किया जा सकता है और ऑक्लूज़न फ़ंक्शन जोड़ा जा सकता है.

ऑब्जेक्ट शेडर के दोनों वर्शन को रखना ज़रूरी है. इससे आपका ऐप्लिकेशन रन-टाइम में यह तय कर पाएगा कि उसे कॉन्टेंट की गहराई के साथ काम करना है या नहीं.

src/main/assets/shaders डायरेक्ट्री में, object.vert और object.frag शेडर फ़ाइलों की कॉपी बनाएं.

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

फ़ाइल के ऊपर main() से ऊपर ये वैरिएबल जोड़कर occlusion_object.frag को अपडेट करें:

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

गहराई से जानने और 'अक्लूज़न' लागू करने के लिए, अब occlusion_object.frag में main() को अपडेट करें. फ़ाइल के निचले हिस्से में ये लाइनें जोड़ें:

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

अब आपके पास अपने ऑब्जेक्ट शेडर का नया वर्शन है, तो आपके पास रेंडरर कोड में बदलाव करने का विकल्प होता है.

ऑब्जेक्ट को सामने लाने की प्रोसेस रेंडर करना

इसके बाद, src/main/java/com/google/ar/core/codelab/common/rendering/ObjectRenderer.java में मौजूद ObjectRenderer क्लास की कॉपी बनाएं.

  • 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 का इस्तेमाल कब किया जाए. जहां virtualObject कॉन्फ़िगर किया गया है वहां नीचे दिए गए onSurfaceCreated तरीके में ये लाइनें जोड़ें:
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);
}

इन बदलावों के बाद, अब ऐप्लिकेशन को वर्चुअल ऑब्जेक्ट रोकने की सुविधा के साथ चलाया जा सकता है!

अब आपका ऐप्लिकेशन सभी फ़ोन पर अच्छी तरह से काम करना चाहिए. साथ ही, ऐप्लिकेशन के साथ काम करने पर, 'हर जगह शामिल होने की गहराई' अपने-आप काम करना चाहिए.

डेप्थ एपीआई की सुविधा के साथ चल रहा ऐप्लिकेशन

डेप्थ एपीआई की सुविधा के बिना ऐप्लिकेशन चल रहा है

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 डिवाइस को प्लग-इन करें.
  2. फ़ाइल > चुनें बनाएं और चलाएं.
  3. इस रूप में सेव करें: ARCodeLab.apk.
  4. ऐप्लिकेशन के बनने और उसे आपके डिवाइस पर डिप्लॉय होने का इंतज़ार करें.

जब आप पहली बार अपने डिवाइस पर ऐप्लिकेशन को डिप्लॉय करने की कोशिश करेंगे, तो:

  • आपको डिवाइस पर यूएसबी डीबग करने की अनुमति देनी होगी. जारी रखने के लिए, 'ठीक है' को चुनें.
  • आपसे पूछा जाएगा कि ऐप्लिकेशन को डिवाइस के कैमरे का इस्तेमाल करने की अनुमति है या नहीं. एआर की सुविधा का इस्तेमाल जारी रखने के लिए, इसे ऐक्सेस करने की अनुमति दें.

अपने ऐप्लिकेशन की जांच करना

ऐप्लिकेशन चलाते समय, डिवाइस को पकड़कर, उसके आस-पास की जगह पर जाकर, और धीरे-धीरे किसी जगह को स्कैन करके ऐप्लिकेशन की सामान्य सेटिंग की जांच की जा सकती है. अगले चरण पर जाने से पहले, कम से कम 10 सेकंड का डेटा इकट्ठा करें और जगह को कई दिशाओं से स्कैन करें.

समस्या हल करने से जुड़ी जानकारी

अपने Android डिवाइस को डेवलपमेंट के लिए सेट करना

  1. अपने डिवाइस को यूएसबी केबल के ज़रिए अपनी डेवलपमेंट मशीन से कनेक्ट करें. अगर Windows का इस्तेमाल करके डेवलप किया जाता है, तो आपको अपने डिवाइस के लिए सही यूएसबी ड्राइवर इंस्टॉल करना पड़ सकता है.
  2. डेवलपर के लिए सेटिंग और टूल की विंडो में, यूएसबी डीबग करना चालू करने के लिए यह तरीका अपनाएं:
  3. Settings ऐप्लिकेशन खोलें.
  4. अगर आपके डिवाइस में Android 8.0 या इसके बाद वाले वर्शन का इस्तेमाल किया जा रहा है, तो सिस्टम चुनें. अगर ऐसा नहीं है, तो अगले चरण पर जाएं.
  5. स्क्रोल करके सबसे नीचे जाएं और फ़ोन के बारे में जानकारी को चुनें.
  6. स्क्रोल करके नीचे जाएं और बिल्ड नंबर पर सात बार टैप करें.
  7. पिछली स्क्रीन पर वापस जाएं, स्क्रोल करके सबसे नीचे जाएं, और डेवलपर के लिए सेटिंग और टूल पर टैप करें.
  8. डेवलपर के लिए सेटिंग और टूल की विंडो में, यूएसबी डीबग करना को ढूंढने और उसे चालू करने के लिए नीचे स्क्रोल करें.

आपको Google की Android डेवलपर वेबसाइट पर इस प्रोसेस के बारे में ज़्यादा जानकारी मिल सकती है.

cfa20a722a68f54f.png

अगर आपको लाइसेंस की वजह से बिल्ड फ़ेल हो जाता है (कुछ लाइसेंस स्वीकार नहीं किए गए हैं, तो यहां दिए गए Android SDK पैकेज इंस्टॉल नहीं हो पाए), तो इन लाइसेंस की समीक्षा करने और उन्हें स्वीकार करने के लिए, यहां दिए गए निर्देशों का इस्तेमाल करें:

cd <path to Android SDK>

tools/bin/sdkmanager --licenses

11. बधाई हो

बधाई हो, आपने Google के ARCore depth API का इस्तेमाल करके, अपना पहला डेप्थ-आधारित ऑगमेंटेड रिएलिटी (एआर) ऐप्लिकेशन बना लिया है और उसे चला लिया है!

अक्सर पूछे जाने वाले सवाल