Дополненная реальность со Swift 5 – с чего начать

Дополненная реальность со Swift 5 – с чего начать

Представьте, что предел между виртуальным и реальным миром исчез.

Дополненная реальность – это результат использования технологии для наложения информации, такой как звуки, изображения, тексты, на реальный мир, который мы видим.

Популярные приложения Apple AR

Measure

С iOS 12 вы можете оставить рулетку в кухонном ящике. После установки iOS 12 вы увидите одно из новых приложений дополненной реальности от Apple под названием Measure. Приложение использует дополненную реальность в качестве линейки, измеряя объекты вокруг вас с помощью камеры телефона.

Just a Line

Just a Line позволяет создавать простые рисунки в дополненной реальности, а затем делиться своими творениями в виде краткого видео. Рисуйте один или с другом, а затем нажмите "записать" и поделитесь тем, что вы сделали, с помощью #justaline.

Knightfall: AR

Knightfall AR – это опыт дополненной реальности, который переносит нас в мир Рыцарей Храма, защищающих город Акко от захватнической армии и охраняющих ценнейшую реликвию христианства – Святой Грааль. Используйте свою смекалку и острый глаз, чтобы убить как можно больше вражеских воинов, прежде чем они прорвутся через стены.

Системные требования

Apple принес дополненную реальность в массы, начиная с iOS 11. С момента запуска iOS 11 сотни миллионов iPhone и iPad поддерживают AR. Это сразу делает Apple ARKit самой большой AR-платформой в мире.

Если вам интересно разработка AR-приложений для iOS, вы попали в правильное место.

ARKit 2

ARKit 2 для разработчиков iOS – это платформа, которую Apple представила вместе с iOS 12. Разработчики могут создавать взаимосвязанные пространства дополненной реальности и постоянные объекты, привязанные к определенным местам, а также использовать идентификацию объектов и автоматическое отслеживание изображений. Для создания демонстрационного проекта мы будем использовать ARKit 2.

Настройка проекта

Создание нового проекта

Для начала откройте Xcode и выберите в меню Файл > Создать > Проект. Выберите "Single View App" и нажмите "Далее". Xcode имеет образец ARKit, но для лучшего понимания мы используем образец "Single View App" для создания нового приложения дополненной реальности.

Разрешение на использование камеры

Теперь перед запуском нашего AR-приложения нам нужно уведомить пользователя, что мы будем использовать камеру его устройства для дополненной реальности. Это требование появилось с момента запуска iOS 10. Для этого откройте Info.plist, щелкните правой кнопкой мыши на пустом месте и выберите "Добавить строку", затем установите ключ "Конфиденциальность - Описание использования камеры" и в завершение добавьте описание того, зачем вам нужно использовать камеру.

Добавление ARKit SceneKit View к раскадровке

Откройте Main.storyboard и выберите ARKit SceneKit View из библиотеки.

Перетащите его на главный контроллер и закрепите по бокам.

Затем, нажав и удерживая правую кнопку на ARKit SceneKit View, мы связываем его с контроллером. После добавления @IBOutlet мы увидим ошибку "Use of undeclared type ARSCNView", это происходит, потому что мы не импортировали ARKit.

Просто добавив импорт ARKit в наш контроллер, мы исправим это.

Настройка сеанса ARSCNView

Мы хотим, чтобы наше приложение посмотрело на мир через камеру и начало распознавать окружающую среду. Apple сделал дополненную реальность доступной разработчикам без необходимости создавать всю технологию с нуля. Спасибо, Apple!

Прекрасно. Пора настроить ARKit SceneKit View. Заполните следующий код в классе ViewController:

 private let configuration = ARWorldTrackingConfiguration()
 
 override func viewDidLoad() {
        super.viewDidLoad()
        // Show statistics such as fps and timing information
        self.sceneView.showsStatistics = true
        self.sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
    }
 
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.sceneView.session.run(configuration)
    }
 
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.sceneView.session.pause()
    }

Первая строка – это конфигурация отслеживания внешнего мира. Но остановитесь, что это? Конфигурация отслеживания мира отслеживает ориентацию и положение устройства. Она также определяет реальные поверхности, видимые через камеру.

