SceneKit для Swift: Полное руководство по 3D-графике
SceneKit — это мощный фреймворк для работы с 3D-графикой, разработанный компанией Apple. Он предоставляет разработчикам высокоуровневый API для создания, визуализации и анимации трёхмерных сцен на всех платформах Apple: iOS, macOS, tvOS и watchOS. В отличие от низкоуровневых графических API (Metal, OpenGL), SceneKit абстрагирует сложные математические вычисления и работу с GPU, позволяя сосредоточиться на логике приложения.
Зачем нужен SceneKit? Этот фреймворк идеально подходит для создания игр, интерактивных 3D-презентаций, визуализации данных, архитектурных макетов и образовательных приложений. Он поддерживает физический движок (симуляция гравитации, коллизий), систему частиц, скелетную анимацию, работу с тенями и светом, а также интеграцию с дополненной реальностью (ARKit). SceneKit позволяет загружать модели в форматах .dae (Collada), .scn (собственный формат Apple) и .usdz, что делает его отличным инструментом для импорта ассетов из Blender, Maya или 3ds Max.
Главное преимущество SceneKit перед конкурентами (например, Unity) — это нативная интеграция с экосистемой Apple. Вы можете использовать SwiftUI, UIKit или AppKit для создания интерфейса, а SceneKit — для отрисовки 3D-контента, при этом весь код будет написан на одном языке (Swift). Фреймворк автоматически управляет памятью и оптимизирует рендеринг под конкретное устройство, что особенно важно для мобильных приложений.
Установка и настройка
SceneKit является частью стандартного SDK Apple, поэтому отдельная установка через менеджеры пакетов (CocoaPods, Swift Package Manager) не требуется. Для начала работы достаточно импортировать фреймворк в ваш проект.
Команда для установки отсутствует, так как фреймворк предустановлен. Однако, если вы используете Swift Package Manager для зависимостей, убедитесь, что ваш проект настроен на iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 3.0+.
Для импорта в код используйте:
import SceneKit
import SceneKit.ModelIO // Для загрузки моделей через Model I/O
Если вы работаете с Xcode, просто добавьте SceneKit.framework в раздел "Frameworks, Libraries, and Embedded Content" вашего таргета.
Быстрый старт: минимальный рабочий пример
Создадим простейшую 3D-сцену с вращающимся кубом. Этот пример можно запустить в Playground или в приложении для iOS/macOS.
import SceneKit
import PlaygroundSupport
// 1. Создаем сцену
let scene = SCNScene()
// 2. Создаем камеру
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 5)
scene.rootNode.addChildNode(cameraNode)
// 3. Создаем куб
let boxGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
let boxNode = SCNNode(geometry: boxGeometry)
boxNode.position = SCNVector3(x: 0, y: 0, z: 0)
scene.rootNode.addChildNode(boxNode)
// 4. Добавляем анимацию вращения
let rotateAction = SCNAction.rotateBy(x: 0, y: CGFloat.pi * 2, z: 0, duration: 5.0)
let repeatAction = SCNAction.repeatForever(rotateAction)
boxNode.runAction(repeatAction)
// 5. Настраиваем отображение в Playground
let scnView = SCNView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
scnView.scene = scene
scnView.backgroundColor = UIColor.darkGray
scnView.autoenablesDefaultLighting = true
PlaygroundPage.current.liveView = scnView
Основные классы и методы SceneKit
Ниже представлены ключевые компоненты фреймворка с примерами использования.
1. SCNScene — контейнер для всей 3D-сцены
Сигнатура: class SCNScene : NSObject
Что делает: Является корневым объектом, содержащим все узлы (SCNNode), геометрию, источники света, камеры и физические тела. Сцена — это граф узлов.
let scene = SCNScene()
scene.background.contents = UIColor.black // Устанавливаем цвет фона
2. SCNNode — узел сцены
Сигнатура: class SCNNode : NSObject
Что делает: Представляет элемент в графе сцены. Узел может содержать геометрию, свет, камеру или быть пустым (для группировки). Узлы образуют иерархию: перемещение родительского узла влияет на все дочерние.
let parentNode = SCNNode()
parentNode.position = SCNVector3(0, 1, 0)
scene.rootNode.addChildNode(parentNode)
let childNode = SCNNode(geometry: SCNSphere(radius: 0.5))
childNode.position = SCNVector3(0, 0, 0)
parentNode.addChildNode(childNode)
3. SCNGeometry — геометрическая форма
Сигнатура: class SCNGeometry : NSObject
Что делает: Абстрактный класс для всех 3D-форм. SceneKit предоставляет встроенные примитивы: SCNBox, SCNSphere, SCNCylinder, SCNCone, SCNTorus, SCNPlane, SCNText. Можно создавать кастомную геометрию через SCNGeometrySource и SCNGeometryElement.
let sphere = SCNSphere(radius: 1.0)
sphere.segmentCount = 48 // Увеличиваем детализацию сферы
let sphereNode = SCNNode(geometry: sphere)
4. SCNMaterial — материал для геометрии
Сигнатура: class SCNMaterial : NSObject
Что делает: Определяет внешний вид поверхности: цвет, текстуру, прозрачность, отражение. Каждая грань геометрии может иметь свой материал (diffuse, specular, normal, emission и т.д.).
let material = SCNMaterial()
material.diffuse.contents = UIColor.blue
material.specular.contents = UIColor.white
material.shininess = 0.5
let box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1)
box.materials = [material]
5. SCNLight — источник света
Сигнатура: class SCNLight : NSObject
Что делает: Освещает сцену. Поддерживаются типы: omni (всенаправленный), directional (направленный), spot (прожектор), ambient (окружающий). Свет может отбрасывать тени.
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .directional
lightNode.light?.color = UIColor.white
lightNode.light?.castsShadow = true
lightNode.position = SCNVector3(2, 3, 4)
lightNode.eulerAngles = SCNVector3(-Float.pi/4, Float.pi/4, 0)
scene.rootNode.addChildNode(lightNode)
6. SCNCamera — камера
Сигнатура: class SCNCamera : NSObject
Что делает: Определяет точку обзора сцены. Можно настроить угол обзора (fieldOfView), глубину резкости (zNear, zFar), эффекты размытия и HDR.
let camera = SCNCamera()
camera.fieldOfView = 60
camera.zNear = 0.1
camera.zFar = 100
camera.wantsHDR = true
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(0, 0, 10)
7. SCNAction — анимация действий
Сигнатура: class SCNAction : NSObject
Что делает: Позволяет анимировать свойства узла (позицию, вращение, масштаб) без написания циклов. Поддерживает последовательности, группы, повторения и easing-функции.
let moveAction = SCNAction.move(by: SCNVector3(0, 2, 0), duration: 1.0)
let fadeAction = SCNAction.fadeOut(duration: 0.5)
let sequence = SCNAction.sequence([moveAction, fadeAction])
node.runAction(sequence)
8. SCNPhysicsBody — физическое тело
Сигнатура: class SCNPhysicsBody : NSObject
Что делает: Добавляет узлу физические свойства: массу, трение, упругость. Узел начинает взаимодействовать с гравитацией и другими физическими телами. Типы: static, dynamic, kinematic.
let floor = SCNFloor()
let floorNode = SCNNode(geometry: floor)
floorNode.physicsBody = SCNPhysicsBody(type: .static, shape: nil)
let ball = SCNSphere(radius: 0.5)
let ballNode = SCNNode(geometry: ball)
ballNode.position = SCNVector3(0, 5, 0)
ballNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
ballNode.physicsBody?.mass = 1.0
scene.physicsWorld.gravity = SCNVector3(0, -9.8, 0)
9. SCNParticleSystem — система частиц
Сигнатура: class SCNParticleSystem : NSObject
Что делает: Создает эффекты частиц: дым, огонь, снег, искры. Можно настроить скорость, цвет, размер, время жизни частиц.
let particleSystem = SCNParticleSystem()
particleSystem.birthRate = 100
particleSystem.particleLifeSpan = 2.0
particleSystem.particleColor = UIColor.orange
particleSystem.emissionDuration = 5.0
let emitterNode = SCNNode()
emitterNode.addParticleSystem(particleSystem)
emitterNode.position = SCNVector3(0, 0, 0)
10. SCNTransaction — пакетное обновление анимации
Сигнатура: class SCNTransaction : NSObject
Что делает: Позволяет группировать изменения свойств и анимировать их как единый блок. Аналог UIView.animate для SceneKit.
SCNTransaction.begin()
SCNTransaction.animationDuration = 2.0
node.position = SCNVector3(5, 0, 0)
node.opacity = 0.5
SCNTransaction.commit()
Реальный практический пример: Интерактивная солнечная система
Создадим миниатюрную модель солнечной системы с вращающимися планетами, источником света и возможностью вращения камеры жестом.
import SceneKit
import UIKit
class SolarSystemViewController: UIViewController {
var scnView: SCNView!
var scene: SCNScene!
override func viewDidLoad() {
super.viewDidLoad()
setupScene()
setupCamera()
setupLight()
createSun()
createEarth()
createMoon()
addGestures()
}
func setupScene() {
scene = SCNScene()
scene.background.contents = UIImage(named: "stars_background")
scnView = SCNView(frame: view.bounds)
scnView.scene = scene
scnView.allowsCameraControl = true // Встроенное управление камерой
scnView.showsStatistics = true
view.addSubview(scnView)
}
func setupCamera() {
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(0, 5, 15)
cameraNode.look(at: SCNVector3(0, 0, 0))
scene.rootNode.addChildNode(cameraNode)
}
func setupLight() {
// Основной свет (Солнце)
let sunLight = SCNNode()
sunLight.light = SCNLight()
sunLight.light?.type = .omni
sunLight.light?.color = UIColor(red: 1.0, green: 0.8, blue: 0.2, alpha: 1.0)
sunLight.light?.intensity = 1200
sunLight.position = SCNVector3(0, 0, 0)
scene.rootNode.addChildNode(sunLight)
// Окружающий свет
let ambientLight = SCNNode()
ambientLight.light = SCNLight()
ambientLight.light?.type = .ambient
ambientLight.light?.color = UIColor.darkGray
scene.rootNode.addChildNode(ambientLight)
}
func createSun() {
let sunGeometry = SCNSphere(radius: 1.5)
let sunMaterial = SCNMaterial()
sunMaterial.diffuse.contents = UIColor.orange
sunMaterial.emission.contents = UIColor(red: 1.0, green: 0.5, blue: 0.0, alpha: 1.0)
sunGeometry.materials = [sunMaterial]
let sunNode = SCNNode(geometry: sunGeometry)
sunNode.position = SCNVector3(0, 0, 0)
scene.rootNode.addChildNode(sunNode)
// Анимация вращения Солнца
let rotateAction = SCNAction.rotateBy(x: 0, y: CGFloat.pi * 2, z: 0, duration: 20.0)
sunNode.runAction(SCNAction.repeatForever(rotateAction))
}
func createEarth() {
let earthGeometry = SCNSphere(radius: 0.6)
let earthMaterial = SCNMaterial()
earthMaterial.diffuse.contents = UIColor.blue
earthMaterial.specular.contents = UIColor.white
earthGeometry.materials = [earthMaterial]
let earthNode = SCNNode(geometry: earthGeometry)
earthNode.position = S Также в библиотеке
Генератор QR-кодов на Swift: Core Image, CIFilter и собственные решения
Работа с CSV в C# (.NET): библиотеки и примеры
Планировщик задач на C# (.NET): Quartz.NET, Hangfire, Coravel