Статьи

Введение в SceneKit: основы

Конечный продукт
Что вы будете создавать

В этом уроке вы узнаете, как создать базовую 3D-сцену в SceneKit без сложностей OpenGL. Это включает в себя базовую геометрию, камеры, источники света, материалы и тени.

Инфраструктура SceneKit была впервые запущена Apple вместе с OS X 10.8 Mountain Lion и позже стала доступна для iOS с выпуском iOS 8. Цель этой платформы — позволить разработчикам легко интегрировать 3D-графику в игры и приложения без сложностей графические API, такие как OpenGL и Metal.

SceneKit позволяет вам просто предоставить описание ресурсов, которые вы хотите видеть в вашей сцене, а сама платформа обрабатывает весь код рендеринга OpenGL для вас. В этом первом уроке я познакомлю вас с некоторыми основами работы с трехмерными активами и основами инфраструктуры SceneKit.

Этот учебник требует, чтобы вы работали на Xcode 6 или выше . Хотя в этом нет необходимости, я рекомендую использовать физическое устройство под управлением iOS 8 для тестирования кода SceneKit. Вы можете использовать iOS Simulator, но производительность не будет хорошей, если ваша сцена станет более сложной. Обратите внимание, что для тестирования на физическом устройстве iOS требуется наличие зарегистрированной учетной записи разработчика iOS.

Первое, что вам нужно знать о SceneKit, — это то, что активы, представленные узлами, расположены в иерархическом дереве, называемом графом сцены . Если вы знакомы с разработкой для iOS, это дерево работает во многом как обычная иерархия представлений.   в UIKit. Каждая сцена, которую вы создаете, имеет единственный корневой узел, к которому вы добавляете последующие узлы, и который также обеспечивает основу для трехмерной системы координат этой сцены.

Когда вы добавляете узел к сцене, его положение задается набором из трех чисел, трехкомпонентного вектора, представленного структурой SCNVector3 в вашем коде. Каждый из этих трех компонентов определяет положение узла по осям x, y и z, как показано на рисунке ниже.

Трехмерная диаграмма координат
Изображение предоставлено: Apple SceneKit Framework Reference

Положение корневого узла вашей сцены определяется как (0, 0, 0) . На изображении выше это точка пересечения трех осей. Включенная камера на изображении представляет направление по умолчанию, на которое указывает камера, когда оно добавляется к вашей сцене.

Теперь, когда вы знаете некоторые основы того, как объекты представлены в SceneKit, вы готовы начать писать некоторый код.

Откройте Xcode и создайте новое приложение iOS на основе шаблона приложения Single View . Хотя вы можете легко создать приложение из шаблона Game, используя SceneKit, для этого урока я покажу вам, как начать работу с SceneKit с нуля.

Выберите шаблон приложения

Введите название продукта , установите для языка значение Swift , а для устройстваUniversal . Нажмите Далее, чтобы продолжить.

Детали приложения

После создания проекта перейдите к ViewController.swift и добавьте следующий оператор импорта вверху, чтобы импортировать инфраструктуру SceneKit:

1
import SceneKit

Затем добавьте следующую реализацию метода ViewController классе ViewController :

1
2
3
4
5
6
override func viewDidLoad() {
    super.viewDidLoad()
     
    let sceneView = SCNView(frame: self.view.frame)
    self.view.addSubview(sceneView)
}

В методе viewDidLoad мы создаем объект SCNView , передавая в кадре представление контроллера представления. Мы присваиваем экземпляр SCNView константе sceneView и добавляем его как подпредставление представления контроллера представления.

Класс SCNView является подклассом UIView и предоставляет выход для вашего содержимого SceneKit. Помимо функциональности обычного представления, SCNView также имеет несколько свойств и методов, относящихся к содержимому SceneKit.

Чтобы убедиться, что все работает правильно, соберите и запустите ваше приложение. Вы увидите, что у вас просто пустой белый вид.

Начальный вид приложения

Чтобы отобразить содержимое в SCNView , сначала необходимо создать SCNScene и назначить его представлению. В этой сцене вам нужно добавить камера и хотя бы один свет. В этом примере вы также собираетесь добавить куб для рендеринга SceneKit. Добавьте следующий код в метод viewDidLoad :

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
26
27
override func viewDidLoad() {
    super.viewDidLoad()
     
    let sceneView = SCNView(frame: self.view.frame)
    self.view.addSubview(sceneView)
         
    let scene = SCNScene()
    sceneView.scene = scene
 
    let camera = SCNCamera()
    let cameraNode = SCNNode()
    cameraNode.camera = camera
    cameraNode.position = SCNVector3(x: 0.0, y: 0.0, z: 3.0)
 
    let light = SCNLight()
    light.type = SCNLightTypeOmni
    let lightNode = SCNNode()
    lightNode.light = light
    lightNode.position = SCNVector3(x: 1.5, y: 1.5, z: 1.5)
 
    let cubeGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
    let cubeNode = SCNNode(geometry: cubeGeometry)
 
    scene.rootNode.addChildNode(lightNode)
    scene.rootNode.addChildNode(cameraNode)
    scene.rootNode.addChildNode(cubeNode)
}