Для отладки viewDidLoad мы установили sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]. Это позволит увидеть, как ARKit обнаруживает поверхность. Когда мы запускаем программу, мы видим много желтых пятен на поверхности. Эти пятна позволяют оценить такие свойства как ориентация и положение физических объектов в текущей базовой среде. Чем больше пятен мы видим, тем больше шансов, что ARKit сможет распознать и отследить оцепление.

Далее, чтобы запустить сцену до того, как появится экран, мы запустим сеанс нашей сцены в viewWillAppear с нашей конфигурацией. Перед тем, как экран у viewWillDisappear исчезнет, мы поставим его на паузу. Запускаем приложение и смотрим, что у нас получилось:

Мы видим статистику в нижней части экрана, ее можно сделать более подробной, нажав на +. Также мы видим желтые пятна, они указывают на места, где обнаружены объекты.

AR оси

Ось X часто используется для позиционирования объекта в мире, справа или слева. Ось Y используется для позиционирования объектов в верхней или нижней части, а ось Z используется для определения того, насколько близко объект находится к источнику.

Положительное значение X перемещает объект вправо, а отрицательное – влево. Положительное значение Y поместит объект в верхней части, а отрицательное – в нижней, положительное значение Z приблизит объект к пользователю, а отрицательное – удалит.

Добавление виртуального объекта

Теперь давайте добавим код для добавления виртуального объекта в наш проект.

private var node: SCNNode!
 
func addBox(x: Float = 0, y: Float = 0, z: Float = -0.2) {
        // 1
        let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
 
        // 2
        let colors = [UIColor.green, // front
            UIColor.red, // right
            UIColor.blue, // back
            UIColor.yellow, // left
            UIColor.purple, // top
            UIColor.gray] // bottom
        let sideMaterials = colors.map { color -> SCNMaterial in
            let material = SCNMaterial()
            material.diffuse.contents = color
            material.locksAmbientWithDiffuse = true
            return material
        }
        box.materials = sideMaterials
 
        // 3
        self.node = SCNNode()
        self.node.geometry = box
        self.node.position = SCNVector3(x, y, z)
 
        //4
        sceneView.scene.rootNode.addChildNode(self.node)
    }

Давайте рассмотрим этот код более подробно:

  1. Создаем здесь виртуальный объект в виде куба с гранью 0,1 метра. 1 поплавок = 1 метр.
  2. Мы добавляем разные цвета к каждой грани и создаем массив цветов, затем создаем материал SCNMaterial для каждой грани на карте и назначаем полученный массив материалам нашего куба.
  3. Мы создаем объект SCNNode, он представляет положения и координаты объекта в трехмерном пространстве. Сам узел не содержит визуального содержимого. Добавляем в него наш кубик.
  4. Наконец, мы прибавляем наш узел к сцене.

Позиционирование и перемещение виртуального объекта

Пора добавить наш куб к реальному миру. Попробуем сделать так, чтобы кубик прилагался на экран в том месте, где мы нажимаем.

Во-первых, мы должны добавить функцию отключения щелчка на нашей сцене и вызвать ее в viewDidLoad().

private func addTapGesture() {
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
        self.sceneView.addGestureRecognizer(tapGesture)
    }

Затем мы добавим код, который найдет поверхность, куда поместить наш куб после щелчка, и добавит куб на сцену, если его нет, или переместит его, если он уже там.

  @objc func didTap(_ gesture: UIPanGestureRecognizer) {
        // 1
        let tapLocation = gesture.location(in: self.sceneView)
        let results = self.sceneView.hitTest(tapLocation, types: .featurePoint)
 
        // 2
        guard let result = results.first else {
            return
        }
 
        // 3
        let translation = result.worldTransform.translation
 
        //4
        guard let node = self.node else {
            self.addBox(x: translation.x, y: translation.y, z: translation.z)
            return
        }
        node.position = SCNVector3Make(translation.x, translation.y, translation.z)
        self.sceneView.scene.rootNode.addChildNode(self.node)
    }
 
//5
extension float4x4 {
    var translation: float3 {
        let translation = self.columns.3
        return float3(translation.x, translation.y, translation.z)
    }
}

