Sådan oprettes en Augmented Reality Android-app med ARCore og Android Studio

Denne artikel blev oprindeligt sendt her

I det forrige indlæg forklarede jeg, hvad ARCore er, og hvordan det hjælper udviklere med at oprette fantastiske augmented reality-apps uden behov for at forstå OpenGL- eller Matrix- matematik.

Hvis du ikke har tjekket det ud endnu, kan jeg varmt anbefale at gøre det, før du går videre med denne artikel og dykke ned i ARCore-appudvikling.

Oversigt

Ifølge Wikipedia er ARCore et softwareudviklingssæt udviklet af Google, der gør det muligt at bygge augmented reality-applikationer.

ARCore bruger tre nøgleteknologier til at integrere virtuelt indhold med det virkelige miljø:

  1. Motion Tracking: det giver telefonen mulighed for at forstå sin position i forhold til verden.
  2. Miljøforståelse: Dette gør det muligt for telefonen at registrere størrelsen og placeringen af ​​alle typer overflader, lodret, vandret og vinklet.
  3. Lysestimering: det giver telefonen mulighed for at estimere miljøets aktuelle lysforhold.

Kom godt i gang

For at komme i gang med ARCore-appudvikling skal du først aktivere ARCore i dit projekt. Dette er simpelt, da vi bruger Android Studio og Sceneform SDK. Der er to store operationer, som Sceneform udfører automatisk:

  1. Kontrollerer tilgængelighed af ARCore
  2. Beder om tilladelse til kameraet

Du behøver ikke bekymre dig om disse to trin, når du opretter en ARCore-app ved hjælp af Sceneform SDK. Men du skal medtage Sceneform SDK i dit projekt.

Opret et nyt Android Studio-projekt, og vælg en tom aktivitet.

Tilføj følgende afhængighed til din projektniveau build.gradle-fil:

dependencies { classpath 'com.google.ar.sceneform:plugin:1.5.0'}

Tilføj følgende til din app-niveau build.gradle-fil:

implementation "com.google.ar.sceneform.ux:sceneform-ux:1.5.0"

Synkroniser nu projektet med Gradle-filer, og vent til build er afsluttet. Dette installerer Sceneform SDK til projektet og Sceneform-plugin til AndroidStudio . Det hjælper dig med at se. sfb filer. Disse filer er de 3D-modeller, der gengives i dit kamera. Det hjælper dig også med at importere, se og opbygge 3D-aktiver .

Byg din første ARCore-app

Nu med vores Android Studio- opsætning komplet og Sceneform SDK installeret, kan vi komme i gang med at skrive vores allerførste ARCore-app.

Først skal vi tilføje Sceneform-fragmentet til vores layoutfil. Dette vil være scenen, hvor vi placerer alle vores 3D-modeller. Det tager sig af kameraets initialisering og tilladelseshåndtering.

Gå over til din hovedlayoutfil. I mit tilfælde er det activity_main.xml og tilføj Sceneform-fragmentet:

Jeg har indstillet bredden og højden, så den passer til forældrene, da dette dækker hele min aktivitet. Du kan vælge dimensionerne efter dine behov.

Kompatibilitetskontrol

Dette er alt hvad du skal gøre i layoutfilen. Gå nu over til java-filen, i mit tilfælde er MainActivity.java. Tilføj nedenstående metode i din klasse:

public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { Log.e(TAG, "Sceneform requires Android N or later"); Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show(); activity.finish(); return false; } String openGlVersionString = ((ActivityManager) 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;}

Denne metode kontrollerer, om din enhed kan understøtte Sceneform SDK eller ej. SDK kræver Android API niveau 27 eller nyere og OpenGL ES version 3.0 eller nyere. Hvis en enhed ikke understøtter disse to, ville scenen ikke blive gengivet, og din applikation viser en tom skærm.

Selvom du stadig kan fortsætte med at levere alle de andre funktioner i din app, som ikke kræver Sceneform SDK.

Nu med kontrol af enhedskompatibilitet fuldført, skal vi bygge vores 3D-model og vedhæfte den til scenen.

