1. Panoramica
ARCore è una piattaforma per la creazione di app di realtà aumentata su Android. Augmented Images ti consente di creare app AR in grado di riconoscere immagini 2D preregistrate nel mondo reale e ancorare contenuti virtuali sopra di esse.
Questo codelab ti guida nella modifica di un'app di esempio ARCore esistente per incorporare immagini aumentate in movimento o fisse.
Cosa creerai
In questo codelab, utilizzerai un'app di esempio ARCore preesistente. Al termine del codelab, la tua app sarà in grado di:
- Rilevare un target immagine e collegare un labirinto virtuale al target
- Traccia il bersaglio in movimento finché si trova nel campo visivo della videocamera

È la prima volta che crei un'app ARCore?
Hai intenzione di scrivere codice di esempio in questo codelab o vuoi solo leggere queste pagine?
Obiettivi didattici
- Come utilizzare le immagini aumentate in ARCore in Java
- Come valutare la capacità di un'immagine di essere riconosciuta da ARCore
- Come allegare un contenuto virtuale a un'immagine e monitorarne il movimento
Prerequisiti
Per completare questo codelab, ti serviranno hardware e software specifici.
Requisiti hardware
- Un dispositivo supportato da ARCore collegato tramite cavo USB alla macchina di sviluppo
Requisiti software
- ARCore APK 1.9.0 o versioni successive. Questo APK viene normalmente installato automaticamente sul dispositivo tramite il Play Store.
- Una macchina di sviluppo con Android Studio (v3.1 o versioni successive)
- Accesso a internet, poiché dovrai scaricare le librerie durante lo sviluppo
Ora che hai tutto pronto, iniziamo.
2. Configura l'ambiente di sviluppo
Scarica l'SDK
Inizieremo scaricando l'SDK ARCore per Android più recente da GitHub. Decomprimilo nella posizione che preferisci. Per questo codelab, la versione dell'SDK meno recente è la 1.18.1. La cartella verrà chiamata arcore-android-sdk-x.xx.x, il valore esatto sarà la versione dell'SDK che utilizzi.
Avvia Android Studio e fai clic su Apri un progetto Android Studio esistente.

Vai a questa cartella decompressa:
arcore-android-sdk-x.xx.x/samples/augmented_image_java
Fai clic su Apri.
Attendi il completamento della sincronizzazione del progetto da parte di Android Studio. Se Android Studio non dispone dei componenti richiesti, potrebbe non funzionare e visualizzare il messaggio Install missing platform and sync project. Segui le istruzioni per risolvere il problema.
Esegui l'app di esempio
Ora che hai un progetto di app ARCore funzionante, eseguiamo un test.
Collega il dispositivo ARCore alla macchina di sviluppo e utilizza il menu Run (Esegui) > Run (Esegui) "app" per eseguire la versione di debug sul dispositivo. Nella finestra di dialogo che ti chiede di scegliere da quale dispositivo eseguire il test, scegli il dispositivo connesso e fai clic su Ok.


Questo progetto di esempio utilizza targetSdkVersion 28. Se si verifica un errore di build come Failed to find Build Tools revision 28.0.3, segui le istruzioni descritte in Android Studio per scaricare e installare la versione richiesta di Android Build Tools.
Se tutto va a buon fine, l'app di esempio verrà avviata sul dispositivo e ti chiederà l'autorizzazione per consentire a Immagine aumentata di scattare foto e registrare video. Tocca CONSENTI per concedere l'autorizzazione.
Test con un'immagine di esempio
Ora che hai configurato l'ambiente di sviluppo, puoi testare l'app fornendole un'immagine da analizzare.
In Android Studio, nella finestra Progetto, vai a app > assets e fai doppio clic sul file default.jpg per aprirlo.

Punta la fotocamera del dispositivo sull'immagine della Terra sullo schermo e segui le istruzioni per centrare l'immagine che stai scansionando nel mirino.
Sull'immagine verrà sovrapposto un riquadro, come questo:

