4

The code below generates a red box in a SCNView. However, the edges are jagged along the top and bottom of the side/element facing you (as illustrated by the attachment). The goal is to render smoother edges similar to Minecraft boxes. Changing the camera position reduces the pixelation of certain edges, so is this a camera angle issue? If yes, is it possible to render boxes with smooth edges no matter the camera angle?

For instance setting the camera position to SCNVector3(x: 0.0, y: 0.0, z: 10.0) renders the box effectively in 2D and with crisp edges (second attachment).

enter image description here enter image description here

    let sceneView = SCNView(frame: self.view.frame)
    self.view.addSubview(sceneView)

    let scene = SCNScene()
    sceneView.scene = scene

    let camera = SCNCamera()
    let cameraNode = SCNNode()
    cameraNode.camera = camera
    cameraNode.position = SCNVector3(x: -3.0, y: 0.0, z: 10.0)

    let ambientLight = SCNLight()
    ambientLight.type = SCNLightTypeAmbient
    ambientLight.color = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1.0)
    cameraNode.light = ambientLight

    let light = SCNLight()
    light.type = SCNLightTypeOmni
    let lightNode = SCNNode()
    lightNode.light = light
    lightNode.position = SCNVector3(x: 0, y: 20, z: 10)

    let cubeGeometry = SCNBox(width: 3.0, height: 3.0, length: 3.0, chamferRadius: 0.0)
    let cubeNode = SCNNode(geometry: cubeGeometry)

    let planeGeometry = SCNPlane(width: 100.0, height: 100.0)
    let planeNode = SCNNode(geometry: planeGeometry)
    planeNode.eulerAngles = SCNVector3(x: GLKMathDegreesToRadians(-90), y: 0, z: 0)
    planeNode.position = SCNVector3(x: 0, y: -0.5, z: 0)

    let redMaterial = SCNMaterial()
    redMaterial.diffuse.contents = UIColor.redColor()
    cubeGeometry.materials = [redMaterial]

    let greenMaterial = SCNMaterial()
    greenMaterial.diffuse.contents = UIColor.greenColor()
    planeGeometry.materials = [greenMaterial]

    let constraint = SCNLookAtConstraint(target: cubeNode)
    constraint.gimbalLockEnabled = true
    cameraNode.constraints = [constraint]
    lightNode.constraints = [constraint]

    scene.rootNode.addChildNode(lightNode)
    scene.rootNode.addChildNode(cameraNode)
    scene.rootNode.addChildNode(cubeNode)
    scene.rootNode.addChildNode(planeNode)
Crashalot
  • 33,605
  • 61
  • 269
  • 439
  • Have you tried `antialiasingMode`? – jtbandes Jan 07 '16 at 01:40
  • @jtbandes Thanks, using `.Multisampling4X` smoothed the edges a bit, but they're still noticeably blurry and not smooth. Other suggestions? – Crashalot Jan 07 '16 at 01:49
  • Appears to be iOS specific. I ran this code in both iOS and OS X playgrounds. OS X version has no jaggies, even without enabling multisampling. What does it look like on real iOS hardware? – Hal Mueller Jan 07 '16 at 22:15
  • Pretty much the same as what you see in the screenshot ... any suggestions @HalMueller? – Crashalot Jan 07 '16 at 22:29
  • I ran it on a few different devices. Adding `.Multisampling4X` improved things a lot. I think the OpenGLES2 renderer does a prettier job than Metal, though, and that warrants filing a Radar. To switch renderers, you can use `SCNView(frame: self.view.frame, options: [SCNPreferredRenderingAPIKey: SCNRenderingAPI.OpenGLES2.rawValue])`, or edit your Info.plist. – Hal Mueller Jan 08 '16 at 03:23
  • Metal also blends differently in SCNKit. In both cases (anti-aliasing and blending) it seems Metal is doing "cheaper" operations than the equivalent settings in OpenGL ES. In the video, the rings on the left are rendered with OpenGL ES, and Metal on the right, captured from device: http://stackoverflow.com/questions/32343199/most-performant-image-format-for-scnparticles – Confused Jan 08 '16 at 12:15

1 Answers1

6

As mentioned in the comments, if you want to antialias every frame set the antialiasingMode. It doesn’t do as nice a job as CoreGraphics but it’s reasonably nice. However, it does increase the workload a ton, so you’re going to burn batteries and slow down your framerate.

On OS X the default is to 4x multisample every frame. On iOS the default is no multisampling, since it’s so expensive.

Luckily there’s a solution I much prefer, which is to turn on jittering. It’s kind of like multisampling but lazy. When objects are moving they are rendered normally (with jaggies), but when they stop moving the renderer runs like 96 more frames in the background and smooths out all the jaggies.

The final look is much better than antialiasing (because there’s no “96x antialiasing mode”) and on fast hardware most people I’ve shown it to don’t realize that the quality is changing when objects move.

Wil Shipley
  • 9,343
  • 35
  • 59
  • Whoa! That is awesome, Wil. Thanks so much. For others, the code is `sceneView.jitteringEnabled = true` if `sceneView` is the name of your `SCNView`. The jaggies remain for a brief moment, but as Wil said, they mostly vanish after a second or two. – Crashalot Jan 08 '16 at 19:48