Давайте viewDidLoad метод viewDidLoad шаг за шагом:

  • Сначала вы создаете сцену для своего представления, вызывая метод init . Если вы не загружаете подготовленную сцену из внешнего файла, этот инициализатор вы всегда будете использовать.
  • Затем вы создаете объект SCNNode экземпляр SCNNode для камеры. Затем вы назначаете объект SCNCamera свойству camera cameraNode и перемещаете этот узел вдоль оси z, чтобы увидеть куб, который вы создадите чуть позже.
  • На следующем шаге вы создадите объект SCNLight и SCNNode именем lightNode . Экземпляр SCNLight назначается свойству light узла light. Свойство type для SCNLight установлено в SCNLightTypeOmni . Этот тип света распределяет свет равномерно во всех направлениях от точки в трехмерном пространстве. Вы можете думать об этом типе света как обычная лампочка.
  • Наконец, вы создаете куб, используя класс SCNBox , делая ширину, высоту и длину одинаковыми по размеру. Класс SCNBox является подклассом SCNGeometry и является одной из примитивных фигур, которые вы можете создать. Другие формы включают сферы, пирамиды и торы. Вы также создаете узел, передающий в куб для параметра geometry .
  • Чтобы настроить сцену, вы добавляете три узла (камера, источник света и куб) к графу сцены сцены. Дополнительная настройка не требуется, поскольку объект SCNScene автоматически обнаруживает, когда узел содержит камеру или объект освещения, соответственно визуализируя сцену.

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

Первый рендеринг SceneKit

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

Однако, чтобы камера была направлена ​​прямо на куб, вы также добавите к камере SCNLookAtConstraint . Начните с обновления положения камеры, как показано ниже.

1
cameraNode.position = SCNVector3(x: -3.0, y: 3.0, z: 3.0)

Затем добавьте следующий фрагмент кода в метод viewDidLoad после создания экземпляра узла для куба:

1
2
3
let constraint = SCNLookAtConstraint(target: cubeNode)
constraint.gimbalLockEnabled = true
cameraNode.constraints = [constraint]

Изменение положения перемещает камеру влево и вверх. Добавляя ограничение, в котором куб является его целью, а gimbalLockEnabled значение true , вы гарантируете, что камера будет оставаться параллельной горизонту и окну просмотра, в данном случае экрану вашего устройства. Это делается путем отключения вращения вдоль оси крена, ось которого направлена ​​от камеры к цели ограничения.

Снова постройте и запустите приложение, и вы увидите свой куб во всей его трехмерной красе.

3D куб

Пришло время добавить больше реализма к сцене с материалами и тенями. Сначала вам понадобится другой объект, чтобы отбросить тень. Используйте следующий фрагмент кода, чтобы создать плоскость, плоский прямоугольник и расположите его под кубом. Не забудьте добавить новый узел в качестве дочернего узла в корневой узел сцены.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
override func viewDidLoad() {
    …
    let cubeGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
    let cubeNode = SCNNode(geometry: cubeGeometry)
     
    let planeGeometry = SCNPlane(width: 50.0, height: 50.0)
    let planeNode = SCNNode(geometry: planeGeometry)
    planeNode.eulerAngles = SCNVector3(x: GLKMathDegreesToRadians(-90), y: 0, z: 0)
    planeNode.position = SCNVector3(x: 0, y: -0.5, z: 0)
    …
    scene.rootNode.addChildNode(lightNode)
    scene.rootNode.addChildNode(cameraNode)
    scene.rootNode.addChildNode(cubeNode)
    scene.rootNode.addChildNode(planeNode)
}

Изменяя свойство eulerAngles узла плоскости, вы поворачиваете плоскость назад на 90 градусов вдоль оси x. Мы должны сделать это, потому что самолеты созданы вертикально по умолчанию. В SceneKit углы поворота рассчитываются в радианах, а не в градусах, но эти значения можно легко преобразовать с помощью GLKMathDegreesToRadians(_:) и GLKMathsRadiansToDegrees(_:) . GLK означает GLKit, каркас Apple OpenGL.