Successivamente, apporteremo piccoli miglioramenti all'app di esempio.
3. Visualizzare un modello di labirinto nell'immagine 2D
Puoi iniziare a utilizzare le immagini aumentate visualizzando un modello 3D sopra l'immagine.
Scaricare un modello 3D
Per questo codelab utilizzeremo "Circle Maze - Green" di Evol, con licenza CC-BY 3.0. Ho memorizzato una copia di questo modello 3D nel repository GitHub di questo codelab, che puoi trovare qui.
Segui questi passaggi per scaricare il modello e includerlo in Android Studio.
- Vai alla directory third_party del repository GitHub di questo codelab.
- Fai clic su GreenMaze_obj.zip e poi sul pulsante Scarica.
Viene scaricato un file denominato GreenMaze_obj.zip.
- In Android Studio, crea la directory
green-mazein app > assets > models - Decomprimi
GreenMaze_obj.zipe copia i contenuti in questa posizione:arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze - In Android Studio, vai a app > assets > models > green-maze.
In questa cartella devono essere presenti due file: GreenMaze.obj e GreenMaze.mtl.

Visualizzare il modello del labirinto
Segui questi passaggi per visualizzare il modello 3D GreenMaze.obj sopra l'immagine 2D esistente.
In AugmentedImageRenderer.java, aggiungi una variabile membro chiamata mazeRenderer per eseguire il rendering del modello del labirinto. Poiché il labirinto deve essere collegato all'immagine, è opportuno inserire mazeRenderer all'interno della classe AugmentedImageRenderer.
AugmentedImageRenderer.java
// Add a member variable to hold the maze model.
private final ObjectRenderer mazeRenderer = new ObjectRenderer();
Nella funzione createOnGlThread(), carica GreenMaze.obj. Per semplicità, utilizza la stessa trama del frame.
AugmentedImageRenderer.java
// Replace the definition of the createOnGlThread() function with the
// following code, which loads GreenMaze.obj.
public void createOnGlThread(Context context) throws IOException {
mazeRenderer.createOnGlThread(
context, "models/green-maze/GreenMaze.obj", "models/frame_base.png");
mazeRenderer.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);
}
Sostituisci la definizione della funzione draw() con la seguente. In questo modo, le dimensioni del labirinto vengono adattate a quelle dell'immagine rilevata e il labirinto viene visualizzato sullo schermo.
AugmentedImageRenderer.java
// Adjust size of detected image and render it on-screen
public void draw(
float[] viewMatrix,
float[] projectionMatrix,
AugmentedImage augmentedImage,
Anchor centerAnchor,
float[] colorCorrectionRgba) {
float[] tintColor =
convertHexToColor(TINT_COLORS_HEX[augmentedImage.getIndex() % TINT_COLORS_HEX.length]);
final float mazeEdgeSize = 492.65f; // Magic number of maze size
final float maxImageEdgeSize = Math.max(augmentedImage.getExtentX(), augmentedImage.getExtentZ()); // Get largest detected image edge size
Pose anchorPose = centerAnchor.getPose();
float mazeScaleFactor = maxImageEdgeSize / mazeEdgeSize; // scale to set Maze to image size
float[] modelMatrix = new float[16];
// OpenGL Matrix operation is in the order: Scale, rotation and Translation
// So the manual adjustment is after scale
// The 251.3f and 129.0f is magic number from the maze obj file
// You mustWe need to do this adjustment because the maze obj file
// is not centered around origin. Normally when you
// work with your own model, you don't have this problem.
Pose mazeModelLocalOffset = Pose.makeTranslation(
-251.3f * mazeScaleFactor,
0.0f,
129.0f * mazeScaleFactor);
anchorPose.compose(mazeModelLocalOffset).toMatrix(modelMatrix, 0);
mazeRenderer.updateModelMatrix(modelMatrix, mazeScaleFactor, mazeScaleFactor/10.0f, mazeScaleFactor); // This line relies on a change in ObjectRenderer.updateModelMatrix later in this codelab.
mazeRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
}
Ora il labirinto dovrebbe essere visualizzato sopra l'immagine default.jpg della Terra.
Nota:poiché non hai il controllo completo di questo modello 3D di esempio, il codice riportato sopra utilizza alcuni numeri "magici". La dimensione del modello del labirinto è 492,65 x 120 x 492,65, con il centro in (251,3, 60, -129,0). L'intervallo dei valori delle coordinate X, Y e Z dei suoi vertici è rispettivamente [5,02, 497,67], [0, 120] e [-375,17, 117,25]. Pertanto, la scala del modello del labirinto deve essere image_size / 492.65. Il mazeModelLocalOffset viene introdotto perché il modello 3D del labirinto non è centrato sull'origine (0, 0, 0).
Il muro del labirinto è ancora un po' troppo alto per essere inserito sopra l'immagine. Crea una funzione helper updateModelMatrix() che possa scalare X, Y e Z in modo non uniforme per scalare l'altezza del labirinto di 0, 1. Tieni presente che devi conservare updateModelMatrix(float[] modelMatrix, float scaleFactor) esistente e aggiungere l'overload della funzione updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) come nuova funzione.
common/rendering/ObjectRenderer.java
// Scale X, Y, Z coordinates unevenly
public void updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) {
float[] scaleMatrix = new float[16];
Matrix.setIdentityM(scaleMatrix, 0);
scaleMatrix[0] = scaleFactorX;
scaleMatrix[5] = scaleFactorY;
scaleMatrix[10] = scaleFactorZ;
Matrix.multiplyMM(this.modelMatrix, 0, modelMatrix, 0, scaleMatrix, 0);
}
Esegui il codice. Ora il labirinto dovrebbe adattarsi perfettamente sopra l'immagine.

