2

For performance reasons I have to switch from SceneView to SpriteView in my macOS project (showing more than 63 scenes did not work with SceneView, but it does with SpriteView).

But now im facing an issue that SpriteView is rendering colors differently than SceneView. Below is a simple reproduction of the issue I am facing.

I have tried a multitude of material and lighting options, but I seem to miss something more fundamental. Help is very much appreciated.

enter image description here

    var body: some View {
        
            HStack {
                
                // SpriteView
                SpriteView(scene: { () -> SKScene in
                    
                    let scene = SKScene()
                    scene.backgroundColor = .white
                    scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
                    
                    let node = SK3DNode()
                    node.scnScene = self.sphereScene
                    scene.addChild(node)
                    
                    return scene
                }())

                // SceneView
                SceneView(scene: sphereScene,
                          options: [.autoenablesDefaultLighting])
            }
    }
    
    var sphereScene: SCNScene {
        
        let scnScene = SCNScene()
        
        let ballGeometry = SCNSphere(radius: 5)
        let ballNode = SCNNode(geometry: ballGeometry)
        
        let material = SCNMaterial()
        material.diffuse.contents = NSColor.purple
        material.lightingModel = .physicallyBased
        ballGeometry.materials = [material]
        
        scnScene.rootNode.addChildNode(ballNode)
        
        return scnScene
    }
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
ChrisR
  • 9,523
  • 1
  • 8
  • 26
  • Is the rootcause due to differences in colorspace? See: https://stackoverflow.com/questions/62956544/why-the-scenekit-material-looks-different-even-when-the-image-is-the-same – JWBR May 26 '22 at 18:48
  • @JWBR thanks for the link. This might very well be the case! In the meantime I got it to work with pure SceneView. But thanks anyway!! – ChrisR May 26 '22 at 19:38

2 Answers2

1

You're absolutely right: SpriteKit processes SceneKit's scenes differently than SceneKit. It's visually noticeable that the lighting intensity, blurring of highlights and decolorization of edges with 90 degree reflection are different. The main tool that can be advised in this case is the use of Ambient Light to additionally illuminate the SpriteKit scene based on the SceneKit content. You should turn a default lighting off (in order to get rid of colorization artifacts) and use regular lights. Here I used directional light.

enter image description here

SpriteView:

import SwiftUI
import SceneKit
import SpriteKit

struct SpriteView: NSViewRepresentable {
    
    var scene = SKScene()

    func makeNSView(context: Context) -> SKView {
        let skView = SKView(frame: .zero)
        skView.presentScene(scene)
        scene.backgroundColor = .black
        return skView
    }
    func updateNSView(_ uiView: SKView, context: Context) { }
}

ContentView:

struct ContentView: View {
    var body: some View {
        ZStack {
            HStack {
                SpriteView(scene: { () -> SKScene in
                       
                    let scene = SKScene()
                    scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)

                    let ambient = SCNNode()
                    ambient.light = SCNLight()
                    ambient.light?.type = .ambient
                    ambient.light?.intensity = 1200
                   
                    let node = SK3DNode()
                    node.autoenablesDefaultLighting = false
                    node.scnScene = self.sphereScene
                    node.scnScene?.rootNode.addChildNode(ambient)
                    scene.addChild(node)
                    return scene
                }() )

                SceneView(scene: sphereScene, options: [])
            }
        }
    }
   
   var sphereScene: SCNScene {
       
       let scnScene = SCNScene()
       scnScene.background.contents = NSColor.black
       let ballNode = SCNNode(geometry: SCNSphere(radius: 5.0))
       
       let directional = SCNNode()
       directional.light = SCNLight()
       directional.light?.type = .directional
       directional.light?.intensity = 500
       scnScene.rootNode.addChildNode(directional)
       
       let material = SCNMaterial()
       material.lightingModel = .physicallyBased
       material.diffuse.contents = NSColor.purple
       ballNode.geometry?.materials = [material]
       scnScene.rootNode.addChildNode(ballNode)
       return scnScene
   }
}
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • 1
    thank you for looking into this. I already tried to "rebuild" the effect with multiple custom lights, but I didn't get near the SceneView appearance. My problem is that I have 100+ scenes optimized for look in SceneView :( ... you might not happen to have a workaround for my initial problem of not being able to show more than 63 different SceneViews in parallel? – ChrisR Apr 02 '22 at 11:09
  • 1. In SpriteKit, it's hard to get close to the quality of SceneKit, it's true. 2. I have never simultaneously processed so many scenes. – Andy Jazz Apr 02 '22 at 11:14
0

The following worked for me, correcting saturation and brightness brought me near to the SceneKit defaultLighting appearance:

    // get object and manipulate
    let object = scene.rootNode.childNode(withName: "object", recursively: false)
    let color = NSColor(named: "\(colorNr)")?
        .usingColorSpace(.displayP3) // specify color space, important!

    object?.geometry?.firstMaterial?.lightingModel = .physicallyBased

    // correct color for SpriteView
    let color2 = NSColor(hue: color?.hueComponent ?? 0,
                         saturation: (color?.saturationComponent ?? 0) * 0.55,
                         brightness: (color?.brightnessComponent ?? 0) * 0.55 + 0.45,
                         alpha: 1.0)
        
    object?.geometry?.firstMaterial?.diffuse.contents = color2
    object?.geometry?.firstMaterial?.diffuse.intensity = 0.9
    object?.geometry?.firstMaterial?.roughness.contents = 0.9
ChrisR
  • 9,523
  • 1
  • 8
  • 26