Fundamentals of ARCore Android

Introduction

 
Before talking about ARCore, the first question that arises is, "What is AR?" Well, AR stands for Augmented Reality. According to Wikipedia,  "Augmented reality (AR) is an interactive experience of a real-world environment where the objects that reside in the real world are enhanced by computer-generated perceptual information, sometimes across multiple sensory modalities, including visual, auditory, haptic, somatosensory and olfactory."
 
ARCore is a Google platform used to make AR-based applications using its predefined APIs. Through it, we can use different APIs to sense the environment and understand the world using motion and light detection and process that information to enable smooth AR experience.

ARCore has capabilities to sense the environment and three key capabilities to integrate virtual content with the real world as seen through your phone's camera:
  • Motion tracking
  • Environmental understanding 
  • Light estimation

Motion tracking


Users use the phone's camera to see the real world and the device must be rotated and it changes orientation, angles, position, etc to get better clicks. Since this device's motion is taken into consideration to track motion,  ARCore uses a process called concurrent odometry and mapping, or COM, to understand where the phone is relative to the world around it. Some visually distinct features detected by ARCore in the image captured by the camera are called feature points and it uses these points to compute its change in location. 
 
The main objective is to capture device orientation and position in the real world with respect to time and this information is used and combined with inertial measurements from the device's IMU to estimate the pose (position and orientation).

If the device's camera orientation and position are aligned to the virtual camera provided by ARCore then users are able to render the 3D image over a virtual camera. That rendered virtual image must be overlayed over the top of the image clicked by the device camera.
 

Environmental understanding

 
ARCore is constantly improving its understanding of the real world through feature point detection and surface detection as well. ARCore looks for the group or cluster of points that show on a vertical or horizontal surface like a table, floor, and walls, etc. After detection and manipulating ARCore enables these surfaces as a plane to render the objects in the real world over the camera. Objects must be shown over these planes.

Note that white walls and floors without textures are difficult to detect surfaces, and it will not enable them as planes.
 

Light Estimation

 
ARCore is so advanced that it can detect lighting in the surroundings, so accordingly, it gives a sense of realism while rendering objects. Based on the light data, it provides the controls to enhance or alter color combinations of the rendered scene.

User Interaction

Since the device screen can be regarded as an (x,y) plane where ever a user interacts or touches, it will give the coordinates of that screen. A ray is formed that intersects the planes and these feature points are returned that are passed by ray along with the pose of that intersection in world space.
 
Oriented Points

Since we know that ARCore uses clusters of points to detect the angled surface, oriented points let us place objects on angled surfaces. When a user tests or moves the device around them, it looks for nearby points and uses that data to sense the angle of the surface.
 
Anchors and trackables

Poses get continuously changed because ARCore is continuously estimating the surface and angles in the real world. When you want to place a virtual object, than the anchor must be defined first and relative to that anchor object should be placed. Planes and points are a special type of object called a trackable. Be sure that trackable and object relation must be stable even if the device rotates or changes pose over time. For example, if you have placed an object on a desk, even if the device position changes, then the object must be in a stable state or appear on the table.
 
Augumented Image
 
An augmented image is a feature used to enhance reality. For example, take a 2D poster. Then, if the camera is placed over the poster, its characters would be popped out and enact a scene that seems to be a 3D experience. 
 
AR principles
  • Designing environments
  • Creating and manipulating virtual objects
  • Real-world movement
  • Making user interfaces
Runtime Consideration
 
You can provide the best possible user experience by ensuring that your app:
  • Provides clear feedback to users
  • Encourages them to move their device
  • Shows them how to interact with their device to experience AR
Encourage users to move the camera slowly

Since ARCore requires visual information and motion information as well, rapid device movement can cause the camera image to become blurry, reducing ARCore's ability to track and detect features. Mostly, data relies on IMU(Inertial Measurement Unit) information to distinguish a pose from the real world surroundings. So, avoid extended periods of rapid movement, which can cause ARCore to lose tracking and prevent the detection of features.
 
Enable ARCore on Android Studio

First, we need to update our Android Studio IDE to 3.1 or higher. Currently, I have updated to 3.6.1, so you guys must update to the latest version of Android Studio.
 
Secondly, update the SDK platform to 7.0 or API level 24 or higher.

We have to follow some steps to enable ARCore in our project:
  • Add AR Required or AR Optional entries to the manifest
  • Add build dependencies to your project
  • Perform runtime checks to ensure the device is ARCore-supported, that Google Play Services for AR is installed on it, and that camera permission has been granted
  • Make sure your app complies with ARCore's User Privacy Requirements
