1. Обзор
ARCore — это платформа для создания приложений дополненной реальности на Android. Дополненные изображения дают вам возможность создавать приложения AR, которые могут распознавать предварительно зарегистрированные 2D-изображения в реальном мире и привязывать к ним виртуальный контент.
В этой лаборатории кода вы узнаете, как изменить существующий пример приложения ARCore для включения дополненных изображений, которые перемещаются или фиксируются на месте.
Что ты построишь
В этой лаборатории кода вы будете использовать уже существующий пример приложения ARCore. К концу работы над кодом ваше приложение сможет:
- Обнаружьте цель изображения и прикрепите к ней виртуальный лабиринт.
- Отслеживайте движущуюся цель, пока она находится в поле зрения камеры.
Вы впервые создаете приложение ARCore?
Планируете ли вы написать пример кода в этой лаборатории или просто хотите прочитать эти страницы?
Что вы узнаете
- Как использовать дополненные изображения в ARCore на Java
- Как оценить способность изображения распознаваться ARCore
- Как прикрепить виртуальный контент к изображению и отслеживать его перемещение
Предварительные условия
Для выполнения этой лаборатории кода вам понадобится специальное оборудование и программное обеспечение.
Требования к оборудованию
- Устройство с поддержкой ARCore , подключенное через USB-кабель к вашей машине разработки.
Требования к программному обеспечению
- ARCore APK 1.9.0 или новее. Этот APK обычно автоматически устанавливается на устройство через Play Store.
- Машина разработки с Android Studio (v3.1 или новее)
- Доступ к Интернету, так как во время разработки вам потребуется загружать библиотеки.
Теперь, когда у вас все готово, приступим!
2. Настройте среду разработки
Загрузите SDK
Начнем с загрузки последней версии ARCore Android SDK с GitHub. Разархивируйте его в удобное для вас место. Для этой лаборатории самой ранней версией SDK является 1.18.1. Папка будет называться arcore-android-sdk-x.xx.x
, точным значением будет версия SDK, которую вы используете.
Запустите Android Studio и нажмите « Открыть существующий проект Android Studio» .
Перейдите в эту разархивированную папку:
arcore-android-sdk-x.xx.x/samples/augmented_image_java
Нажмите Открыть .
Подождите, пока студия Android завершит синхронизацию проекта. Если в вашей Android Studio нет необходимых компонентов, может произойти сбой с сообщением Install missing platform and sync project
. Следуйте инструкциям, чтобы устранить проблему.
Запустите пример приложения
Теперь, когда у вас есть рабочий проект приложения ARCore, давайте его протестируем.
Подключите устройство ARCore к машине разработки и используйте меню «Выполнить» > «Выполнить приложение», чтобы запустить отладочную версию на устройстве. В диалоговом окне, предлагающем выбрать, с какого устройства запускаться, выберите подключенное устройство и нажмите «ОК» .
В этом примере проекта используется targetSdkVersion 28
. Если у вас возникла ошибка сборки, например « Failed to find Build Tools revision 28.0.3
, следуйте инструкциям, описанным в Android Studio, чтобы загрузить и установить необходимую версию инструментов сборки Android.
Если все прошло успешно, на устройстве запустится пример приложения и запросит разрешение на использование дополненного изображения для съемки фотографий и видео. Нажмите РАЗРЕШИТЬ , чтобы предоставить разрешение.
Тестирование с образцом изображения
Теперь, когда вы настроили среду разработки, вы можете протестировать приложение, предоставив ему изображение для просмотра.
Вернувшись в Android Studio, в окне проекта перейдите к app > assets и дважды щелкните файл default.jpg
, чтобы открыть его.
Наведите камеру вашего устройства на изображение Земли на экране и следуйте инструкциям, чтобы поместить сканируемое изображение в перекрестие.
Рамка изображения будет наложена поверх изображения, как показано ниже:
Далее мы внесем небольшие улучшения в пример приложения.
3. Отображение модели лабиринта на 2D-изображении.
Вы можете начать играть с дополненными изображениями, отобразив поверх них 3D-модель.
Скачать 3D-модель
Для этой кодовой лаборатории мы будем использовать « Circle Maze — Green » от Evol под лицензией CC-BY 3.0 . Копию этой 3D-модели я сохранил в репозитории github этой кодлаба, который вы можете найти здесь .
Выполните следующие действия, чтобы загрузить модель и включить ее в Android Studio.
- Перейдите в репозиторий GitHub этой лаборатории кода, каталог Third_party .
- Нажмите GreenMaze_obj.zip и нажмите кнопку «Загрузить» .
При этом загружается файл GreenMaze_obj.zip
.
- В студии Android создайте каталог
green-maze
в разделе «Приложение» > «Активы» > «Модели». - Разархивируйте
GreenMaze_obj.zip
и скопируйте содержимое в это место:arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze
- В Android Studio перейдите в приложение > ресурсы > модели > зеленый лабиринт .
В этой папке должно быть два файла: GreenMaze.obj
и GreenMaze.mtl
.
Рендеринг модели лабиринта
Выполните следующие действия, чтобы отобразить 3D-модель GreenMaze.obj
поверх существующего 2D-изображения.
В AugmentedImageRenderer.java
добавьте переменную-член mazeRenderer
для визуализации модели лабиринта. Поскольку лабиринт должен быть прикреплен к изображению, имеет смысл поместить mazeRenderer
внутри класса AugmentedImageRenderer
.
AugmentedImageRenderer.java
// Add a member variable to hold the maze model.
private final ObjectRenderer mazeRenderer = new ObjectRenderer();
В функции createOnGlThread()
загрузите GreenMaze.obj
. Для простоты используйте ту же текстуру кадра, что и его текстура.
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);
}
Замените определение функции draw()
следующим. Это подстраивает размер лабиринта под размер обнаруженного изображения и отображает его на экране.
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);
}
Теперь лабиринт должен отображаться поверх изображения Земли default.jpg
.
Примечание. Поскольку у вас нет полного контроля над этим примером 3D-модели, в приведенном выше коде используется несколько «магических» чисел. Размер модели лабиринта — 492,65 x 120 x 492,65 с центром (251,3, 60, -129,0). Диапазон значений координат X, Y и Z его вершин составляет [5,02, 497,67], [0, 120] и [-375,17, 117,25] соответственно. Таким образом, масштаб модели лабиринта должен быть image_size / 492.65
. mazeModelLocalOffset
введен потому, что трехмерная модель лабиринта не центрирована вокруг начала координат (0, 0, 0).
Стена лабиринта все еще слишком высока, чтобы поместиться наверху картины. Создайте вспомогательную функцию updateModelMatrix()
, которая может масштабировать X, Y, Z неравномерно, чтобы масштабировать высоту лабиринта на 0,1. Обратите внимание: вы должны сохранить существующую updateModelMatrix(float[] modelMatrix, float scaleFactor)
и добавить перегрузку функции updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ)
в качестве новой функции.
общий/рендеринг/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);
}
Запустите код. Теперь лабиринт должен идеально располагаться над изображением.
4. Добавьте Энди в лабиринт.
Теперь, когда у вас есть лабиринт, добавьте персонажа, который будет перемещаться внутри него. Используйте файл andy.obj
, включенный в ARCore Android SDK. Сохраните текстуру кадра изображения в качестве его текстуры, поскольку она отличается от зеленого лабиринта, отображаемого поверх изображения.
В AugmentedImageRenderer.java
добавьте частный ObjectRenderer
для рендеринга Энди.
AugmentedImageRenderer.java
// Render for Andy
private final ObjectRenderer andyRenderer = new ObjectRenderer();
Затем инициализируйте andyRenderer
в конце 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);
}
Наконец, в конце функции 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);
}
Запустите свой код. Вы должны увидеть Энди, стоящего на вершине лабиринта.
Определите целевое качество изображения
ARCore полагается на визуальные функции для распознавания изображений. Из-за различий в качестве не все изображения можно легко распознать.
arcoreimg
— это инструмент командной строки, который позволяет вам определить, насколько распознаваемым изображение будет для ARCore. Он выводит число от 0 до 100, причем 100 легче всего распознать.
. Вот пример:
arcore-android-sdk-x.xx.x/tools/arcoreimg/macos$
$ ./arcoreimg eval-img --input_image_path=/Users/username/maze.jpg
100
maze.jpg
имеет значение 100, поэтому ARCore легко распознает его.
5. Необязательно: заставьте Энди двигаться по лабиринту.
Наконец, вы можете добавить код, который заставит Энди двигаться по лабиринту. Например, используйте физический движок с открытым исходным кодом jBullet для моделирования физики. Ничего страшного, если вы пропустите эту часть.
Загрузите PhysicsController.java
и добавьте его в свой проект в каталоге.
arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/java/com/google/ar/core/examples/java/augmentedimage/
В Android Studio добавьте GreenMaze.obj в каталог ресурсов проекта , чтобы его можно было загружать во время выполнения. Скопируйте GreenMaze.obj
из приложения > ресурсы > модели > зеленый лабиринт в приложение > активы .
Добавьте следующие зависимости в файл build.gradle
приложения.
приложение/build.gradle
// jbullet library
implementation 'cz.advel.jbullet:jbullet:20101010-1'
Определите переменную andyPose
для хранения положения текущей позы Энди.
AugmentedImageRenderer.java
// Create a new pose for the Andy
private Pose andyPose = Pose.IDENTITY;
Измените AugmentedImageRenderer.java
, чтобы визуализировать Энди, используя новую переменную 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);
}
Добавьте новую служебную функцию updateAndyPose()
для получения обновлений позы Энди.
AugmentedImageRenderer.java
// Receive Andy pose updates
public void updateAndyPose(Pose pose) {
andyPose = pose;
}
В AugmentedImageActivity.java
создайте объект PhysicsController
, который использует физический движок JBullet для управления всеми функциями, связанными с физикой.
AugmentedImageActivity.java
import com.google.ar.core.Pose;
// Declare the PhysicsController object
private PhysicsController physicsController;
В физическом движке мы фактически используем твердый шар для представления Энди и обновляем позу Энди, используя позу мяча. Вызовите PhysicsController
, чтобы обновить физику всякий раз, когда приложение распознает изображение. Чтобы переместить мяч, как в реальном мире, примените реальную гравитацию, чтобы переместить мяч в лабиринте.
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;
Запустите приложение. Энди теперь должен реалистично двигаться, когда вы наклоняете изображение.
В примере ниже для отображения изображения используется другой телефон, смело используйте все, что вам удобно, например, планшет, или обложку печатной книги, или просто распечатанный лист бумаги, прикрепленный к плоскому предмету.
Вот и все! Развлекайтесь, пытаясь провести Энди через лабиринт. Подсказка: выход легче найти, если держать целевое изображение перевернутым.
6. Поздравления
Поздравляем, вы достигли конца этой кодовой лаборатории и, таким образом:
- Создал и запустил образец Java ARCore AugmentedImage .
- Обновлен образец для отображения модели лабиринта на изображении в правильном масштабе.
- Использовал позу изображения, чтобы сделать что-нибудь веселое.
Если вы хотите ознакомиться с полным кодом, вы можете скачать его здесь .