I'd like to use a Metal
shader to apply a toon/cell shading to materials used in the scene. The shader I'm trying to implement is Apple's own AAPLCelShader
found in the MetalShaderShowcase. I'm a little new to implementing shaders in SceneKit
so I could be doing everything completely wrong.
From what I understand in the documentation a Metal
or GL shader can be used to modify SceneKit
rendering as of iOS 9 and Xcode 7. The documentation suggests that this is done in the same way as GL shaders.
My method is to try and get the path for the .metal
file and then load it into a string to be used in the SCNMaterial
shaderModifiers
property in the form of a [String: String] dictionary, as you would with a GL shader (though I've yet to get that working properly).
My code is:
var shaders = [String: String]()
let path = NSBundle.mainBundle().pathForResource("AAPLCelShader", ofType: "metal")!
do{
let celShader = try String(contentsOfFile: path, encoding: NSUTF8StringEncoding)
shaders[SCNShaderModifierEntryPointLightingModel] = celShader
}catch let error as NSError{
error.description
}
scene.rootNode.enumerateChildNodesUsingBlock({ (node, stop) -> Void in
node.geometry?.firstMaterial?.shaderModifiers = shaders
})
This is being run on a test scene with a single box geometry in it that is being loaded from a .scn
file. The file loading works fine so that's not an issue.
The error comes from the second line where I try and find the path, it gives me the "found nil while unwrapping optional value" which would be expected with the force unwrapping, other than the fact that the file it is trying to load is most certainly in the bundle and the same code works fine with other file types, just not .metal
ones.
Am I going about this completely the wrong way? I can't understand why it won't access the file other than an issue with me misusing the metal file.
Is there a simpler or better way to implement cell shading and eventually a bold outline on edges?
AAPLCelShader.metal is a complete vertex/rastex/fragment implementation, which is not what shaderModifiers are: source code that will be injected into already-complete shaders.
Instead, you can create an SCNProgram, using the vertex and fragment functions in AAPLCelShader.metal. What's nice about Metal, versus GLSL, is that you can use names of Metal functions, instead of source code strings, which is easier to work with, and results in having the functions compiled before runtime, which GLSL doesn't support. (This is still not as good as it should be, where Swift would recognize the Metal functions as correctly-typed, refactorable, namespaced closures.) Of course, while GLSL SCNPrograms will be converted to Metal, for compatible hardware, Metal SCNPrograms will not be translated for obsolete devices.
As for mapping from SceneKit to the rastex's (incorrectly-named ColorInOut) members:
…I unfortunately do not have an answer for you yet.