Solution I
Banding artifacts on gradients is quite common issue in computer graphics. To eliminate banding you usually need to use blur. Here's a code that helps you do it for SceneKit diffuse material:
import SceneKit
class ViewController: UIViewController {
@IBOutlet var sceneView: SCNView!
let ciContext = CIContext()
fileprivate func gaussianBlur() -> UIImage? {
let uiImage = UIImage(named: "art.scnassets/banding.png")!
let ciImage = CIImage(image: uiImage)
guard let ciBlurFilter = CIFilter(name: "CIGaussianBlur")
else { return nil }
ciBlurFilter.setValue(ciImage, forKey: "inputImage")
let resultedImage = ciBlurFilter.value(forKey: "outputImage") as! CIImage
var blurredImage = UIImage(ciImage: resultedImage)
let cgImage = ciContext.createCGImage(resultedImage,
from: resultedImage.extent)
blurredImage = cgImage.flatMap { UIImage(cgImage: $0) }!
return blurredImage
}
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = SCNScene()
let sphereNode = SCNNode(geometry: SCNSphere(radius: 0.2))
sphereNode.geometry?.firstMaterial?.diffuse.contents = gaussianBlur()
sceneView.scene?.rootNode.addChildNode(sphereNode)
}
}
Solution II
Banding artifacts isn't possible in generated source as 16-bit and 32-bit images (for instance .psd
, .hdr
, .tiff
or .exr
file formats). Regular .png
or .jpg
are 8-bit per channel.
Increasing a size of 8-bit image doesn't bring a positive result. That's because you still have 256 grey half-tones per channel. But if you use 16-bit .tiff
you get 65536 steps of grey color per channel. It's 256 times more than in an 8-bit image.
However, let's see what Apple documentation says about it.
Although image objects support all platform-native image formats, it is recommended that you use PNG
or JPEG
files for most images in your app. Image objects are optimized for reading and displaying both formats, and those formats offer better performance than most other image formats. Because the PNG format is lossless, it is especially recommended for the images you use in your app’s interface.
So Apple tries to say us that using 16-bit and 32-bit files is possible but it smells like non-optimized
way of development. In case you're planning to render too many 32-bit textures in SCNScene – be ready to get a freezed (unresponsive) view.
I personally tried using .hdr
, .tiff
and .exr
file formats and it looks OK about them. Not 100% sure, but I think you could exploit 16-bit and 32-bit .psd
files, however I suppose they must be flattened (to be a-single-layer) before importing them into Xcode project.
Solution III
You can build a CIFilter's CISmoothLinearGradient programmatically. This filter has four parameters:
- inputPoint0 (CIVector)
- inputPoint1 (CIVector)
- inputColor0 (CIColor)
- inputColor1 (CIColor)