Tilføjelse af aktiverne

Du bliver nødt til at tilføje de 3D-modeller, der gengives på din skærm. Nu kan du selv bygge disse modeller, hvis du er fortrolig med oprettelse af 3D-modeller. Eller du kan besøge Poly.

Der finder du et stort lager af 3D-aktiver at vælge imellem. De er gratis at downloade. Bare kredit skaberen, og du er god til at gå.

I Android Studio skal du udvide din app-mappe, der er tilgængelig i projektruden til venstre. Du vil bemærke en " sampledata " -mappe. Denne mappe indeholder alle dine 3D-modelaktiver. Opret en mappe til din model inde i eksempeldatamappen.

Når du downloader zip-filen fra poly, finder du sandsynligvis 3 filer.

  1. .mtl-fil
  2. .obj-fil
  3. .png-fil

Den vigtigste af disse 3 er .obj-filen. Det er din egentlige model. Placer alle de 3 filer i samplede data -> “din models folde r”.

Højreklik nu på .obj- filen . Den første mulighed ville være at importere Sceneform Asset. Klik på det, skift ikke standardindstillingerne, bare klik på finish i det næste vindue. Din gradle synkroniseres for at inkludere aktivet i aktivmappen. Når gradlebygningen er færdig, er du klar til at gå.

Du er færdig med at importere et 3D-aktiv, der bruges af Sceneform i dit projekt. Næste , lad os bygge aktiv fra vores kode og medtage den i den scene.

Opbygning af modellen

Føj følgende kode til din MainActivity.java-fil (eller hvad det end er i dit tilfælde). Bare rolig, jeg forklarer al koden linje for linje:

private static final String TAG = MainActivity.class.getSimpleName();private static final double MIN_OPENGL_VERSION = 3.0;
ArFragment arFragment;ModelRenderable lampPostRenderable;
@[email protected]({"AndroidApiChecker", "FutureReturnValueIgnored"})
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (!checkIsSupportedDeviceOrFinish(this)) { return; } setContentView(R.layout.activity_main); arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
 ModelRenderable.builder() .setSource(this, Uri.parse("LampPost.sfb")) .build() .thenAccept(renderable -> lampPostRenderable = renderable) .exceptionally(throwable -> { Toast toast = Toast.makeText(this, "Unable to load andy renderable", Toast.LENGTH_LONG); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); return null; });
}

First, we find the arFragment that we included in the layout file. This fragment is responsible for hosting the scene. You can think of it as the container of our scene.

Next, we are using the ModelRenderable class to build our model. With the help of setSource method, we load our model from the .sfb file. This file was generated when we imported the assets. thenAccept method receives the model once it is built. We set the loaded model to our lampPostRenderable.

For error handling, we have .exceptionally method. It is called in case an exception is thrown.

All this happens asynchronously, hence you don’t need to worry about multi-threading or deal with handlers XD

With the model loaded and stored in the lampPostRenderable variable, we’ll now add it to our scene.

Adding the Model to Scene

The arFragment hosts our scene and will receive the tap events. So we need to set the onTap listener to our fragment to register the tap and place an object accordingly. Add the following code to onCreate method:

arFragment.setOnTapArPlaneListener( (HitResult hitresult, Plane plane, MotionEvent motionevent) -> { if (lampPostRenderable == null){ return; }
 Anchor anchor = hitresult.createAnchor(); AnchorNode anchorNode = new AnchorNode(anchor); anchorNode.setParent(arFragment.getArSceneView().getScene());
 TransformableNode lamp = new TransformableNode(arFragment.getTransformationSystem()); lamp.setParent(anchorNode); lamp.setRenderable(lampPostRenderable); lamp.select(); });

We set the onTapArPlaneListener to our AR fragment. Next what you see is the Java 8 syntax, in case you are not familiar with it, I would recommend checking out this guide.

First, we create our anchor from the HitResult using hitresult.createAnchor() and store it in an Anchor object.

Next, create a node out of this anchor. It will be called AnchorNode. It will be attached to the scene by calling the setParent method on it and passing the scene from the fragment.

