SceneKit Tutorial

With iOS 8, Apple released a fantastic framework - SceneKit - that makes it simple to setup a 3D scene in a matter of minutes.

I am going to walk through the setup of a simple scene to show the power of this framework.

You can find my completed example code at my Github repo.

SceneKit Introduction

SceneKit is a node-based framework that allows for construction and rendering of scene graphs. Any transformations applied to a node’s geometry also apply to that node’s children. For example, if a 3D car lunges forward 10ft, then all of its parts move forward 10ft too – simulating physics. Additionally, every component’s relative position remains unchanged and independent e.g. a seat may tilt back without the car tilting back too.

The SCNScene object you’ll create and manipulate in this tutorial contains a rootNode property to which you can add several other SCNNode objects as child nodes, thus forming a tree structure like so:

From Apple’s Scene Kit Programming Guide

Setting Up Your UIViewController

It is very easy to get started with SceneKit. In this section, you’ll take a quick look at this template and familiarize yourself with a basic SceneKit scene.

Open Xcode and create a new iOS Application based on the Single View Application template. While you could easily create an application from the Game template using SceneKit, for this tutorial I am going to show you how to start working with SceneKit from scratch.

Enter a Product Name, set Language to Swift, and Devices to Universal. Click Next to continue.

After creating your project, navigate to ViewController.swift and add the following import statement at the top to import the SceneKit framework:

UIViewController
1
import SceneKit

Next, add the following implementation of the viewDidLoad method in the ViewController class:

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

In the viewDidLoad method, we create a SCNView object, passing in the frame of the view controller’s view. We assign the SCNView instance to a constant, sceneView, and add it as a subview of the view controller’s view.

The SCNView class is a subclass of UIView and provides an outlet for your SceneKit content. Aside from having the functionality of a regular view, an SCNView also has several properties and methods relating to the SceneKit content.

To check that everything is functioning correctly, build and run your app. You will see that you just have a blank white view.

Setting Up Your Scene

To render content in an SCNView, you first need to create an SCNScene and assign it to the view. In this scene, you then need to add a camera and at least one light. For this example, you are also going to add a cube for SceneKit to render. Add the following code to the viewDidLoad method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
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.1)
let cubeNode = SCNNode(geometry: cubeGeometry)
scene.rootNode.addChildNode(lightNode)
scene.rootNode.addChildNode(cameraNode)
scene.rootNode.addChildNode(cubeNode)
cameraNode.position = SCNVector3(x: -3.0, y: 3.0, z: 3.0)
let constraint = SCNLookAtConstraint(target: cubeNode)
constraint.gimbalLockEnabled = true
cameraNode.constraints = [constraint]
//allows user to move camera with touch
sceneView.allowsCameraControl = true
}

Build and run your app again, and you will see your cube in all of its 3D glory.

Materials

It’s time to add more realism to the scene with materials and shadows. You’re first going to need another object to cast a shadow onto. Use the following code snippet to create a plane, a flat rectangle, and position it below the cube. Don’t forget to add the new node as a child node to the scene’s root node.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
override func viewDidLoad() {
...
let cubeGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.1)
let cubeNode = SCNNode(geometry: cubeGeometry)
let planeGeometry = SCNPlane(width: 100.0, height: 100.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)
}

Next, add a material to the cube and the plane. For this example, you are going to give the cube and the plane a solid color, red and green respectively. Add the following lines to the viewDidLoad method to create these materials.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
override func viewDidLoad() {
...
planeNode.position = SCNVector3(x: 0, y: -0.5, z: 0)
let greenMaterial = SCNMaterial()
greenMaterial.diffuse.contents = UIColor.greenColor()
cubeGeometry.materials = [greenMaterial]
let blueMaterial = SCNMaterial()
blueMaterial.diffuse.contents = UIColor.blueColor()
planeGeometry.materials = [blueMaterial]
let constraint = SCNLookAtConstraint(target: cubeNode)
...
}

##The Final Scene!

After building and running, your scene should like this:

Your Simple SceneKit Scene