4. Aggiungi Andy al labirinto
Ora che hai un labirinto, aggiungi un personaggio che si muova al suo interno. Utilizza il file andy.obj incluso nell'SDK ARCore per Android. Mantieni la trama della cornice dell'immagine così com'è, perché è diversa dal labirinto verde visualizzato sopra l'immagine.
In AugmentedImageRenderer.java, aggiungi un ObjectRenderer privato per visualizzare Andy.
AugmentedImageRenderer.java
// Render for Andy
private final ObjectRenderer andyRenderer = new ObjectRenderer();
Successivamente, inizializza andyRenderer alla fine di createOnGlThread().
AugmentedImageRenderer.java
public void createOnGlThread(Context context) throws IOException {
// Initialize andyRenderer
andyRenderer.createOnGlThread(
context, "models/andy.obj", "models/andy.png");
andyRenderer.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);
}
Infine, esegui il rendering di Andy in cima al labirinto alla fine della funzione draw().
AugmentedImageRenderer.java
public void draw(
float[] viewMatrix,
float[] projectionMatrix,
AugmentedImage augmentedImage,
Anchor centerAnchor,
float[] colorCorrectionRgba) {
// Render Andy, standing on top of the maze
Pose andyModelLocalOffset = Pose.makeTranslation(
0.0f,
0.1f,
0.0f);
anchorPose.compose(andyModelLocalOffset).toMatrix(modelMatrix, 0);
andyRenderer.updateModelMatrix(modelMatrix, 0.05f); // 0.05f is a Magic number to scale
andyRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
}
Esegui il codice. Dovresti vedere Andy in cima al labirinto.