AR Optional Apps
  • AR optional apps are those apps that can run on devices that don't support ARCore.
  • Google play store doesn't install automatically Google play services for AR
AndroidManifest.xml 
  1. <!-- "AR Optional" apps must declare minSdkVersion ≥ 14 -->  
  2. <uses-sdk android:minSdkVersion="24" />  
  3.   
  4. <uses-permission android:name="android.permission.CAMERA" />  
  5.   
  6. <application>  
  7.     …  
  8.     <!-- Indicates that the app supports, but does not require ARCore ("AR Optional").  
  9.          Unlike "AR Required" apps, the Google Play Store will not automatically  
  10.          download and install Google Play Services for AR when the app is installed.  
  11.     -->  
  12.     <meta-data android:name="com.google.ar.core" android:value="optional" />  
  13. </application>  
AR Required apps
  • AR Required apps are those apps that can run on devices that support ARCore and Google play store list them for supported devices only.
  • Google play store install automatically Google play services for AR
  1. <!-- "AR Required" apps must declare minSdkVersion ≥ 24 -->  
  2. <uses-sdk android:minSdkVersion="24" />  
  3.   
  4. <uses-permission android:name="android.permission.CAMERA" />  
  5.   
  6. <!-- Indicates that the app requires ARCore ("AR Required"). Ensures the app is  
  7.      visible only in the Google Play Store on devices that support ARCore.  
  8. -->  
  9. <uses-feature android:name="android.hardware.camera.ar" />  
  10.   
  11. <application>  
  12.     …  
  13.     <!-- Indicates that the app requires ARCore ("AR Required"). Causes the Google  
  14.          Play Store to download and install Google Play Services for AR when the  
  15.          app is installed.  
  16.     -->  
  17.     <meta-data android:name="com.google.ar.core" android:value="required" />  
  18. </application>  
Add gradle dependencies to your app's build.gradle and add Google's maven to project's build.gradle

build.gradle project level
  1. // Top-level build file where you can add configuration options common to all sub-projects/modules.  
  2.   
  3. buildscript {  
  4.     repositories {  
  5.         jcenter()  
  6.         google()  
  7.     }  
  8.     dependencies {  
  9.         classpath 'com.android.tools.build:gradle:3.6.1'  
  10.   
  11.         // NOTE: Do not place your application dependencies here; they belong  
  12.         // in the individual module build.gradle files  
  13.     }  
  14. }  
  15.   
  16. allprojects {  
  17.     repositories {  
  18.         jcenter()  
  19.         google()  
  20.     }  
  21. }  
  22.   
  23. task clean(type: Delete) {  
  24.     delete rootProject.buildDir  
  25. }  
 Now see the app level build.gradle 
  1. apply plugin: 'com.android.application'  
  2. apply plugin: 'com.google.gms.google-services'  
  3.   
  4. repositories {  
  5.   
  6.     maven { url 'https://maven.fabric.io/public' }  
  7.     maven { url "https://maven.google.com" }  
  8.   
  9. }  
  10.   
  11. buildscript {  
  12.     repositories {  
  13.         mavenCentral()  
  14.         maven { url "https://maven.google.com" }  
  15.   
  16.     }  
  17. }  
  18.   
  19. android {  
  20.     compileSdkVersion 28  
  21.   
  22.     defaultConfig {  
  23.         applicationId "com.example.com"  
  24.         minSdkVersion 24  
  25.         targetSdkVersion 28  
  26.         versionCode 28  
  27.         versionName "1.0.0"  
  28.       
  29.     }  
  30.     buildTypes {  
  31.         release {  
  32.             minifyEnabled false  
  33.             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
  34.         }  
  35.     }  
  36.   
  37.     packagingOptions {  
  38.         exclude 'META-INF/DEPENDENCIES.txt'  
  39.         exclude 'META-INF/LICENSE.txt'  
  40.         exclude 'META-INF/NOTICE.txt'  
  41.         exclude 'META-INF/NOTICE'  
  42.         exclude 'META-INF/LICENSE'  
  43.         exclude 'META-INF/DEPENDENCIES'  
  44.         exclude 'META-INF/notice.txt'  
  45.         exclude 'META-INF/license.txt'  
  46.         exclude 'META-INF/dependencies.txt'  
  47.         exclude 'META-INF/LGPL2.1'  
  48.     }  
  49.   
  50.      
  51. }  
  52.   
  53. dependencies {  
  54.     implementation 'com.android.support.constraint:constraint-layout:1.0.2'  
  55.     implementation fileTree(include: ['*.jar'], dir: 'libs')  
  56.     implementation 'com.android.support:support-v4:28.0.0'  
  57.     testImplementation 'junit:junit:4.12'  
  58.     implementation 'com.android.support:appcompat-v7:28.0.0'  
  59.   
  60.     // this is dependency for enabling ARCore  
  61.     implementation 'com.google.ar:core:1.15.0'  
  62.   
  63.      
  64. }  
