1. Présentation
ARCore est une plate-forme qui permet de créer des applications de réalité augmentée sur Android. Augmented Images vous offre la possibilité de concevoir des applications de RA qui peuvent reconnaître des images 2D préenregistrées dans le monde réel et d'y associer des contenus virtuels.
Cet atelier de programmation vous aidera à modifier un exemple d'application ARCore existant afin d'y intégrer des éléments Augmented Images en mouvement ou fixes.
Ce que vous allez faire
Dans cet atelier de programmation, vous allez vous appuyer sur un exemple d'application ARCore existant. Voici les tâches que votre application pourra effectuer quand vous aurez terminé :
- Détecter une image cible et y associer un labyrinthe virtuel
- Suivre une cible en mouvement tant qu'elle reste visible dans le champ de l'appareil photo
Est-ce la première fois que vous créez une application ARCore ?
Avez-vous l'intention d'écrire un exemple de code dans cet atelier de programmation ou souhaitez-vous simplement lire ces pages ?
Ce que vous allez apprendre
- Utiliser Augmented Images dans ARCore en Java
- Évaluer la capacité d'une image à être reconnue par ARCore
- Associer un contenu virtuel à une image et suivre ses déplacements
Prérequis
Vous aurez besoin de matériel et de logiciels spécifiques pour cet atelier de programmation.
Matériel requis
- Un appareil compatible ARCore, connecté à l'ordinateur de développement via un câble USB
Logiciels requis
- APK ARCore 1.9.0 ou version ultérieure (cet APK est normalement installé automatiquement sur l'appareil via le Play Store)
- Un ordinateur de développement sur lequel est installé Android Studio (version 3.1 ou ultérieure)
- Un accès à Internet (car vous devrez télécharger des bibliothèques pendant le développement)
Maintenant que tout est prêt, nous pouvons nous lancer.
2. Configurer l'environnement de développement
Télécharger le SDK
Nous allons commencer par télécharger la dernière version du SDK Android ARCore depuis GitHub. Décompressez-le à l'emplacement de votre choix. La plus ancienne version du SDK qui est compatible avec cet atelier de programmation est la version 1.18.1. Le dossier s'intitule arcore-android-sdk-x.xx.x
("x.xx.x" correspondra à la version du SDK que vous utilisez).
Lancez Android Studio, puis cliquez sur Open an existing Android Studio project (Ouvrir un projet Android Studio existant).
Accédez à ce dossier décompressé :
arcore-android-sdk-x.xx.x/samples/augmented_image_java
Cliquez sur Open (Ouvrir).
Attendez qu'Android Studio synchronise le projet. Si des composants requis manquent à Android Studio, le message d'erreur Install missing platform and sync project
peut s'afficher. Suivez les instructions pour résoudre le problème.
Exécuter l'application exemple
Maintenant que vous disposez d'un projet d'application ARCore opérationnel, nous allons pouvoir essayer de l'exécuter.
Connectez votre appareil ARCore à l'ordinateur de développement, puis utilisez le menu Run > Run 'app' (Exécuter > Exécuter "application") pour exécuter la version de débogage sur l'appareil. Dans la boîte de dialogue qui vous invite à choisir l'appareil sur lequel exécuter l'application, sélectionnez l'appareil connecté, puis cliquez sur OK.
Cet exemple de projet utilise targetSdkVersion 28
. Si vous rencontrez une erreur de build telle que Failed to find Build Tools revision 28.0.3
, suivez les instructions fournies dans Android Studio pour télécharger et installer la version requise d'Android Build Tools.
Si l'opération réussit, l'application exemple s'ouvre sur l'appareil et vous invite à autoriser Augmented Images à prendre des photos et à enregistrer des vidéos. Appuyez sur ALLOW (AUTORISER) pour accorder cette autorisation.
Tester l'application avec un exemple d'image
Maintenant que vous avez configuré votre environnement de développement, vous pouvez ajouter une image afin que l'application l'examine.
Dans Android Studio, dans la fenêtre Project (Projet), accédez à app > assets (Application > Éléments), puis double-cliquez sur le fichier default.jpg
pour l'ouvrir.
Orientez l'appareil photo de votre appareil vers l'image de la Terre affichée à l'écran, puis suivez les instructions pour adapter l'image que vous scannez au cadre.
Une image de cadre se superpose sur la photo, comme ci-dessous :
Apportons quelques petites améliorations à notre application.
3. Afficher un modèle de labyrinthe sur l'image 2D
Vous pouvez commencer à tester votre image Augmented Images en y superposant un modèle 3D.
Télécharger un modèle 3D
Pour cet atelier de programmation, nous allons utiliser Circle Maze - Green par Evol, sous licence CC-BY 3.0. Une copie de ce modèle 3D est disponible dans le dépôt GitHub de cet atelier de programmation. Pour y accéder, cliquez ici.
Procédez comme suit pour télécharger le modèle et l'importer dans Android Studio.
- Accédez au répertoire "third_party" du dépôt GitHub de cet atelier de programmation.
- Cliquez sur GreenMaze_obj.zip, puis sur le bouton Download (Télécharger).
Un fichier nommé GreenMaze_obj.zip
est alors téléchargé.
- Dans Android Studio, créez le répertoire
green-maze
sous app > assets > models (Application > Éléments > Modèles). - Décompressez
GreenMaze_obj.zip
, puis copiez son contenu à l'emplacement suivant :arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze
- Dans Android Studio, accédez à app > assets > models > green-maze (Application > Éléments > Modèles > green-maze).
Ce dossier doit contenir deux fichiers : GreenMaze.obj
et GreenMaze.mtl
.
Afficher le modèle de labyrinthe
Procédez comme suit pour superposer le modèle 3D GreenMaze.obj
à l'image 2D existante.
Dans AugmentedImageRenderer.java
, ajoutez une variable de membre appelée mazeRenderer
pour afficher le modèle de labyrinthe. Comme le labyrinthe doit se superposer à l'image, il est logique de placer mazeRenderer
dans la classe AugmentedImageRenderer
.
AugmentedImageRenderer.java
// Add a member variable to hold the maze model.
private final ObjectRenderer mazeRenderer = new ObjectRenderer();
Dans la fonction createOnGlThread()
, chargez GreenMaze.obj
. Pour plus de simplicité, utilisez la même texture pour l'élément et son cadre.
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);
}
Remplacez la définition de la fonction draw()
par ce qui suit. Cela permet d'ajuster la taille du labyrinthe à celle de l'image détectée et de l'afficher à l'écran.
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);
}
Le labyrinthe doit maintenant s'afficher au-dessus de l'image default.jpg
de la Terre.
Remarque : Comme vous ne contrôlez pas entièrement cet exemple de modèle 3D, le code ci-dessus contient des nombres qui peuvent donner l'impression de sortir de nulle part. Les dimensions du modèle de labyrinthe sont de 492,65 × 120 × 492,65, et les coordonnées de son centre sont : (251,3 ; 60 ; -129). Les coordonnées x, y et z minimales et maximales de ses sommets sont : [5,02 ; 497,67], [0 ; 120], [-375,17 ; 117,25]. Ainsi, l'échelle du modèle de labyrinthe doit être image_size / 492.65
. Un décalage mazeModelLocalOffset
est ajouté, car le modèle 3D du labyrinthe n'est pas centré sur l'origine (0, 0, 0).
Le mur du labyrinthe est encore un peu trop élevé pour être superposé à l'image. Créez une fonction d'assistance updateModelMatrix()
capable de mettre à l'échelle les coordonnées X, Y et Z de façon différenciée, afin de redimensionner la hauteur du labyrinthe à l'échelle 0,1. Notez que vous devez conserver la fonction updateModelMatrix(float[] modelMatrix, float scaleFactor)
existante et ajouter la surcharge de fonction updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ)
en tant que nouvelle fonction.
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);
}
Exécutez le code. Le labyrinthe doit maintenant se superposer parfaitement à l'image.
4. Ajouter le personnage Andy au labyrinthe
Ajoutons maintenant un personnage qui se déplace à l'intérieur du labyrinthe. Utilisez le fichier andy.obj
inclus dans le SDK Android ARCore. Utilisez la même texture pour le cadre et l'image, car elle est différente du labyrinthe vert superposé à l'image.
Dans AugmentedImageRenderer.java
, ajoutez un ObjectRenderer
privé pour afficher Andy.
AugmentedImageRenderer.java
// Render for Andy
private final ObjectRenderer andyRenderer = new ObjectRenderer();
Ensuite, initialisez andyRenderer
à la fin de 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);
}
Pour terminer, affichez Andy sur le labyrinthe à la fin de la fonction 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);
}
Exécutez votre code. Vous devriez voir Andy apparaître sur le labyrinthe.
Déterminer la qualité de l'image cible
ARCore se base sur certaines caractéristiques visuelles pour reconnaître des images. Comme toutes les images n'ont pas la même qualité, certaines sont moins faciles à identifier.
L'outil de ligne de commande arcoreimg
vous permet de déterminer si une image sera facilement reconnaissable par ARCore. Cet outil génère un nombre compris entre 0 et 100, où 100 correspond à la plus grande facilité de reconnaissance.
Voici un exemple.
arcore-android-sdk-x.xx.x/tools/arcoreimg/macos$
$ ./arcoreimg eval-img --input_image_path=/Users/username/maze.jpg
100
L'image maze.jpg
présente 100 comme valeur. ARCore peut donc facilement la reconnaître.
5. Facultatif : Déplacer Andy dans le labyrinthe
Enfin, vous pouvez ajouter du code pour qu'Andy se déplace dans le labyrinthe. Par exemple, utilisez le moteur physique Open Source jBullet pour gérer la simulation physique. N'hésitez pas à sauter cette étape si elle ne vous intéresse pas.
Téléchargez PhysicsController.java
, puis ajoutez-le à votre projet dans le répertoire.
arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/java/com/google/ar/core/examples/java/augmentedimage/
Dans Android Studio, ajoutez GreenMaze.obj au répertoire des éléments du projet, afin qu'il puisse être chargé au moment de l'exécution. Copiez GreenMaze.obj
depuis app > assets > models > green-maze (Application > Éléments > Modèles > green-maze), puis collez-le dans app > assets (Application > Éléments).
Ajoutez les dépendances suivantes au fichier build.gradle
de l'application.
app/build.gradle
// jbullet library
implementation 'cz.advel.jbullet:jbullet:20101010-1'
// Obj - a simple Wavefront OBJ file loader
// https://github.com/javagl/Obj
implementation 'de.javagl:obj:0.2.1'
Définissez une variable andyPose
pour enregistrer la position actuelle d'Andy.
AugmentedImageRenderer.java
// Create a new pose for the Andy
private Pose andyPose = Pose.IDENTITY;
Modifiez AugmentedImageRenderer.java
pour afficher Andy en fonction de la nouvelle variable 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);
}
Ajoutez une fonction utilitaire updateAndyPose()
pour recevoir des informations sur la position d'Andy.
AugmentedImageRenderer.java
// Receive Andy pose updates
public void updateAndyPose(Pose pose) {
andyPose = pose;
}
Dans AugmentedImageActivity.java
, créez un objet PhysicsController
qui utilise le moteur physique JBullet pour gérer toutes les fonctions physiques.
AugmentedImageActivity.java
import com.google.ar.core.Pose;
// Declare the PhysicsController object
private PhysicsController physicsController;
Dans le moteur physique, nous représentons Andy avec une bille rigide et nous mettons à jour sa position avec celle de la bille. Appelez PhysicsController
pour mettre à jour les mouvements chaque fois que l'application reconnaît une image. Pour déplacer la bille dans le labyrinthe comme dans la vraie vie, appliquez la gravité réelle.
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;
Exécutez l'application. Andy devrait désormais se déplacer de manière réaliste lorsque vous inclinez l'image.
Dans l'exemple ci-dessous, un autre téléphone est utilisé pour afficher l'image. Vous pouvez vous servir du support qui vous convient, par exemple d'une tablette, de la couverture d'un livre ou simplement d'une feuille fixée sur un objet plat.
Et voilà ! Vous pouvez désormais vous amuser à déplacer Andy dans le labyrinthe. Conseil : Pour trouver la sortie plus facilement, il vous suffit de positionner l'image cible à l'envers.
6. Félicitations
Félicitations, vous avez terminé cet atelier de programmation ! Voici ce que vous y avez appris :
- Créer et exécuter un exemple AugmentedImage Java
- Mettre à jour le code pour afficher un modèle de labyrinthe sur l'image, à l'échelle appropriée
- Créer un jeu basé sur le positionnement de l'image
Si vous souhaitez accéder au code complet, vous pouvez le télécharger sur cette page.