Рассмотрим этот код:

  1. Определяем позицию клика на экране и передаем функцию hitTest нашей сцены, получаем массив ARHitTestResult, который определяет место контакта с поверхностью.
  2. Если точки контакта были найдены, мы получаем первую из них и двигаемся дальше, в противном случае – завершаем функцию.
  3. Используя поле worldTransform точки соприкосновения, определяем координаты этой точки в системе координат реального мира.
  4. Если объект существует, мы изменяем его положение, иначе вызываем функцию сложения и передаем ей координаты точки соприкосновения.
  5. Это расширение для float4x4, которое делает работу с координатами более удобной.

Запустив код и нажав на поверхность, мы увидим наш разноцветный куб:

Масштабирование объекта

Теперь добавим функцию масштабирования куба. Мы сделаем так, чтобы наш куб растягивался и сужался жестом сжатия.

Как и в предыдущем примере, мы добавим функцию для определения сжатия и вызовем ее в viewDidLoad().

   private func addPinchGesture() {
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(didPinch(_:)))
        self.sceneView.addGestureRecognizer(pinchGesture)
    }
 

Затем мы добавим функцию, которая будет растягивать и сужать куб до определенного размера.

  @objc func didPinch(_ gesture: UIPinchGestureRecognizer) {
 
        switch gesture.state {
        // 1
        case .began:
            gesture.scale = CGFloat(node.scale.x)
        // 2
        case .changed:
            var newScale: SCNVector3
	// a
            if gesture.scale < 0.5 {
                newScale = SCNVector3(x: 0.5, y: 0.5, z: 0.5)
	// b
            } else if gesture.scale > 3 {
                newScale = SCNVector3(3, 3, 3)
	// c
            } else {
                newScale = SCNVector3(gesture.scale, gesture.scale, gesture.scale)
            }
	// d
            node.scale = newScale
        default:
            break
        }
    }

Давайте рассмотрим этот код:

  1. Во-первых – берём жест сжатия и добавляем к нему масштаб нашего куба для случаев, когда куб уже был растянут и мы хотим продолжить масштабирование, а не начинать его с самого начала.
  2. При растяжении или сужении:
  • Если масштаб меньше 0.5, мы оставляем его на уровне 0.5
  • Если масштаб превышает 3, мы оставляем его на уровне 3
  • Во всех остальных случаях устанавливаем масштаб, соответствующий жесту сжатия.
  • Устанавливаем масштаб для нашего узла.

Вращение объекта

Теперь добавим функцию вращения объекта вокруг оси Y. Мы заставим наш куб вращаться вокруг оси Y с помощью жеста вращения.

Как и в предыдущем примере, мы добавим функцию для определения жеста вращения и вызовем ее в viewDidLoad().

 private func addRotationGesture() {
        let panGesture = UIRotationGestureRecognizer(target: self, action: #selector(didRotate(_:)))
        self.sceneView.addGestureRecognizer(panGesture)
    }

И вращение самого куба:

  private var lastRotation: Float = 0
 
    @objc func didRotate(_ gesture: UIRotationGestureRecognizer) {
        switch gesture.state {
        case .changed:
            // 1
            self.node.eulerAngles.y = self.lastRotation + Float(gesture.rotation)
        case .ended:
            // 2
            self.lastRotation += Float(gesture.rotation)
        default:
            break
        }
    }

Давайте рассмотрим этот код:

  1. Изменяем угол Y в узле.
  2. Сохраняем последнее значение поворота.

Будущее ARKit

На конференции для разработчиков WWDC 2019 Apple представила обновленную платформу ARKit 3. ARKit 3 идет дальше, чем когда-либо прежде, используя People Occlusion и показывая AR-контент людям естественным образом, перед ними или позади них, отслеживая до 3 человек одновременно, поддерживая общий доступ к сеансам и многое другое. Используя новые знания ARKit о людях, можно будет интегрировать движение людей в приложение.

Вывод

Поздравляем вас! Мы создали небольшое приложение с дополненной реальностью. Теперь вы знаете, как разрабатывать AR-приложения, настраивать проект, добавлять виртуальные объекты, изменять их положение, масштабировать и вращать. ARKit – это большая тема, и в этой статье мы затронули лишь некоторые основы, но это лишь верхушка большого айсберга под названием ARKit. Я надеюсь, что эта информация была полезна для вас и желаю вам удачи в ваших начинаниях.

Вы можете найти полный демо-проект на on Bitbucket