Home > Back-end >  Same color rendered differently in SpriteView vs. SceneView
Same color rendered differently in SpriteView vs. SceneView

Time:04-05

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
    }

CodePudding user response:

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 only 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
   }
}

CodePudding user response:

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
  • Related