SceneKit is a high level 3D framework for Mountain Lion1 that was introduced almost a year ago at WWDC 12. It is all Objective-C and integrates with other UI frameworks like Cocoa and Core Animation. This means that you can use normal
CATransform3Ds to configure your 3D scene. It also means that you can easily animate property changes, like for example
transform, using a regular
addAnimation:forKey:. Sounds amazing? It is.
Shortly after it was introduced Jeff LaMarche wrote a great Introduction to SceneKit. Since then it has mostly been quiet — at the time of this writing SceneKit has only 12 questions asked on Stack Overflow — until recently when (once again) Jeff LaMarche gave a presentation at CocoaConf San Jose with a lot of sample code available on GitHub and Delicious Library 3 launched with 3D shelves made using SceneKit. SceneKit may have been launched at the wrong time, in the middle of the debate about skeuomorphism but I think it’s a great framework and I’m also going to try to show why that is.
The basics of a scene
The mental model of SceneKit is similar to how you would think of a scene in real life, and similar to that of OpenGL 1 if you ever used it. A scene is composed of geometry with different material that is lit up by lights and seen through a camera. These nouns correspond to the classes
SCNCamera. There wouldn’t be much of a 3D scene without the ability to position and rotate any of these elements but neither of them have a
transform property. That is where nodes come in.
Nodes, instances of
SCNNode, are the backbone of a scene. Think of them like a plain layer but in 3D. Nodes are hierarchical and you can add, insert, replace and remove child nodes using
There resemblance to layer hierarchies in 2D is obvious.
Nodes are positioned relative to their parent node using the
position property that unlike its 2D equivalent takes a
SCNVector3 instead of a
CGPoint. Don’t let the vector name scare you, it’s just a 3D coordinate (x, y, z)2. The
transform property takes a normal
CATransform3D (transforms were 3D even in the 2D world of layers) but there are also convenience properties for the
rotation that automatically change when the transform is set and vice versa. There is also a 3D equivalent to the anchor point called the
pivot that influences position and transform just like the anchor point does.
A node in itself is nothing but an empty container but each node has properties to have a geometry, a light, or a camera attached to it.
A basic scene
A scene is displayed in a
SCNView. The node hierarchy is attached to the scenes
rootNode. In this minimal example we create a single box with a single red spotlight and move both the spotlight and the camera back and up from the center of the scene. To give the effect that the spotlight is next to the camera we add the spotlight node to the camera node and offset it slightly. The box remains centered in the scene. This is all the code it takes:
// An empty scene SCNScene *scene = [SCNScene scene]; self.scene = scene; // A camera SCNNode *cameraNode = [SCNNode node]; cameraNode.camera = [SCNCamera camera]; cameraNode.position = SCNVector3Make(0, 15, 30); cameraNode.transform = CATransform3DRotate(cameraNode.transform, -M_PI/7.0, 1, 0, 0); [scene.rootNode addChildNode:cameraNode]; // A spotlight SCNLight *spotLight = [SCNLight light]; spotLight.type = SCNLightTypeSpot; spotLight.color = [NSColor redColor]; SCNNode *spotLightNode = [SCNNode node]; spotLightNode.light = spotLight; spotLightNode.position = SCNVector3Make(-2, 1, 0); [cameraNode addChildNode:spotLightNode]; // A square box CGFloat boxSide = 15.0; SCNBox *box = [SCNBox boxWithWidth:boxSide height:boxSide length:boxSide chamferRadius:0]; SCNNode *boxNode = [SCNNode nodeWithGeometry:box]; boxNode.transform = CATransform3DMakeRotation(M_PI_2/3, 0, 1, 0); [scene.rootNode addChildNode:boxNode];
And the result is something like this
My absolute favorite part of SceneKit is the excellent integration with Core Animation (which still remains my favorite framework). That integration comes in two parts: properties in SceneKit can be animated using
CAAnimations3 and layers can be used as content (textures) of SceneKit materials.
Animating scene properties
Animations in SceneKit works just like Core Animation have always worked. I could be done with just that sentence but let’s do an example just to see how cool and powerful it is. Let’s take the very static example above and bring it to life by animating the rotation of the box and the color of the spotlight.
We want to animate the
color property of the spotlight and the
transform property of the box node. We create two animations for the two key paths and add them to the light and the box node.
// Changing the color of the spotlight CAKeyframeAnimation *spotColor = [CAKeyframeAnimation animationWithKeyPath:@"color"]; spotColor.values = @[(id)[NSColor redColor], (id)[NSColor blueColor], (id)[NSColor greenColor], (id)[NSColor redColor]]; spotColor.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; spotColor.repeatCount = INFINITY; spotColor.duration = 3.0; [spotLight addAnimation:spotColor forKey:@"ChangeTheColorOfTheSpot"]; // Rotating the box CABasicAnimation *boxRotation = [CABasicAnimation animationWithKeyPath:@"transform"]; boxRotation.toValue = [NSValue valueWithCATransform3D:CATransform3DRotate(boxNode.transform, M_PI, 1, 1, 0)]; boxRotation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; boxRotation.repeatCount = INFINITY; boxRotation.duration = 2.0; [boxNode addAnimation:boxRotation forKey:@"RotateTheBox"];
And now the scene looks like this
Layers as content
The second part of the Core Animation integration is that layers can be used the content of materials. The most basic example of this would be to use a layer as the texture of a material. Apple uses this to great effect in the SceneKit session from WWDC 12 (504) when they animated photos inside of 3D photo frames (at the 40:53 minute mark and forward). More advanced visual effects could be achieved by using the layer as the content of for example the specular part of the material.
All the other cool stuff
This is just a scratch on the surface of the capabilities of SceneKit.
For example: fully configured scenes with geometry, materials, lights, cameras and animations that a designer has exported as a .DEA-file can be imported in a single line of code
sceneWithURL:options:error:. There is also support for hit testing in 3D (you see it in action when dragging items around in Delicious Library 3).
When necessary a material can be rendered using custom vertex shaders and fragment shaders (GLSL). If that isn’t powerful enough there are render delegates that allow both complete custom OpenGL rendering of single nodes and post-processing and pre-processing of the entire scene using callbacks like
If you haven’t already, I recommend that you take a look at what Jeff LaMache has already written about SceneKit and watch the WWDC video about SceneKit. The very simple sample code for this post can be found on GitHub.