Затем добавьте материал к кубу и плоскости. В этом примере вы дадите кубу и плоскости сплошной цвет, соответственно красный и зеленый. Добавьте следующие строки в метод viewDidLoad для создания этих материалов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
override func viewDidLoad() {
    …
    planeNode.position = SCNVector3(x: 0, y: -0.5, z: 0)
     
    let redMaterial = SCNMaterial()
    redMaterial.diffuse.contents = UIColor.redColor()
    cubeGeometry.materials = [redMaterial]
     
    let greenMaterial = SCNMaterial()
    greenMaterial.diffuse.contents = UIColor.greenColor()
    planeGeometry.materials = [greenMaterial]
     
    let constraint = SCNLookAtConstraint(target: cubeNode)
    …
}

Для каждого объекта SCNMaterial его диффузное содержимое UIColor значение UIColor . Диффузное свойство материала определяет его внешний вид при прямом освещении. Обратите внимание, что присвоенное значение не обязательно должно быть объектом UIColor . Есть много других приемлемых типов объектов для назначения этому свойству, таких как UIImage , CALayer и даже текстура SKTexture ( SKTexture ).

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

Красный куб и зеленый самолет

Теперь пришло время добавить тени на вашу сцену. Из четырех типов освещения, доступных в SceneKit, только точечные источники света могут создавать тени. В этом примере вы собираетесь превратить ваш существующий омни-свет в точечный источник света, нацеленный на куб. Добавьте следующий код в метод viewDidLoad :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
override func viewDidLoad() {
    …
    let light = SCNLight()
    light.type = SCNLightTypeSpot
    light.spotInnerAngle = 30.0
    light.spotOuterAngle = 80.0
    light.castsShadow = true
    let lightNode = SCNNode()
    lightNode.light = light
    lightNode.position = SCNVector3(x: 1.5, y: 1.5, z: 1.5)
    …
    let constraint = SCNLookAtConstraint(target: cubeNode)
    constraint.gimbalLockEnabled = true
    cameraNode.constraints = [constraint]
    lightNode.constraints = [constraint]
    …
}

Чтобы создать точечный источник света, сначала установите тип SCNLightTypeSpot света SCNLightTypeSpot . Затем вы указываете внутренний и внешний углы прожектора в градусах. Значения по умолчанию — 0 и 45 соответственно. Внутренний угол определяет, какую площадь свет освещает при прямом освещении, в то время как внешний угол определяет, какая часть освещена частично. Разница между этими углами станет ясна, как только вы увидите получившуюся сцену. Затем вы явно указываете свету отбрасывать тени, а также добавляете тот же SCNLookAtConstraint который вы создали для вашей камеры ранее.

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

Куб и плоскость с тенью

Вы увидите, что теперь ваш куб правильно отбрасывает тень. Точечный свет, однако, освещает только часть самолета. Это потому, что в вашей сцене нет рассеянного света.

Окружающий свет — это источник света, который освещает все с равным распределением света. Поскольку окружающий свет освещает всю сцену, его положение не имеет значения, и вы можете добавить его к любому нужному узлу, даже к тому же узлу, что и ваша камера. Используйте следующий фрагмент кода, чтобы создать рассеянный свет для вашей сцены.

01
02
03
04
05
06
07
08
09
10
11
12
13
override func viewDidLoad() {
    …
    let camera = SCNCamera()
    let cameraNode = SCNNode()
    cameraNode.camera = camera
    cameraNode.position = SCNVector3(x: -3.0, y: 3.0, z: 3.0)
     
    let ambientLight = SCNLight()
    ambientLight.type = SCNLightTypeAmbient
    ambientLight.color = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1.0)
    cameraNode.light = ambientLight
    …
}

Фрагмент кода создает SCNLight , как вы делали раньше. Основным отличием является свойство type света, для которого установлено значение SCNLightTypeAmbient . Вы также устанавливаете его цвет на темно-серый, чтобы он не подавлял вашу сцену. Цвет по умолчанию для источника света — чистый белый (значение RGB 1, 1, 1), а наличие этого цвета на окружающем источнике света приводит к тому, что вся сцена полностью освещается, как показано на снимке экрана ниже.

Полностью освещенная сцена

Создайте и запустите приложение в последний раз, чтобы увидеть конечный результат.

Конечный продукт

Если вы дошли до конца этого урока, вы должны освоить следующие темы:

  • 3D-система координат и граф сцены, используемые SceneKit
  • настройка SCNView с помощью SCNScene
  • добавление камер, источников света и узлов к сцене
  • назначение материалов геометрии
  • работа со светом для освещения сцены и отбрасывания теней

В следующем уроке этой серии вы узнаете о некоторых более продвинутых концепциях инфраструктуры SceneKit, включая анимацию, взаимодействие с пользователем, системы частиц и симуляцию физики.