Perform runtime checks

Check whether ARCore is supported (AR Optional apps only). Let's see the code of MainActivity.java
  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.   super.onCreate(savedInstanceState);  
  4.   
  5.   // Enable AR related functionality on ARCore supported devices only.  
  6.   maybeEnableArButton();  
  7.   …  
  8. }  
  9.   
  10. void maybeEnableArButton() {  
  11.   ArCoreApk.Availability availability = ArCoreApk.getInstance().checkAvailability(this);  
  12.   if (availability.isTransient()) {  
  13.     // Re-query at 5Hz while compatibility is checked in the background.  
  14.     new Handler().postDelayed(new Runnable() {  
  15.       @Override  
  16.       public void run() {  
  17.         maybeEnableArButton();  
  18.       }  
  19.     }, 200);  
  20.   }  
  21.   if (availability.isSupported()) {  
  22.     mArButton.setVisibility(View.VISIBLE);  
  23.     mArButton.setEnabled(true);  
  24.     // indicator on the button.  
  25.   } else { // Unsupported or unknown.  
  26.     mArButton.setVisibility(View.INVISIBLE);  
  27.     mArButton.setEnabled(false);  
  28.   }  
  29. }  
Request camera permission (AR Optional and AR Required apps)

If we do not request a permission model for Camera, then the app crash due to security issues.
  1. @Override  
  2. protected void onResume() {  
  3.   super.onResume();  
  4.   
  5.   // ARCore requires camera permission to operate.  
  6.   if (!CameraPermissionHelper.hasCameraPermission(this)) {  
  7.     CameraPermissionHelper.requestCameraPermission(this);  
  8.     return;  
  9.   }  
  10.   
  11.   …  
  12. }  
After requesting camera one should handle the response of the user if user has given consent or not.
  1. @Override  
  2. public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {  
  3.   if (!CameraPermissionHelper.hasCameraPermission(this)) {  
  4.     Toast.makeText(this"Camera permission is needed to run this application", Toast.LENGTH_LONG)  
  5.         .show();  
  6.     if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) {  
  7.       // Permission denied with checking "Do not ask again".  
  8.       CameraPermissionHelper.launchPermissionSettings(this);  
  9.     }  
  10.     finish();  
  11.   }  
  12. }  
Check whether Google Play Services for AR is installed (AR Optional and AR Required apps)
  1. // Set to true ensures requestInstall() triggers installation if necessary.  
  2. private boolean mUserRequestedInstall = true;  
  3.   
  4. @Override  
  5. protected void onResume() {  
  6.   super.onResume();  
  7.   
  8.   // Check camera permission.  
  9.   …  
  10.   
  11.   // Make sure Google Play Services for AR is installed and up to date.  
  12.   try {  
  13.     if (mSession == null) {  
  14.       switch (ArCoreApk.getInstance().requestInstall(this, mUserRequestedInstall)) {  
  15.         case INSTALLED:  
  16.           // Success, create the AR session.  
  17.           mSession = new Session(this);  
  18.           break;  
  19.         case INSTALL_REQUESTED:  
  20.           // Ensures next invocation of requestInstall() will either return  
  21.           // INSTALLED or throw an exception.  
  22.           mUserRequestedInstall = false;  
  23.           return;  
  24.       }  
  25.     }  
  26.   } catch (UnavailableUserDeclinedInstallationException e) {  
  27.     // Display an appropriate message to the user and return gracefully.  
  28.     Toast.makeText(this"TODO: handle exception " + e, Toast.LENGTH_LONG)  
  29.         .show();  
  30.     return;  
  31.   } catch (…) {  // Current catch statements.  
  32.     …  
  33.     return;  // mSession is still null.  
  34.   }  
  35.   …  
  36. }  
Note:
 
If requestInstall() returns INSTALL_REQUESTED, the current activity pauses and the user is prompted to install or update Google Play Services for AR. Now ARCore is fully enabled in Android Studio.