One of the features I'm considering adding to Nodality, my node based image editor for iPads, is a function to create materials for SceneKit geometries. My first step is to create a user interface control for Nodality nodes that can preview the user generated material. I've worked on this component in a little test harness project that I thought I'd share.
My MaterialPreviewWidget is a component that initialises itself with a default SCNMaterial, accepts a new material and renders a sphere using that material. My view controller adds an instance of MaterialPreviewWidget to its view, along with sliders to control the fresnel exponent, shininess and transparency and segmented controls to select normal, specular, diffuse and reflective maps.
Creating the sphere preview using SceneKit is pretty simple. MaterialPreviewWidget extends the SCNView class and in its didMoveToSuperview() method I start building the scene. First of all, I define an SCNSceneset that at the scene view's scene:
...next I create a camera, define its position and field of view and add it as a child node to the scene:
...at the top of the class, I create a constant named sphere that's an instance of SCNSphere and defines the sphere's geometry. I now need to create an SCNNode to act as the sphere's parent and manage its position in space. I also apply the default material to the geometry:
...next I create an ambient and an omni light. As with the sphere geometry, these need an SCNNode to handle their positions in space:
Finally, I need a SCNFloor instance which is a reflective infinite plane. Again, this needs an SCNNode to manage its position:
Back up in the view controller, when any of the sliders or segmented controls dispatch a UIControlEvents.ValueChanged control event, I simply update the widget's material's properties:
...and the sphere updates with the updated material.
One thing to note: when I've worked with bump maps in the past, they were always monochrome images where the brightness affected the height. Normal maps are slightly different - they use the red, green and blue values of an image to change the normal in the x, y and z directions. That's why to affect just the 'height' of a bump, my bump map is blue only:
All the source code for this project is available at my GitHub repository here.
Thanks to Swift Development with Cocoa by O'Reilly for guiding me through this!
Finally, Nodality version 2.3 is beginning to take shape and here's a screen shot of the material preview widget inside a node and accepting inputs:
My MaterialPreviewWidget is a component that initialises itself with a default SCNMaterial, accepts a new material and renders a sphere using that material. My view controller adds an instance of MaterialPreviewWidget to its view, along with sliders to control the fresnel exponent, shininess and transparency and segmented controls to select normal, specular, diffuse and reflective maps.
Creating the sphere preview using SceneKit is pretty simple. MaterialPreviewWidget extends the SCNView class and in its didMoveToSuperview() method I start building the scene. First of all, I define an SCNSceneset that at the scene view's scene:
let thisScene = SCNScene()
scene = thisScene
...next I create a camera, define its position and field of view and add it as a child node to the scene:
let camera = SCNCamera()
camera.xFov = 45
camera.yFov = 45
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 20)
thisScene.rootNode.addChildNode(cameraNode)
...at the top of the class, I create a constant named sphere that's an instance of SCNSphere and defines the sphere's geometry. I now need to create an SCNNode to act as the sphere's parent and manage its position in space. I also apply the default material to the geometry:
let sphereNode = SCNNode(geometry: sphere)
sphereNode.position = SCNVector3(x: 0, y: 0, z: 0)
thisScene.rootNode.addChildNode(sphereNode)
sphere.materials = [material]
...next I create an ambient and an omni light. As with the sphere geometry, these need an SCNNode to handle their positions in space:
let ambientLight = SCNLight()
ambientLight.type = SCNLightTypeAmbient
ambientLight.color = UIColor(white: 0.25, alpha: 1.0)
let ambientLightNode = SCNNode()
ambientLightNode.light = ambientLight
thisScene.rootNode.addChildNode(ambientLightNode)
let omniLight = SCNLight()
omniLight.type = SCNLightTypeOmni
omniLight.color = UIColor(white: 1.0, alpha: 1.0)
let omniLightNode = SCNNode()
omniLightNode.light = omniLight
omniLightNode.position = SCNVector3(x: -5, y: 8, z: 10)
thisScene.rootNode.addChildNode(omniLightNode)
Finally, I need a SCNFloor instance which is a reflective infinite plane. Again, this needs an SCNNode to manage its position:
let floor = SCNFloor()
let floorNode = SCNNode(geometry: floor)
floorNode.position = SCNVector3(x: 0, y: -6.2, z: 0)
thisScene.rootNode.addChildNode(floorNode)
Back up in the view controller, when any of the sliders or segmented controls dispatch a UIControlEvents.ValueChanged control event, I simply update the widget's material's properties:
func propertySliderChange()
{
materialPreviewWidget.material.fresnelExponent = fresnelExponentSlider.value
materialPreviewWidget.material.shininess = shininessSlider.value
materialPreviewWidget.material.transparency = transparencySlider.value
materialPreviewWidget.material.specular.contents = specularSegmentedControl.value
materialPreviewWidget.material.diffuse.contents = diffuseSegmentedControl.value
materialPreviewWidget.material.reflective.contents = reflectiveSegmentedControl.value
materialPreviewWidget.material.normal.contents = normalSegmentedControl.value
}
...and the sphere updates with the updated material.
One thing to note: when I've worked with bump maps in the past, they were always monochrome images where the brightness affected the height. Normal maps are slightly different - they use the red, green and blue values of an image to change the normal in the x, y and z directions. That's why to affect just the 'height' of a bump, my bump map is blue only:
All the source code for this project is available at my GitHub repository here.
Thanks to Swift Development with Cocoa by O'Reilly for guiding me through this!
Finally, Nodality version 2.3 is beginning to take shape and here's a screen shot of the material preview widget inside a node and accepting inputs: