ARCore Basics
Augmented Reality basically allows us to show virtual objects in a real-world environment and allow interactions with them.
ARCore requires :
- Android N+
- OpenGL 3.0 +
Knowledge in OpenGL 3d graphics programming is not necessary since Sceneform SDK is introduced for the same.
Sceneform allows us to create 3d models. It consists of ShapeFactory and MaterialFactory classes that allow us to create renderable objects from simple shapes and materials.
Shapes like a sphere, cube, cylinder can easily we created as we shall see later in this tutorial.
ARFragment
is used to create an Ar scene and AR Session (both are created automatically).
Using an ARFragment
we detect planes or feature points.
setOnTapArPlaneListener is set on the ArFragment to listen to changes whenever the click event takes place.
Few terms that form of the core of ARCore are listed below with descriptions:
- Scene – This is where our 3D objects are rendered.
- HitResult – On tap, this provides us the location of a real-world object. Basically, it estimates the position by finding the first point of intersection with an imaginary ray of light coming from infinity.
- Anchor – A fixed location in real-world in terms of x,y,z coordinates in 3D space. Just like a ship anchor
- Pose – provides the position and orientation of the object in the scene.
- AnchorNode – This is the node that automatically positions itself in the world. This is the first node that gets set when a plane is detected.
- TransformableNode – This node is where we set our 3d object. It can interact, scale, transform, rotate according to the user interactions.
In the next section, we’ll be developing our AR application in which we’ll calculate the distance of an object from us i.e the camera.
Project Structure

Android Arcore Project Structure
Add the following dependency to the build.gradle
:
implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.10.0'
In the AndroidManifest file we need to add the following:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
android:name="android.hardware.camera.ar"
android:required="true" />
Inside the application tag add the following:
<meta-data
android:name="com.google.ar.core"
android:value="required" />
Code
The code for the activity_main.xml
is given below:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/ux_fragment"
android:name="com.google.ar.sceneform.ux.ArFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/tvDistance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@android:color/black"
android:gravity="center"
android:padding="8dp"
android:text="Distance from camera"
android:textColor="@android:color/white"
android:textSize="20sp" />
</FrameLayout>
The code for the MainActivity.java is given below:
package com.journaldev.androidarcoredistancecamera;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import com.google.ar.core.Anchor;
import com.google.ar.core.Frame;
import com.google.ar.core.Pose;
import com.google.ar.sceneform.AnchorNode;
import com.google.ar.sceneform.FrameTime;
import com.google.ar.sceneform.Scene;
import com.google.ar.sceneform.math.Vector3;
import com.google.ar.sceneform.rendering.Color;
import com.google.ar.sceneform.rendering.MaterialFactory;
import com.google.ar.sceneform.rendering.ModelRenderable;
import com.google.ar.sceneform.rendering.ShapeFactory;
import com.google.ar.sceneform.ux.ArFragment;
import com.google.ar.sceneform.ux.TransformableNode;
import java.util.Objects;
public class MainActivity extends AppCompatActivity implements Scene.OnUpdateListener {
private static final double MIN_OPENGL_VERSION = 3.0;
private static final String TAG = MainActivity.class.getSimpleName();
private ArFragment arFragment;
private AnchorNode currentAnchorNode;
private TextView tvDistance;
ModelRenderable cubeRenderable;
private Anchor currentAnchor = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!checkIsSupportedDeviceOrFinish(this)) {
Toast.makeText(getApplicationContext(), "Device not supported", Toast.LENGTH_LONG).show();
}
setContentView(R.layout.activity_main);
arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
tvDistance = findViewById(R.id.tvDistance);
initModel();
arFragment.setOnTapArPlaneListener((hitResult, plane, motionEvent) -> {
if (cubeRenderable == null)
return;
// Creating Anchor.
Anchor anchor = hitResult.createAnchor();
AnchorNode anchorNode = new AnchorNode(anchor);
anchorNode.setParent(arFragment.getArSceneView().getScene());
clearAnchor();
currentAnchor = anchor;
currentAnchorNode = anchorNode;
TransformableNode node = new TransformableNode(arFragment.getTransformationSystem());
node.setRenderable(cubeRenderable);
node.setParent(anchorNode);
arFragment.getArSceneView().getScene().addOnUpdateListener(this);
arFragment.getArSceneView().getScene().addChild(anchorNode);
node.select();
});
}
public boolean checkIsSupportedDeviceOrFinish(final Activity activity) {
String openGlVersionString =
((ActivityManager) Objects.requireNonNull(activity.getSystemService(Context.ACTIVITY_SERVICE)))
.getDeviceConfigurationInfo()
.getGlEsVersion();
if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) {
Log.e(TAG, "Sceneform requires OpenGL ES 3.0 later");
Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG)
.show();
activity.finish();
return false;
}
return true;
}
private void initModel() {
MaterialFactory.makeTransparentWithColor(this, new Color(android.graphics.Color.RED))
.thenAccept(
material -> {
Vector3 vector3 = new Vector3(0.05f, 0.01f, 0.01f);
cubeRenderable = ShapeFactory.makeCube(vector3, Vector3.zero(), material);
cubeRenderable.setShadowCaster(false);
cubeRenderable.setShadowReceiver(false);
});
}
private void clearAnchor() {
currentAnchor = null;
if (currentAnchorNode != null) {
arFragment.getArSceneView().getScene().removeChild(currentAnchorNode);
currentAnchorNode.getAnchor().detach();
currentAnchorNode.setParent(null);
currentAnchorNode = null;
}
}
@Override
public void onUpdate(FrameTime frameTime) {
Frame frame = arFragment.getArSceneView().getArFrame();
Log.d("API123", "onUpdateframe... current anchor node " + (currentAnchorNode == null));
if (currentAnchorNode != null) {
Pose objectPose = currentAnchor.getPose();
Pose cameraPose = frame.getCamera().getPose();
float dx = objectPose.tx() - cameraPose.tx();
float dy = objectPose.ty() - cameraPose.ty();
float dz = objectPose.tz() - cameraPose.tz();
///Compute the straight-line distance.
float distanceMeters = (float) Math.sqrt(dx * dx + dy * dy + dz * dz);
tvDistance.setText("Distance from camera: " + distanceMeters + " metres");
/*float[] distance_vector = currentAnchor.getPose().inverse()
.compose(cameraPose).getTranslation();
float totalDistanceSquared = 0;
for (int i = 0; i < 3; ++i)
totalDistanceSquared += distance_vector[i] * distance_vector[i];*/
}
}
}
In the above code we do the following things:
- Check if the phone is AR compatible.
- Create a 3d cube shaped model.
- Add it on tap once the plane is detected.
- Update the distance from the camera to the anchor in every frame.
- Once you tap again, clear the previous anchor.
The output of the application in action is given below:

Android Arcore Distance Camera Output