Дополненная реальность (AR) является горячей темой в мобильных приложениях сегодня. Смартфоны и планшеты обладают мощью и аппаратным обеспечением, позволяющим разработчикам создавать интересные новые приложения, которые включают в себя живую камеру и видеопотоки, сверхточные данные датчиков и другие пользовательские данные в реальном времени интересными способами. Сегодня мы начнем исследовать мир дополненной реальности и то, что платформа Android может предложить разработчикам, которые хотят создавать приложения AR и предоставлять своим пользователям глубокие и богатые возможности.
Также доступно в этой серии:
- Android SDK дополненной реальности: настройка камеры и сенсора
- Android SDK дополненной реальности: местоположение и расстояние
Шаг 0: Предварительные условия и начало работы
Этот урок движется в довольно быстром темпе. Таким образом, мы ожидаем, что читатели будут знакомы со всеми основами, касающимися создания и запуска проектов Android. Мы также ожидаем, что у читателей будет устройство Android, достаточно мощное для запуска приложений AR (таких как Nexus S). Большую часть тестирования этого приложения необходимо будет выполнить на реальном устройстве Android, поскольку приложение в значительной степени зависит от камеры, датчиков и данных о местоположении, которые недоступны в эмуляторе.
Мы предоставили образец приложения вместе с этим руководством для загрузки. Этот проект будет улучшен в ходе этой серии руководств по AR, так что скачайте Android-проект и следуйте инструкциям!
Часть 1: Какое приложение AR мы разрабатываем?
Во-первых, нам нужно определить (в общих чертах), что будет делать это приложение. Мы хотим придерживаться типичных функций приложения AR и улучшать его с течением времени.
Так какое приложение мы будем создавать? Мы собираемся создать приложение AR на основе определения местоположения. Эти типы AR-приложений обычно показывают, где что-то происходит в реальном мире, указывая, где приложение думает, что находится над камерой, когда пользователь держит телефон и перемещает его.
AR-приложение другого типа — это тип, который распознает объект с помощью алгоритмов компьютерного зрения и может либо рисовать информацию через камеру, чтобы показать, каков объект, как он ориентирован, либо, возможно, добавить или показать другие объекты в представлении. Хотя мы не будем создавать такого рода AR-приложение, многие из тех методик, которые мы здесь учим, могут применяться к любому типу AR-приложений.
Базовая архитектура приложения на основе определения местоположения включает в себя вид с камеры, местоположение пользователя, ориентацию телефона по сравнению с реальным миром и список мест для элементов, которые мы хотим отобразить на дисплее — данные, с помощью которых мы увеличиваем камера видение мира.
Эта первая часть нашей серии расскажет о камере и датчиках. В следующей части мы поговорим о данных о местоположении и математике, связанной с созданием приложения.
Требования к приложению: какие функции устройства нужны нашему приложению?
Ваше типичное приложение AR требует доступа к следующим функциям устройства:
- Доступ к виду из камеры
- Доступ к местоположению пользователя
- Доступ к другим датчикам устройства, особенно к компасу (акселерометр и гироскоп также могут быть полезны)
Приложению также могут потребоваться другие сервисы и разрешения, такие как доступ к сети, но они не являются центральными для аспектов AR приложения.
Замечание о целевых устройствах: Nexus S
Не все устройства Android (или мобильные устройства в целом) имеют технические характеристики оборудования, чтобы соответствовать требованиям приложений AR. Для целей данного руководства мы нацелены на Nexus S под управлением Android 2.3.4, что хорошо соответствует нашим специфическим требованиям к оборудованию. Поэтому наше приложение будет предназначаться для очень специфической версии Android SDK. Это сделает приложение простым. Однако требования SDK могут быть более гибкими, если манифест Android правильно идентифицирует все необходимые аппаратные функции или вы реализуете альтернативные функции для менее мощных устройств.
Хотя Nexus S более чем способен выполнять разумные задачи AR, многие другие устройства также способны, в том числе некоторые старые устройства. Если вы создаете производственное приложение, использующее технологию AR, вам необходимо тщательно протестировать на целевых устройствах наличие откликов, удобство использования и правильную функциональность.
Часть 2. Настройка проекта
Теперь мы готовы начать разработку! Давайте сначала правильно настроим проект AR. Это приложение настроено так же, как любой другой проект приложения в Eclipse.
Шаг 1. Создайте новый проект Eclipse
Начните с создания нового проекта Android в Eclipse. Вот детали проекта, которые мы использовали для примера проекта (доступны для скачивания):
- Название проекта: АРТУТ
- Уровень API Мин. / Цель: 10
- Launch Activity Class: ArtutActivity
Шаг 2. Настройка файла манифеста
Далее вы захотите настроить файл манифеста Android. Вы хотите указать следующие параметры:
На вкладке «Манифест» добавьте
- android.hardware.camera (Обязательно = верно)
- android.hardware.camera.autofocus (обязательно = false)
- android.hardware.location.gps (обязательно = true)
- android.hardware.sensor.compass (обязательно = верно)
- android.hardware.sensor.gyroscope (Обязательно = верно)
- android.hardware.sensor.accelerometer (обязательно = true)
На вкладке приложения:
- Отладка должна быть установлена в true
На вкладке «Разрешения» добавьте
- android.permission.CAMERA
- android.permission.FINE_LOCATION
Шаг 3: подключите целевое устройство
Наконец, если вы еще этого не сделали, вам нужно настроить устройство Android для тестирования и подключить его к компьютеру для разработки через USB. Если вы этого не сделали раньше, вам может потребоваться загрузить и установить драйверы перед использованием.
Нет смысла создавать виртуальное устройство Android (AVD) для использования с этим проектом, так как эмулятор не обладает достаточными функциями для тестирования приложений на основе живых камер или датчиков с какой-либо степенью эффективности. Мы сделаем все наши разработки и тестирование на реальном оборудовании. Помимо обеспечения лучшей среды тестирования при использовании датчиков, камеры и другого оборудования, мы также обнаруживаем, что тестирование на реальном оборудовании намного быстрее из-за ограничений производительности и возможностей эмулятора.
Часть 2. Работа с камерой
Наш первый реальный шаг в разработке приложения AR включает отображение содержимого камеры на экране. Мы делаем это путем реализации пользовательского SurfaceView, который отображается в макете действия.
Шаг 1. Определение макета экрана приложения
Во-первых, нам нужно определить ресурс макета, который будет использоваться для основного экрана AR. FrameLayout является наиболее подходящим для нашего экрана, так как он позволяет многоуровневое представление в пределах области отображения. Поэтому наш ресурс макета /res/layout/main.xml может быть довольно простым:
1
2
3
4
5
6
|
<?xml version=»1.0″ encoding=»utf-8″?>
<FrameLayout xmlns:android=»http://schemas.android.com/apk/res/android»
android:id=»@+id/ar_view_pane»
android:layout_width=»fill_parent»
android:layout_height=»fill_parent»>
</FrameLayout>
|
Шаг 2. Определение пользовательского SurfaceView для хранения содержимого камеры
Затем вам нужно определить пользовательский элемент управления для просмотра в вашем FrameLayout. Для этого можно использовать класс SurfaceView. Следовательно, вы можете использовать этот класс для инкапсуляции рисунка вашей камеры.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public class ArDisplayView extends SurfaceView
{
public static final String DEBUG_TAG = «ArDisplayView Log»;
Camera mCamera;
SurfaceHolder mHolder;
Activity mActivity;
public ArDisplayView(Context context, Activity activity) {
super(context);
mActivity = activity;
mHolder = getHolder();
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mHolder.addCallback(this);
}
|
Класс ArDisplayView начинается с конструктора. Мы добавили Activity в качестве параметра конфигурации (подробнее об этом на следующем шаге). Конструктор должен инициализировать базовый SurfaceHolder, который управляет рисованием на экране.
(Здесь следует отметить, что хотя метод setType () устарел, а в документации Android SDK говорится, что он больше не нужен, мы обнаружили, что код камеры не будет работать без него, он просто не запускается, даже на самой новой версии). устройства.)
Мы добавили соответствующие переменные-члены для работы с Камерой, а также держим Активность так, чтобы мы могли реагировать на изменения ориентации и любой другой удобный доступ к Контексту или Деятельности, которые могут нам понадобиться.
Шаг 3: Реализация обратных вызовов SurfaceHolder
Далее вам необходимо реализовать обратные вызовы SurfaceHolder внутри класса. Обновите ваш класс ArDisplayView, чтобы он реализовывал интерфейс SurfaceHolder.Callback, например так:
1
2
3
4
|
public class ArDisplayView extends SurfaceView implements SurfaceHolder.Callback
{
//…
}
|
Это потребует переопределения и реализации трех новых методов: surfaceCreated (), surfaceChanged () и surfaceDestroyed ().
При работе с камерой существует ряд шагов, необходимых для запуска и выключения. Эти шаги четко задокументированы в документации по Android SDK для класса Camera. Мы в основном опускаем эти шаги в соответствующие обратные вызовы SurfaceHolder.
Шаг 4: Реализация обратного вызова surfaceCreated ()
Мы начнем с метода обратного вызова surfaceCreated (). Здесь мы запрашиваем Camera, устанавливаем ориентацию отображения камеры на основе текущей ориентации устройства (чтобы предварительный просмотр камеры всегда отображался правой стороной вверх) и вызываем метод setPreviewDisplay (), чтобы привязать предварительный просмотр Camera к нашему SurfaceHolder.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open();
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(CameraInfo.CAMERA_FACING_BACK, info);
int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0;
case Surface.ROTATION_90: degrees = 90;
case Surface.ROTATION_180: degrees = 180;
case Surface.ROTATION_270: degrees = 270;
}
cam.setDisplayOrientation((info.orientation — degrees + 360) % 360);
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
Log.e(DEBUG_TAG, «surfaceCreated exception: «, e);
}
}
|
Шаг 5: Реализация обратного вызова surfaceChanged ()
Большая часть интересного кода для предварительного просмотра камеры происходит в обратном вызове surfaceChanged (). Здесь мы запрашиваем параметры камеры и проверяем поддерживаемые размеры предварительного просмотра. Нам нужно найти размер предварительного просмотра, который может быть адаптирован к поверхности, поэтому мы ищем наиболее близкое совпадение и используем его в качестве размера предварительного просмотра. Затем мы устанавливаем формат предварительного просмотра камеры, фиксируем изменения параметров камеры и, наконец, вызываем метод startPreview (), чтобы начать отображение предварительного просмотра камеры на нашей поверхности.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
Camera.Parameters params = mCamera.getParameters();
List<Size> prevSizes = params.getSupportedPreviewSizes();
for (Size s : prevSizes)
{
if((s.height <= height) && (s.width <= width))
{
params.setPreviewSize(s.width, s.height);
break;
}
}
mCamera.setParameters(params);
mCamera.startPreview();
}
|
Шаг 6: Реализация обратного вызова surfaceDestroyed ()
Мы заканчиваем методом обратного вызова surfaceDestroyed (). Здесь мы выключаем предварительный просмотр камеры и освобождаем ресурсы Камеры.
1
2
3
4
|
public void surfaceDestroyed(SurfaceHolder holder) {
cam.stopPreview();
cam.release();
}
|
Шаг 7: Пользовательский вид камеры и основное действие
Теперь вам нужно добавить свои собственные представления в элемент управления FrameLayout. Для этого обновите метод onCreate () класса ArtutActivity, например:
1
2
3
4
5
6
7
8
9
|
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FrameLayout arViewPane = (FrameLayout) findViewById(R.id.ar_view_pane);
ArDisplayView arDisplay = new ArDisplayView(this);
arViewPane.addView(arDisplay);
}
|
Это добавляет класс предварительного просмотра камеры, называемый ArDisplayView к FrameLayout. Если вы запустите приложение сейчас, действие запустится, и вы должны увидеть предварительный просмотр камеры на экране.
Примечание к цифрам: Снимок экрана DDMS по умолчанию не захватывает предварительный просмотр камеры. Запустите приложение самостоятельно, чтобы увидеть истинную функциональность.
Часть 3: Работа с датчиками устройства
Наш следующий шаг в разработке приложения AR включает доступ к данным датчика устройства. Нам также нужен способ наложения контента поверх предварительного просмотра камеры. Мы делаем это путем реализации пользовательского представления, которое может быть наслоено поверх ArDisplayView, отображаемого в данный момент в элементе управления FrameLayout действия.
Шаг 1: Отображение содержимого наложения
Во-первых, вам нужно определить пользовательский элемент управления view для рисования оверлейного содержимого. Создайте новый класс с именем OverlayView, производный от класса View. Этот класс начинается просто для этой части урока. Мы просто хотим наложить некоторые данные с сенсора поверх изображения с камеры (пока нет ничего сложного). Для этого мы создаем переменные-члены для хранения последних известных данных датчика для каждого интересующего нас датчика. Затем мы реализуем метод onDraw () нашего представления и рисуем некоторый текст на холсте.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class OverlayView extends View {
public static final String DEBUG_TAG = «OverlayView Log»;
String accelData = «Accelerometer Data»;
String compassData = «Compass Data»;
String gyroData = «Gyro Data»;
public OverlayView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint contentPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
contentPaint.setTextAlign(Align.CENTER);
contentPaint.setTextSize(20);
contentPaint.setColor(Color.RED);
canvas.drawText(accelData, canvas.getWidth()/2, canvas.getHeight()/4, contentPaint);
canvas.drawText(compassData, canvas.getWidth()/2, canvas.getHeight()/2, contentPaint);
canvas.drawText(gyroData, canvas.getWidth()/2, (canvas.getHeight()*3)/4, contentPaint);
}
}
|
Метод onDraw () выполняет всю работу здесь. Мы настраиваем Paint с соответствующими текстовыми атрибутами, а затем рисуем некоторый текст на холсте, используя метод drawText ().
Зачем использовать пользовательский вид вместо обычных элементов управления? Позже нам понадобится точность для размещения объектов AR внутри вида камеры, которые представляют местоположения в реальных местах. Альтернативой этому методу является использование 3D-рендеринга для лучшего управления глубиной и расстоянием. В приложении AR для определения местоположения это, как правило, менее важно, поскольку расстояния часто довольно велики (километры, а не метры).
Шаг 2: Реализация обратных вызовов SurfaceHolder
Затем вам нужно реализовать интерфейс SensorEventListener внутри класса. Обновите ваш класс OverlayView, чтобы он реализовывал интерфейс SensorEventListener, например так:
1
|
public class OverlayView extends View implements SensorEventListener {
|
Это потребует переопределения и реализации двух новых методов: onAccuracyChanged () и onSensorChanged (). Для целей этого урока нас интересует только метод onSensorChanged ().
Шаг 3: Регистрация для обновления данных датчика
Прежде чем мы реализуем обратные вызовы SensorEventListener, мы хотим зарегистрироваться для соответствующих обновлений данных датчика. Для этого обновите метод onCreate () класса OverlayView.
Начните с запроса SensorManager и получения датчиков, которые вы хотите просмотреть. В этом случае мы будем наблюдать за акселерометром, компасом и гироскопом. Затем зарегистрируйтесь, чтобы прослушивать события на каждом из этих датчиков.
1
2
3
4
5
6
7
8
|
SensorManager sensors = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
Sensor accelSensor = sensors.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Sensor compassSensor = sensors.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
Sensor gyroSensor = sensors.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
boolean isAccelAvailable = sensors.registerListener(this, accelSensor, SensorManager.SENSOR_DELAY_NORMAL);
boolean isCompassAvailable = sensors.registerListener(this, compassSensor, SensorManager.SENSOR_DELAY_NORMAL);
boolean isGyroAvailable = sensors.registerListener(this, gyroSensor, SensorManager.SENSOR_DELAY_NORMAL);
|
Теперь вы готовы реализовать функцию обратного вызова датчика onSensorChanged (), чтобы реагировать на эти обновления датчика.
Шаг 4: Реализация обратного вызова onSensorChanged ()
Большая часть интересного кода для обновлений датчика происходит в обратном вызове onSensorChanged (). Здесь нам нужно, чтобы вид реагировал на изменения датчика. Мы зарегистрировали несколько различных типов изменений, поэтому этот метод может быть вызван для любого изменения датчика. Сейчас мы просто хотим сохранить данные датчика, которые изменились, а затем принудительно обновить представление. Мы можем сделать это, просто аннулируя представление и заставляя его перерисовываться.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public void onSensorChanged(SensorEvent event) {
StringBuilder msg = new StringBuilder(event.sensor.getName()).append(» «);
for(float value: event.values)
{
msg.append(«[«).append(value).append(«]»);
}
switch(event.sensor.getType())
{
case Sensor.TYPE_ACCELEROMETER:
accelData = msg.toString();
break;
case Sensor.TYPE_GYROSCOPE:
gyroData = msg.toString();
break;
case Sensor.TYPE_MAGNETIC_FIELD:
compassData = msg.toString();
break;
}
this.invalidate();
}
|
Шаг 5: Ваше пользовательское наложение и основное действие
Теперь вам нужно добавить свой пользовательский вид в элемент управления FrameLayout. Для этого обновите метод onCreate () класса ArtutActivity. Добавьте следующий код чуть ниже вызова addView (), который добавляет ArDisplayView:
1
2
|
OverlayView arContent = new OverlayView(getApplicationContext());
arViewPane.addView(arContent);
|
Это объединяет два вида: сначала класс представления предварительного просмотра камеры, называемый ArDisplayView, а затем накладывается на OverlayView, который отображает данные датчика поверх него. Если вы запустите приложение сейчас, действие запустится, и вы увидите предварительный просмотр камеры на экране с данными датчика, отображенными поверх него (напомним, вы не можете делать снимки экрана камеры, поэтому на следующем рисунке показано только наложение контента) ,
Если вы запускаете приложение в этот момент, вы, вероятно, заметили, что данные датчика обновляются очень быстро, несмотря на запрос скорости, достаточно медленной для работы с изменениями ориентации. Эти датчики устройства имеют очень высокую степень чувствительности. В конечном итоге это приведет к шаткости в пользовательском интерфейсе. В конечном итоге нам потребуется реализовать некоторые алгоритмы сглаживания, чтобы избежать дрожания, когда мы начнем использовать данные датчиков в нашем приложении.
Вам также может быть интересно, будем ли мы использовать все эти датчики и каковы их единицы измерения. Мы не обязательно будем использовать все датчики, но мы подумали, что покажем вам, как они выглядят и как получить к ним доступ, если ваши конкретные интересы AR лежат в основе разных датчиков.
- Числа акселерометра даны в единицах СИ (м / с ^ 2, т.е. в метрах в секунду в квадрате, где сила тяжести Земли составляет 9,81 м / с ^ 2).
- Магнитные сенсорные блоки находятся в микротеслах. Поскольку они входят в формы x, y и z, мы можем измерить вектор и сравнить с гравитацией, чтобы определить, где (магнитный) север находится относительно устройства.
- Гироскоп измеряет вращение вокруг каждой оси в радианах в секунду. Это также может быть использовано для расчета относительной ориентации устройства.
Вывод
На этом мы завершаем первую часть нашего учебника по AR. Дополненная реальность — захватывающий жанр для приложений на платформе Android. Android SDK предоставляет все необходимое для разработки изящных и интересных приложений AR, но не все устройства могут соответствовать требованиям к оборудованию, требуемым этими приложениями. К счастью, новейшее поколение Android-устройств является самым мощным из всех, и многие из Android-устройств вполне способны запускать современные приложения AR.
В следующей части этого урока мы перейдем к сбору данных о местоположении и размещению объектов AR на экране в определенном месте. Это будет включать в себя различные математические алгоритмы, но конечный результат будет … увеличение !