Custom Metal Drawing in SceneKit
SceneKit is a powerful framework with many useful features. I’ve been using it on a daily basis for the past six months and I’ve grown to love it. However, one thing that Apple hasn’t done well is providing us with extensive documentation. Using SceneKit means digging through GitHub, StackOverflow and the very few meaningful results returned by Google.
One of the things that is hard to figure out is how to perform custom Metal drawing after SceneKit has rendered a scene. Apple’s documentation reveals the following:
… to perform custom Metal or OpenGL rendering before or after SceneKit renders the scene, specify your own custom object that implements the
SCNSceneRendererDelegate
protocol for the view’sdelegate
property.
Two methods in SCNSceneRendererDelegate
are of interest:
func renderer(SCNSceneRenderer, willRenderScene: SCNScene, atTime: TimeInterval)
Tells the delegate that the renderer has cleared the viewport and is about to render the scene.
and
func renderer(SCNSceneRenderer, didRenderScene: SCNScene, atTime: TimeInterval)
Tells the delegate that the renderer has rendered the scene.
Apple’s documentation has one last bit of useful information:
To render using Metal, use the
renderer
parameter to retrieve the scene renderer’scurrentRenderCommandEncoder
object and encode your own drawing commands. If you need to reference other Metal state, see the properties listed inSCNSceneRenderer
.
That’s where the documentation ends so it is up to us to figure out the rest.
Let’s Draw A Triangle
There are some really good SceneKit and Metal tutorials out there so for the sake of brevity I won’t go into the basics.
First you’ll need to setup a new project in Xcode using the “Game” template for iOS or macOS. This will generate a new SceneKit project with a rotating airplane.
Open the GameViewController.swift file and make sure it conforms to the SCNSceneRenderer
protocol:
Next, assign the delegate to SCNView
inviewDidLoad():
Now we are going to set up the resources we need for Metal. First, add two instance variables:
Time to add the function that takes care of setting up the Metal resources we’ll need:
Our new function should be called in viewDidAppear():
When creating the MTLRenderPipelineDescriptor
we referenced two shader functions called passthrough_vertex
and passthrough_fragment
. These functions need to be defined in a Metal shader file.
Create a new file called Shaders.metal
and paste the following:
Now the only thing left to do is implement SCNSceneRenderer
‘s renderer(didRenderScene:)
method:
Run the project and behold the colorful triangle:
This is a very basic example but hopefully it’ll allow you to create your own, more exciting, 3D content.
It took me a while to figure this out so I hope it’ll benefit anyone looking to add custom Metal drawing to SceneKit. Let me know if it did.