Determinare la qualità dell'immagine target
ARCore si basa su caratteristiche visive per riconoscere le immagini. A causa delle differenze di qualità, non tutte le immagini possono essere riconosciute facilmente.
arcoreimg è uno strumento a riga di comando che ti consente di determinare il livello di riconoscibilità di un'immagine per ARCore. Restituisce un numero compreso tra 0 e 100, dove 100 è il più facile da riconoscere.
Ecco un esempio:
arcore-android-sdk-x.xx.x/tools/arcoreimg/macos$
$ ./arcoreimg eval-img --input_image_path=/Users/username/maze.jpg
100
maze.jpg ha un valore di 100, quindi viene facilmente riconosciuto da ARCore.
5. (Facoltativo) Fai muovere Andy nel labirinto
Infine, puoi aggiungere del codice per far muovere Andy nel labirinto. Ad esempio, utilizza il motore fisico open source jBullet per gestire la simulazione fisica. Puoi saltare questa parte.
Scarica PhysicsController.java e aggiungilo al tuo progetto nella directory
arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/java/com/google/ar/core/examples/java/augmentedimage/
In Android Studio, aggiungi GreenMaze.obj alla directory project assets, in modo che possa essere caricato in fase di runtime. Copia GreenMaze.obj da app > assets > models > green-maze a app > assets.
Aggiungi le seguenti dipendenze al file build.gradle dell'app.
app/build.gradle
// jbullet library
implementation 'cz.advel.jbullet:jbullet:20101010-1'
Definisci una variabile andyPose per memorizzare la posizione della posa attuale di Andy.
AugmentedImageRenderer.java
// Create a new pose for the Andy
private Pose andyPose = Pose.IDENTITY;
Modifica AugmentedImageRenderer.java per eseguire il rendering di Andy utilizzando la nuova variabile andyPose.
AugmentedImageRenderer.java
public void draw(
float[] viewMatrix,
float[] projectionMatrix,
AugmentedImage augmentedImage,
Anchor centerAnchor,
float[] colorCorrectionRgba) {
// Use these code to replace previous code for rendering the Andy object
//
// Adjust the Andy's rendering position
// The Andy's pose is at the maze's vertex's coordinate
Pose andyPoseInImageSpace = Pose.makeTranslation(
andyPose.tx() * mazeScaleFactor,
andyPose.ty() * mazeScaleFactor,
andyPose.tz() * mazeScaleFactor);
anchorPose.compose(andyPoseInImageSpace).toMatrix(modelMatrix, 0);
andyRenderer.updateModelMatrix(modelMatrix, 0.05f);
andyRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
}
Aggiungi una nuova funzione di utilità, updateAndyPose(), per ricevere gli aggiornamenti della posa di Andy.
AugmentedImageRenderer.java
// Receive Andy pose updates
public void updateAndyPose(Pose pose) {
andyPose = pose;
}
In AugmentedImageActivity.java, crea un oggetto PhysicsController che utilizza il motore fisico JBullet per gestire tutte le funzioni correlate alla fisica.
AugmentedImageActivity.java
import com.google.ar.core.Pose;
// Declare the PhysicsController object
private PhysicsController physicsController;
Nel motore fisico, utilizziamo una palla rigida per rappresentare Andy e aggiorniamo la sua posa utilizzando la posa della palla. Chiama PhysicsController per aggiornare la fisica ogni volta che l'app riconosce un'immagine. Per muovere la palla come nel mondo reale, applica la gravità per spostarla nel labirinto.
AugmentedImageActivity.java
// Update the case clause for TRACKING to call PhysicsController
// whenever the app recognizes an image
private void drawAugmentedImages(
...
case TRACKING:
// Switch to UI Thread to update View
this.runOnUiThread(
new Runnable() {
@Override
public void run() {
fitToScanView.setVisibility(View.GONE);
}
});
// Create a new anchor for newly found images
if (!augmentedImageMap.containsKey(augmentedImage.getIndex())) {
Anchor centerPoseAnchor = augmentedImage.createAnchor(augmentedImage.getCenterPose());
augmentedImageMap.put(
augmentedImage.getIndex(), Pair.create(augmentedImage, centerPoseAnchor));
physicsController = new PhysicsController(this);
} else {
Pose ballPose = physicsController.getBallPose();
augmentedImageRenderer.updateAndyPose(ballPose);
// Use real world gravity, (0, -10, 0), as gravity
// Convert to Physics world coordinate(maze mesh has to be static)
// Use the converted coordinate as a force to move the ball
Pose worldGravityPose = Pose.makeTranslation(0, -10f, 0);
Pose mazeGravityPose = augmentedImage.getCenterPose().inverse().compose(worldGravityPose);
float mazeGravity[] = mazeGravityPose.getTranslation();
physicsController.applyGravityToBall(mazeGravity);
physicsController.updatePhysics();
}
break;
Esegui l'app. Ora Andy dovrebbe muoversi in modo realistico quando inclini l'immagine.
L'esempio riportato di seguito utilizza un altro smartphone per visualizzare l'immagine. Puoi utilizzare qualsiasi cosa ti sia comoda, ad esempio un tablet, la copertina di un libro cartaceo o semplicemente un foglio stampato attaccato a un oggetto piatto.

È tutto. Divertiti a cercare di far uscire Andy dal labirinto. Suggerimento: è più facile trovare l'uscita quando tieni l'immagine di destinazione capovolta.
6. Complimenti
Congratulazioni, hai raggiunto la fine di questo codelab e quindi:
- Hai creato ed eseguito un esempio di AugmentedImage Java di ARCore.
- È stato aggiornato l'esempio per mostrare un modello di labirinto sull'immagine, alla scala corretta.
- Ha utilizzato la posa dell'immagine per fare qualcosa di divertente.
Se vuoi fare riferimento al codice completo, puoi scaricarlo qui.