Scene Kit Tutorial: Getting Started

Learn how to easily create 3D scenes in your iOS apps or games in this Scene Kit tutorial! By Ricardo Rendon Cepeda.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Lights, Camera, Action!

The built-in SCNView features are very useful, but also rather limited. In order to fully customize and control your scene, you must learn how to create and add multiple nodes to your scene graph, of various different types.

As film directors would say, “Lights, camera, action!” :]

Add the following lines to sceneSetup, just below let scene = SCNScene():

let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = SCNLightTypeAmbient
ambientLightNode.light!.color = UIColor(white: 0.67, alpha: 1.0)
scene.rootNode.addChildNode(ambientLightNode)

Here, you’re creating a new ambient light node with a 67% white color, then adding it to your scene.

Ambient light is a basic form of lighting that illuminates all objects in a scene evenly, with a constant intensity from all directions, much like cloud-filtered sunlight. It’s a great addition to most scenes when you want a more natural look. Otherwise, objects in shadow would be pure black and that’s not the desired effect in most cases – especially when you build a model-viewer type of app, like the one in this tutorial.

Although ambient light is part of the lighting equation, it’s not very useful on its own because it does little to illuminate surface details. So, add one more light to your scene with the following lines, just after your previous addition:

let omniLightNode = SCNNode()
omniLightNode.light = SCNLight()
omniLightNode.light!.type = SCNLightTypeOmni
omniLightNode.light!.color = UIColor(white: 0.75, alpha: 1.0)
omniLightNode.position = SCNVector3Make(0, 50, 50)
scene.rootNode.addChildNode(omniLightNode)

This new type of light is called an omnidirectional light, or point light. It’s similar to ambient light in that it has constant intensity, but differs because it has a direction. The light’s position relative to other objects in your scene determines its direction. Your box node’s default position is (0, 0, 0), so positioning your new light node at (0, 50, 50) means that it’s above and in front of your box.

Note: Don’t worry too much about units at this point. All units are relative to the scene and defined by your own set of rules. A position of (0, 50, 50) doesn’t mean 50 meters, miles, or light years; it’s simply 50 units. You’ll define a proper unit of measurement shortly.

Note: Don’t worry too much about units at this point. All units are relative to the scene and defined by your own set of rules. A position of (0, 50, 50) doesn’t mean 50 meters, miles, or light years; it’s simply 50 units. You’ll define a proper unit of measurement shortly.

Now that you’ve set up some lights, delete the following line from sceneSetup:

sceneView.autoenablesDefaultLighting = true

Build and run! Pinch to zoom out, and you’ll find your box properly illuminated:

BuildRun04

Behold your amazing and talented box! For this tutorial, these two sources will do just fine, but be sure to experiment with directional (SCNLightTypeDirectional) and spot (SCNLightTypeSpot) lights in future projects.

Next, set up a camera by adding the following lines just below where you setup the lights in sceneSetup:

let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3Make(0, 0, 25)
scene.rootNode.addChildNode(cameraNode)

Just like the light nodes, a camera node is just as easy – if not easier – to set up. The default projection settings are spot-on for a basic scene, so you don’t have to modify the camera’s field of view, focal range or other properties. The only thing you need to define is its position, conveniently set right in front of your box at (0, 0, 25).

Build and run to “get behind the lens.”

BuildRun05

Note: The default projection type of a SCNCamera is perspective, but you can easily change this to orthographic projection by enabling usesOrthographicProjection and setting an appropriate orthographicScale value.

Note: The default projection type of a SCNCamera is perspective, but you can easily change this to orthographic projection by enabling usesOrthographicProjection and setting an appropriate orthographicScale value.

Now, if you navigate your scene with the default camera controls (rotation, specifically), you’ll notice an odd effect: you might expect the box to rotate and the lighting to stay in place but, in fact, it’s actually the camera rotating around the scene. That’s why you see the box from different points of view.

The default camera controls are very powerful, but not customizable. In order to gain total control, you’ll create your own. First, add the following variables to ViewController.swift, just above viewDidLoad:

// Geometry
var geometryNode: SCNNode = SCNNode()
  
// Gestures
var currentAngle: Float = 0.0

Dealing with a single box is simple enough, but once you have more complex models with multiple geometry sources, it’ll be much easier to manage them as a single node – hence the addition of geometryNode. Furthermore, currentAngle will help modify the y-axis rotation of geometryNode exclusively, leaving the rest of your scene nodes untouched.

Next up, add the following function below sceneSetup:

func panGesture(sender: UIPanGestureRecognizer) {
  let translation = sender.translationInView(sender.view!)
  var newAngle = (Float)(translation.x)*(Float)(M_PI)/180.0
  newAngle += currentAngle
      
  geometryNode.transform = SCNMatrix4MakeRotation(newAngle, 0, 1, 0)
    
  if(sender.state == UIGestureRecognizerState.Ended) {
    currentAngle = newAngle
  }
}

Whenever sceneView detects a pan gesture, this function will be called, and it transforms the gesture’s x-axis translation to a y-axis rotation on the geometry node (1 pixel = 1 degree).

In this implementation, you modify the transform property of geometryNode by creating a new rotation matrix, but you could also modify its rotation property with a rotation vector. A transformation matrix is better because you can easily expand it to include translation and scale.

Next, add the following lines to sceneSetup, just above sceneView.scene = scene:

geometryNode = boxNode
    
let panRecognizer = UIPanGestureRecognizer(target: self, action: "panGesture:")
sceneView.addGestureRecognizer(panRecognizer)

These lines finalize the setup by connecting your new functions and variables. Finally, remove the default camera controls by deleting the following line:

sceneView.allowsCameraControl = true

Build, run, action! Now try rotating your cube with a one-finger pan.

BuildRun06

Much better :]

Note: A full 3D transformation engine with gestures is far too complex for this tutorial, but definitely worth a look. Luckily, for you, it’s something we’ve covered on our site before! Check out OpenGL ES Transformations with Gestures for the implementation details.

Note: A full 3D transformation engine with gestures is far too complex for this tutorial, but definitely worth a look. Luckily, for you, it’s something we’ve covered on our site before! Check out OpenGL ES Transformations with Gestures for the implementation details.

Ricardo Rendon Cepeda

Contributors

Ricardo Rendon Cepeda

Author

Over 300 content creators. Join our team.