In the spirit of #showyourwork, and learning in the open, I’ve decided to document my journey as I teach myself about SceneKit on iOS and OS X, and modern 3D APIs.
My first foray into 3D was sometime in the early 1980s, when I played with drawing a 3D model of the earth. My inspiration was most likely Robert Tinney’s cover for the May 1979 issue of BYTE magazine:
Unfortunately insipation didn’t equal reality on my home-brewed TRS-80 clone. Its 128×48 graphics mode was a bit limiting, and state-of-the-art 3D on that platform looked like subLogic Flight Simulator:
In the mid 1990s I was experimenting with projecting from 3D to 2D, then drawing directly to Windows’ GDI API. Later, I played around with Microsoft’s WinG API, then Direct3D.
By about 2000, my insipration was the movie Toy Story, and the game Train Simulator (fuelled by my then two-year-old son’s train obsession). I still have a bunch of books with titles like OpenGL Programming Guide (the “red book”), Advanced RenderMan, Real-Time Rendering, and Advanced Animation and Rendering Techniques.
At the time I played around with modelling the front of a locomotive, in code, and rendering it with OpenGL.
Today’s inspiration are games like The Witness and Monument Valley:
I’ll be doing all of these steps in a simple single-view application. The scene is presented in an SCNView that completly covers the app’s main view. The SCNView from the storyboard is accessed through an @IBOutlet called sceneView. The complete listing for the view controller can be found at the bottom of this post.
An empty scene
The first step is to create an empty scene. Note that I set the scence view’s background color to black, for that super-spooky look (and to make sure that the simple lighting looks realistic). I also get a reference to the scene’s root node, as a convenience for later:
Next up we need something to look at. In this first test, it’s going to be a 1 × 1 × 1 cube with rounded edges. SceneKit provides the SCNBox class which creates some geometry in the shape of a rounded box. Add one of those to a node, and add that node to the root node of the scene, and we have our boxy actor:
At this point the scene will render, but with a default light and camera. The position of the default camera means that the cube will fill the entire viewport. To fix that we need our own camera.
Here I’ve setup a camera with a field-of-view of 60º in the x-axis (xFov), a constraint that keeps the camera looking at the box, and with gimbal-lock enabled to keep the horizon level if the camera moves.
With the camera moved into place, here’s what we can see (remember that we’re still using the default light source):
So, things don’t look great with the default lighting. Let’s replace the defaults with our own light:
I’ve placed a light pink (rose gold!) omnidirectional light to the right, above, and in-front of the box. I’ve also used attenuationStartDistance and attenuationEndDistance to make sure that the brightness falls off with distance from the light. Here’s what it looks like now:
The cube is starting to look better, but some better materials and texture could definitely spruce things up. Next time I’ll look at adding some of those.