So, I have this scene in SceneKit, where I programmatically create a bunch of objects with each height. Most of them are simple boxes, but some of them are pressed tracks.
I need to add a simple material color for them so that the color goes from a darker value to a lighter one from the bottom.
I tried to achieve this by creating material with the texture of a gradient image programmatically created using CAGradientLayer . It almost works. The problem with this approach is that ALL the faces of the object will get the same effect, which is not what I want. The upper and lower surfaces of objects should be solid colors - light color on the upper surface and light below.
I also played with shaders, but the result is a gradient that is relative to the screen instead of my object, thereby turning my 3D object into 2D.
Here I create a simple color :
func createBox(with bounds: CGRect, and height: CGFloat) { let material = SCNMaterial() material.diffuse.contents = UIColor.white if #available(iOS 10.0, *) { material.lightingModel = .physicallyBased material.metalness.contents = 0.2 material.roughness.contents = 0.5 } let geometry = SCNBox(width: bounds.size.width, height: height, length: bounds.size.height, chamferRadius: 0.0) geometry.materials = [material] geometry.chamferRadius = 0.02 let node = NKNode(geometry: geometry) }
Now, since the box has 6 different geometry elements by default, I can choose to create a material that has its contents configured for the gradient image, and then add this material 6 times to the materials geometry array. This allows me to set the material for each person individually, for example:
let material = SCNMaterial() material.diffuse.contents = UIImage(named: "gradient-test") geometry.materials = [material, material, material, material, material, material]
With this I can have 3 different materials (sides, top, bottom) and by trial and error add them in the correct order in the materials array. This will mean the following:
let sideMaterial = SCNMaterial() sideMaterial.diffuse.contents = UIImage(named: "gradient") let topMaterial = SCNMaterial() topMaterial.diffuse.contents = UIColor.white let bottomMaterial = SCNMaterial() bottomMaterial.diffuse.contents = UIColor.black
But my problem appears when my object is not an easy but extruded path. Here's how they are created:
func createFromPath(_ path: UIBezierPath, with height: CGFloat) { let material = SCNMaterial() material.diffuse.contents = UIColor.white if #available(iOS 10.0, *) { material.lightingModel = .physicallyBased material.metalness.contents = 0.2 material.roughness.contents = 0.5 } material.isDoubleSided = true let geometry = SCNShape(path: path, extrusionDepth: height) geometry.materials = [material] geometry.chamferRadius = 0.02 }
As you can see, a simple solid color is simple. But applying the same logic to add a gradient texture to the side no longer works, since my path may have an undefined number of faces. And, frankly, I even think that the geometry created in this way does not break into several "elements", like a simple box.
So any idea how I can achieve what I want?
Update
Here is a really basic example of what I would like to achieve. As you can see, the 3D βbuildingsβ on this map are thin from bottom to top gradient.
