На WWDC 2017 Apple анонсировала ARKit — SDK для работы с дополненной реальностью. Благодаря ему порог вхождения в эту технологию стал значительно ниже. Можно ожидать появления большого количества качественных игр и приложений.
Если вы смотрели Keynote, то, вероятно, вы уже в восторге от увиденного. Игровой мир, который инженеры Apple смогли развернуть на обычном столе при помощи ARKit, не может оставить равнодушными даже самых искушенных геймеров. Это был не просто прототип, а хорошо работающая технология, над которой действительно потрудились. В этом легко убедиться, запустив несколько демо или попробовав самим привнести что-либо виртуальное в наш мир.
Вынужден расстроить счастливых обладателей iPhone 6 и ниже. На данных девайсах все эти прелести жизни будут недоступны. Для использования всех ключевых функций ARKit необходим процессор А9 и выше. Apple, конечно, даст урезанный доступ к функциональности, но это уже совсем не то.
Дополненная реальность
Дополненная реальность (augmented reality, AR) — это виртуальная среда, которая накладывается на реальный мир для придания ему большей выразительности, информативности или просто ради развлечения. Термин, предположительно, был предложен исследователем компании Boeing Томасом Коделлом еще в 1990 году. Уже тогда начали появляться первые примеры устройств с применением данной технологии. Впервые дополненная реальность была реализована на электронных шлемах летчиков для вывода информации о полете и радаре.
Хочется спросить, чем же все занимались почти 20 лет и почему масштабное развитие эта технология получила лишь сейчас. Все предельно просто. Появление хороших камер в телефонах, сенсоров и развитие технологий компьютерного зрения сделали это возможным.
Что же можно сделать полезного и чего ждать в ближайшее время на полках AppStore? На самом деле все ограничивается лишь фантазией разработчиков. Можно с уверенностью назвать несколько отраслей, где AR произведет революцию с выходом нового фреймворка от Apple:
- Игровая индустрия;
- Архитектура;
- Киноиндустрия.
Возможности ARKit
ARKit — не волшебная палочка Гарри Поттера, а инструмент, который умеет грамотно обрабатывать большое количество данных, полученных от устройства. Благодаря камере и датчикам движения фреймворк отслеживает движение, находит поверхности и определяет освещенность. После анализа данных мы получаем конкретное представление об окружающем мире в виде точек пересечения, координат поверхностей и положении камеры в пространстве.
Основой задачей ARKit является слежение за окружающим миром (World Tracking) для создания виртуальной модели реального мира. Фреймворк распознает особенности видеокадров, отслеживает изменения их положения и сравнивает эту информацию с данными от датчиков движения. Результатом является виртуальная модель реального мира. Отдельная возможность — распознавание плоских горизонтальных поверхностей. ARKit находит плоскости и сообщает об их расположении и размерах.
Слежение за окружающим миром требует анализа картинки, получаемой от камеры. Для достижения наилучшего результата, необходимо хорошее освещение.
Основой ARKit являются ARSCNView
и ARSKView
. Они служат для отображения live видео и рендеринга 3D и 2D изображений. Как все уже догадались, это наследники от SCNView
и SKView
. Следовательно, ARKit не привносит каких-то невероятных особенностей в отображении данных. Это все те же движки для работы с 2D и 3D графикой, с которыми уже все знакомы. Поэтому порог вхождения в данную технологию будет достаточно низким. Apple знаменита любовью к своим технологиям и продуктам, но несмотря на это разработчики ARKit сделали поддержку Unity и Unreal Engine. Это положительно скажется на количестве качественных приложений, которые появятся в ближайшее время.
ARSCNView
и ARSKView
содержат в себе сердце ARKit — ARSession
. Именно этот класс содержит в себе все необходимое для работы с дополненной реальностью. Для запуска ARSession
необходимо передать конфигурацию работы сессии.
Тип конфигурации определяет стиль и качество работы AR, которое может быть достигнуто:
- На девайсах с процессором A9 и новее можно использовать
ARWorldTrackingSessionConfiguration
. Именно эта конфигурация дает возможность воспользоваться всей мощью нового фреймворка. Для вас будет создана модель окружающего мира в виртуальной реальности и предоставлена информация о плоскостях в поле видимости камеры. Это поможет расположить виртуальные объекты с максимальной точностью. - На остальных девайсах, поддерживающих ARKit, будет доступна лишь
ARSessionConfiguration
. Базовый класс предоставляет только информацию о движении устройства в пространстве, но не строит виртуальных моделей. Это не даст необходимого эффекта и не позволит насладиться всем качеством новой технологии. Вам будет недоступна возможность фиксации виртуальных объектов относительно объектов реального мира.
После выбора типа конфигурации необходимо создать ее экземпляр, произвести настройку и запустить сессию:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingSessionConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
Важно помнить, что ARKit потребляет довольно много энергии для расчетов. Если View с контентом не отображается в данный момент на экране, то имеет смысл приостановить сессию на это время, используя session.pause()
.
После запуска сессии можно начинать работать с виртуальным контентом. Если хотите, чтобы ARKit распознавал плоскости, не забудьте установить значение planeDetection
у конфигурации в значение horizontal
. Изначально распознавание горизонтальных поверхностей выключено. Будем надеяться, что в будущем появится возможность находить и вертикальные поверхности, но пока только горизонтальные.
Способ получения информации об окружающей среде зависит от того, какой вид отображения данных вы будете использовать ARSCNView
, ARSKView
или Metal
. Единицей информации, которую предоставляет ARKit, является ARAnchor
. Если у вас включено распознавание поверхностей, то вы столкнетесь с сабклассом ARPlaneAnchor
. Он содержит в себе информацию о найденных плоскостях. Благодаря данным якорям есть возможность ориентироваться в пространстве. В случае использования Metal вам придется вручную заниматься рендерингом. Тогда можете подписаться на обновления, используя делегат ARSessionDelegate
у класса ARSession
, и получать якоря от сессии. Если используете один из Apple движков для рендеринга объектов, тогда есть возможность воспользоваться более удобными делегатами ARSCNViewDelegate
или ARSKViewDelegate
.
На первый взгляд все довольно просто. Почти всю сложную работу делает ARSession
. Давайте попробуем сделать тестовое приложение.
Тестируем возможности ARKit
Дополненная реальность сейчас у всех ассоциируется с игрой Pokémon GO, которая взорвала рынок игровой индустрии. Попробуем сделать нечто похожее.
Для создания тестового приложения мы воспользуемся ARSCNView
для создания и рендеринга 3D моделей. Наша игра будет состоять из 2 этапов. Сначала мы будем расставлять мишени по комнате, а после пытаться как можно быстрее попасть по ним всем. Игра довольна примитивна, но продемонстрирует простоту создания игр с дополненной реальностью.
Начнем с того, что растянем на весь ViewController ARSCNView
и создадим IBOutlet. Далее будем работать с ней, как с обычной SCNView
. Произведем первоначальную настройку. Сделаем контроллер делегатом контактов физического мира и выведем статистику. Настроим запуск и паузу сессии при появлении и скрытии контроллера.
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene.physicsWorld.contactDelegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARSessionConfiguration.isSupported ?
ARWorldTrackingSessionConfiguration() : ARSessionConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
Выбор конфигурации сессии зависит от модели устройства, на котором запустили приложение. Крайне важно сделать эту проверку. Иначе, в случае неверной конфигурации, сессия пришлет ошибку и игра не запустится вообще.
ARKit настолько прост, что мы больше не будем использовать никакие его настройки. Единственное, что еще понадобится — это расположение камеры в пространстве виртуального мира. Остальное — дело техники и немного SceneKit.
Мы не будем здесь описывать обработку нажатий или подсчет очков. Это не так важно, и вы можете это увидеть сами в ДЕМО, представленном в конце статьи.
Наша игра содержит две модели объектов: шарик, которым мы будем стрелять, и летающие логотипы Touch Instinct. Для добавления этих моделей на экран, необходимо создать их, используя SCNNode
.
Что понадобится, чтобы создать физический объект:
- задать фигуру определенного размера;
- создать фигуру с физическими свойствами для контролирования контактов с другими объектам;
- создать физическое тело для описания поведения объекта при соприкосновении;
- задать текстуры.
Пример реализации классов патрона в виде шара и логотипа в виде куба с нужными текстурами.
class ARBullet: SCNNode {
override init() {
super.init()
let arKitBox = SCNSphere(radius: 0.025)
self.geometry = arKitBox
let shape = SCNPhysicsShape(geometry: arKitBox, options: nil)
self.physicsBody = SCNPhysicsBody(type: .dynamic, shape: shape)
self.physicsBody?.isAffectedByGravity = false
self.physicsBody?.categoryBitMask = CollisionCategory.arBullets.rawValue
self.physicsBody?.contactTestBitMask = CollisionCategory.logos.rawValue
// add texture
let material = SCNMaterial()
material.diffuse.contents = UIImage(named: "art.scnassets/ARKit_logo.png")
self.geometry?.materials = [material]
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class Logo: SCNNode {
override init() {
super.init()
let logo = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
self.geometry = logo
let shape = SCNPhysicsShape(geometry: logo, options: nil)
self.physicsBody = SCNPhysicsBody(type: .dynamic, shape: shape)
self.physicsBody?.isAffectedByGravity = false
self.physicsBody?.categoryBitMask = CollisionCategory.logos.rawValue
self.physicsBody?.contactTestBitMask = CollisionCategory.arBullets.rawValue
// add texture
let material = SCNMaterial()
material.diffuse.contents = UIImage(named: "art.scnassets/logo-mobile.png")
self.geometry?.materials = Array(repeating: material, count: 6)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Хочется обратить внимание на CollisionCategory. Это структура используется для определения типа объекта при контакте.
struct CollisionCategory: OptionSet {
let rawValue: Int
static let arBullets = CollisionCategory(rawValue: 1 << 0)
static let logos = CollisionCategory(rawValue: 1 << 1)
}
Это стандартная тактика для определения контакта. Свойство categoryBitMask
задает маску конкретного объекта, а contactTestBitMask
настраивает все контакты, которые нам будут интересны и о которых мы хотим получать уведомления.
Раз мы заговорили про обработку контактов, давайте посмотрим, как это выглядит в контроллере. Во viewDidLoad
мы уже подписались на события контактов физического мира. Осталось реализовать одну функцию.
extension ViewController: SCNPhysicsContactDelegate {
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
guard let nodeABitMask = contact.nodeA.physicsBody?.categoryBitMask,
let nodeBBitMask = contact.nodeB.physicsBody?.categoryBitMask,
nodeABitMask & nodeBBitMask == CollisionCategory.logos.rawValue & CollisionCategory.arBullets.rawValue else {
return
}
contact.nodeB.removeFromParentNode()
logoCount -= 1
if logoCount == 0 {
DispatchQueue.main.async {
self.stopGame()
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
contact.nodeA.removeFromParentNode()
})
}
}
Самое интересное — это первая проверка того, что произошло именно соударение патрона и мишени в виде куба. Она выполняется на основе битовой маски. Это очень удобно и избавляет от большого количества других проверок.
При соударении мы убавляем счетчик оставшихся логотипов и удаляем оба объекта. Второй объект удаляется с небольшой задержкой для визуализации столкновения.
Две основные игровые функции — это добавление мишени и выстрел. Добавление происходит на небольшом расстоянии от экрана в той стороне, куда направлена камера. Мы просто создаем уже сконфигурированный объект куба, добавляем его на сцену и настраиваем его расположение относительно камеры в пространстве.
private func addLogo() {
guard let currentFrame = sceneView.session.currentFrame else {
return
}
let logo = Logo()
sceneView.scene.rootNode.addChildNode(logo)
var translation = matrix_identity_float4x4
translation.columns.3.z = -1
logo.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
logoCount += 1
if logoCount == ViewController.logoMaxCount {
startGame()
}
}
При выстреле мы также создаем объект шара. Добавляем его на сцену. Но теперь нам необходимо не просто его добавить, но и придать ему ускорение. Для этого мы определяем позицию посередине экрана и придаем ускорение, приложив силу в нужном направлении.
private func shoot() {
let arBullet = ARBullet()
let (direction, position) = cameraVector
arBullet.position = position
arBullet.physicsBody?.applyForce(direction, asImpulse: true)
sceneView.scene.rootNode.addChildNode(arBullet)
}
Вот так всего за пару десятков строк мы создали простую игру.
Будущее наступит в сентябре
Как видите, Apple потрудились на славу. Благодаря новому фреймворку ARKit создание приложений с дополненной реальностью — так же просто, как сделать приложение с несколькими контроллерами. При этом вам не нужно беспокоиться о красивых декорациях. Эта технология точно изменит наше представление о мобильных приложениях.
Скачивайте новый Xcode 9, и создавайте приложения, которые добавят в наш мир виртуальной магии. Будущее уже здесь. Ну или будет здесь ближе к сентябрю, после очередной презентации Apple.
Демо проект
Скачивайте в репозитории Touch Instinct