1. Panoramica
ARCore è una piattaforma per creare app di realtà aumentata su Android. Le immagini aumentate ti consentono di creare app AR in grado di riconoscere immagini 2D preregistrate nel mondo reale e di aggiungere contenuti virtuali su 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, creerai un'app di esempio ARCore preesistente. Al termine del codelab, la tua app sarà in grado di:
- Rileva un obiettivo immagine e collega un labirinto virtuale al bersaglio
- Monitora il bersaglio in movimento purché si trovi nel campo visivo della videocamera.
È la prima volta che crei un'app ARCore?
Hai intenzione di scrivere codice campione in questo codelab o vuoi semplicemente 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 al computer di sviluppo
Requisiti software
- APK ARCore 1.9.0 o versioni successive. Normalmente questo APK viene installato automaticamente sul dispositivo tramite il Play Store
- Una macchina di sviluppo con Android Studio (v3.1 o versioni successive)
- Accesso a Internet, in quanto dovrai scaricare le librerie durante lo sviluppo
Ora che hai tutto pronto, possiamo iniziare.
2. Configurare l'ambiente di sviluppo
Scarica l'SDK
Per iniziare, scarica l'SDK ARCore Android più recente da GitHub. Decomprimilo nella posizione che preferisci. Per questo codelab, la prima versione dell'SDK è la 1.18.1. La cartella verrà denominata arcore-android-sdk-x.xx.x
e 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 che Android Studio completi la sincronizzazione del progetto. Se Android Studio non dispone dei componenti necessari, l'operazione potrebbe non riuscire e viene visualizzato 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 dell'app ARCore funzionante, eseguiamolo un test.
Collega il dispositivo ARCore al computer di sviluppo e usa il menu Esegui > Esegui "app" per eseguire la versione di debug sul dispositivo. Nella finestra di dialogo in cui ti viene chiesto di scegliere il dispositivo da cui eseguire l'esecuzione, scegli il dispositivo connesso e fai clic su OK.
Questo progetto di esempio utilizza targetSdkVersion 28
. Se si verifica un errore di generazione 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 a posto, l'app di esempio verrà avviata sul dispositivo e ti chiederà l'autorizzazione per consentire all'immagine aumentata di scattare foto e registrare video. Tocca CONSENTI per concedere l'autorizzazione.
Esegui il test con un'immagine di esempio
Ora che hai configurato l'ambiente di sviluppo, puoi testare l'app assegnandole un'immagine da esaminare.
Torna in Android Studio, nella finestra Progetto vai ad app > asset e fai doppio clic sul file default.jpg
per aprirlo.
Inquadra con la fotocamera del dispositivo l'immagine della Terra sullo schermo e segui le istruzioni per inserire l'immagine nel mirino.
Sull'immagine verrà sovrapposta una cornice, in questo modo:
Successivamente, apporteremo piccoli miglioramenti all'app di esempio.
3. Visualizzazione di un modello di labirinto sull'immagine 2D
Puoi iniziare a giocare con le immagini aumentate visualizzando un modello 3D sopra.
Scarica un modello 3D
Per questo codelab utilizzeremo "Labirinto circolare - Verde". da Evol e concesso in licenza ai sensi delle CC-BY 3.0. Ho archiviato 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 al repository GitHub di questo codelab, directory third_party.
- Fai clic su GreenMaze_obj.zip e sul pulsante Scarica.
Verrà scaricato un file denominato GreenMaze_obj.zip
.
- In Android Studio, crea la directory
green-maze
in app > asset > di machine learning - Decomprimi
GreenMaze_obj.zip
e copia il contenuto in questa posizione:arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze
- In Android Studio, vai su app > asset > modelli > labirinto verde.
Questa cartella dovrebbe contenere due file: GreenMaze.obj
e GreenMaze.mtl
.
Visualizza il modello del labirinto
Segui questi passaggi per visualizzare il modello 3D di GreenMaze.obj
sopra l'immagine 2D esistente.
In AugmentedImageRenderer.java
, aggiungi una variabile membro denominata mazeRenderer
per eseguire il rendering del modello del labirinto. Poiché il labirinto deve essere attaccato all'immagine, ha senso inserire mazeRenderer
nella 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 texture 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 all'immagine rilevata e la viene visualizzata 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 su questo modello 3D di esempio, il codice riportato sopra utilizza alcune espressioni "magiche" numeri. Le dimensioni del modello del labirinto sono 492,65 x 120 x 492,65, con il centro a (251,3, 60, -129,0). L'intervallo dei vertici I valori delle coordinate X, Y e Z sono rispettivamente [5.02, 497.67], [0, 120] e [-375.17, 117.25]. Di conseguenza, la scala del modello di labirinto deve essere image_size / 492.65
. mazeModelLocalOffset
viene introdotto perché il modello 3D del labirinto non è centrato sull'origine (0, 0, 0).
La parete del labirinto è ancora un po' troppo alta per essere posizionata sopra l'immagine. Crea una funzione helper updateModelMatrix()
in grado di scalare X, Y, Z in modo non uniforme per scalare l'altezza del labirinto di 0,1. Tieni presente che devi mantenere l'elemento updateModelMatrix(float[] modelMatrix, float scaleFactor)
esistente e aggiungere la funzione sovraccarico 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. Il labirinto dovrebbe ora adattarsi perfettamente all'immagine.
4. Aggiungi Andy al labirinto
Ora che hai creato un labirinto, aggiungi un personaggio per spostarti al suo interno. Utilizza il file andy.obj
incluso nell'SDK ARCore Android. Mantieni la texture della cornice immagine come texture, perché ha un aspetto diverso dal labirinto verde visualizzato sopra l'immagine.
In AugmentedImageRenderer.java
, aggiungi un elemento ObjectRenderer
privato per eseguire il rendering di Andrea.
AugmentedImageRenderer.java
// Render for Andy
private final ObjectRenderer andyRenderer = new ObjectRenderer();
Quindi, 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, mostra Andrea 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à delle immagini target
ARCore si basa su funzionalità visive per riconoscere le immagini. A causa delle differenze di qualità, non tutte le immagini sono facilmente riconoscibili.
arcoreimg
è uno strumento a riga di comando che consente di determinare quanto sarà riconoscibile un'immagine per ARCore. Restituisce un numero compreso tra 0 e 100, dove 100 è il numero più facile da riconoscere.
di Google. 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 pari a 100, pertanto è facilmente riconoscibile da ARCore.
5. (Facoltativo) Fai muovere Andy nel labirinto
Infine, puoi aggiungere del codice per far spostare l'Andy nel labirinto. Ad esempio, utilizza il motore di fisica open source jBullet per gestire la simulazione della fisica. Se salti questa parte va benissimo.
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 degli asset del progetto in modo che possa essere caricato durante l'esecuzione. Copia GreenMaze.obj
dall'app > asset > models > green-maze ad app > asset.
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 aggiornamenti sulla posa di Andy.
AugmentedImageRenderer.java
// Receive Andy pose updates
public void updateAndyPose(Pose pose) {
andyPose = pose;
}
In AugmentedImageActivity.java
, crea un oggetto PhysicsController
che utilizzi 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 di fisica, in realtà utilizziamo una palla rigida per rappresentare Andy e aggiorniamo la posa di Andy utilizzando la posa della palla. Chiama PhysicsController
per aggiornare le leggi fisiche ogni volta che l'app riconosce un'immagine. Per spostare la palla come se fosse nel mondo reale, applica la gravità reale 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. Andy ora dovrebbe spostarsi realisticamente quando inclini l'immagine.
Nell'esempio riportato di seguito viene utilizzato un altro smartphone per visualizzare l'immagine. Puoi utilizzare tutto ciò che ti è più comodo, come un tablet, la copertina di un fotolibro o solo una carta stampata attaccata a un oggetto piatto.
È tutto. Divertiti a cercare di portare Andrea nel labirinto. Suggerimento: è più facile trovare l'uscita se tieni l'immagine target capovolta.
6. Complimenti
Complimenti, hai raggiunto la fine di questo codelab e hai quindi:
- Ha creato e eseguito un campione AugmentedImage Java ARCore.
- È stato aggiornato l'esempio per visualizzare il modello del labirinto sull'immagine, con la scala corretta.
- Ha utilizzato la posa dell'immagine per fare qualcosa di divertente.
Per consultare il codice completo, scaricalo qui.