Now we create a TransformableNode which will be our lamppost and set it to the anchor spot or our anchor node. The node still doesn’t have any information about the object it has to render. We’ll pass that object using lamp.setRenderable method which takes in a renderable as it’s parameter. Finally call lamp.select();

Phew!! Too much terminology there, but don’t worry, I’ll explain it all.

  1. Scene: This is the place where all your 3D objects will be rendered. This scene is hosted by the AR Fragment which we included in the layout. An anchor node is attached to this screen which acts as the root of the tree and all the other objects are rendered as its objects.
  2. HitResult: This is an imaginary line (or a ray) coming from infinity which gives the point of intersection of itself with a real-world object.
  3. Anchor: An anchor is a fixed location and orientation in the real world. It can be understood as the x,y,z coordinate in the 3D space. You can get an anchor’s post information from it. Pose is the position and orientation of the object in the scene. This is used to transform the object’s local coordinate space into real-world coordinate space.
  4. AnchorNode: This is the node that automatically positions itself in the world. This is the first node that gets set when the plane is detected.
  5. TransformableNode: It is a node that can be interacted with. It can be moved around, scaled rotated and much more. In this example, we can scale the lamp and rotate it. Hence the name Transformable.

There is no rocket science here. It’s really simple. The entire scene can be viewed as a graph with Scene as the parent, AnchorNode as its child and then branching out different nodes/objects to be rendered on the screen.

Your final MainActivity.java must look something like this:

package com.ayusch.arcorefirst;
import android.app.Activity;import android.app.ActivityManager;import android.content.Context;import android.net.Uri;import android.os.Build;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.Gravity;import android.view.MotionEvent;import android.widget.Toast;
import com.google.ar.core.Anchor;import com.google.ar.core.HitResult;import com.google.ar.core.Plane;import com.google.ar.sceneform.AnchorNode;import com.google.ar.sceneform.rendering.ModelRenderable;import com.google.ar.sceneform.ux.ArFragment;import com.google.ar.sceneform.ux.TransformableNode;
public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private static final double MIN_OPENGL_VERSION = 3.0;
 ArFragment arFragment; ModelRenderable lampPostRenderable;
 @Override @SuppressWarnings({"AndroidApiChecker", "FutureReturnValueIgnored"}) protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (!checkIsSupportedDeviceOrFinish(this)) { return; } setContentView(R.layout.activity_main); arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
 ModelRenderable.builder() .setSource(this, Uri.parse("LampPost.sfb")) .build() .thenAccept(renderable -> lampPostRenderable = renderable) .exceptionally(throwable -> { Toast toast = Toast.makeText(this, "Unable to load andy renderable", Toast.LENGTH_LONG); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); return null; });
 arFragment.setOnTapArPlaneListener( (HitResult hitresult, Plane plane, MotionEvent motionevent) -> { if (lampPostRenderable == null){ return; }
 Anchor anchor = hitresult.createAnchor(); AnchorNode anchorNode = new AnchorNode(anchor); anchorNode.setParent(arFragment.getArSceneView().getScene());
 TransformableNode lamp = new TransformableNode(arFragment.getTransformationSystem()); lamp.setParent(anchorNode); lamp.setRenderable(lampPostRenderable); lamp.select(); } );
 }
 public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { Log.e(TAG, "Sceneform requires Android N or later"); Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show(); activity.finish(); return false; } String openGlVersionString = ((ActivityManager) 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; }}

Congratulations!! You’ve just completed your first ARCore app. Start adding objects and see them come alive in the real world!

This was your first look into how to create a simple ARCore app from scratch with Android studio. In the next tutorial, I would be going deeper into ARCore and adding more functionality to the app.

If you have any suggestions or any topic you would want a tutorial on, just mention in the comments section and I’ll be happy to oblige.

Kan du lide det, du læser? Glem ikke at dele dette indlæg på Facebook , Whatsapp og LinkedIn .

Du kan følge mig på LinkedIn, Quora, Twitter og Instagram, hvor jeg besvarer spørgsmål relateret til mobiludvikling , især Android og Flutter .