iosscenekitskviewscnview

SK3DNode shows SCNScene darker than in SCNView


Before I am going mad I'd like to ask here about the following issue. I wrote an iOS test app in SwiftUI that shows a SpriteView and a SceneView. The SpriteView shows (among other things) two SK3DNode nodes that display one and the same SCNScene from different camera positions. The SceneView renders the very same SCNScene.

The problem is that the rendered scene in both SK3DNode's are shown darker than in SceneView.

The scenes in SpriteKit are unusable because they are too dark.

This is a screenshot from the iPad simulator:

Screenshot in simulator

I expected both SK3DNode's (at the top half) to render the scene at the same brightness as the SceneView (bottom half).

If I simply increase the ambient light, objects are shown way too bright. I thought the problem is the texture or the material, but the problem also occurs with colours. Hence the red/green/blue shapes. I have disabled SK3DNode's built-in lighting by setting autoenablesDefaultLighting to false. Somehow SceneView (or its internal SCNView) must add its own lighting but I cannot figure how. The scene's rootNode only shows my nodes from the .scn file.

I've disabled the environment in the scene file so there is no sky cube. There is one ambient light source, one spot light, and one omni light. No matter which lights I am using, both SK3DNode's still present the scene much darker than the SceneView.

Here is the function that creates the two SK3DNodes:

func make3DNode(camPos: SCNVector3) -> SK3DNode {
    // Add SceneKit scene in 3D node
    let node3d = SK3DNode(viewportSize: CGSize(width: 300, height: 200))
    node3d.scnScene = scnScene
    node3d.autoenablesDefaultLighting = false
    
    let camera = SCNCamera()
    let cameraNode = SCNNode()
    cameraNode.camera = camera
    if let lookAtTarget = scnScene.rootNode.childNode(withName: "red_texture", recursively: false) {
        let constraint = SCNLookAtConstraint(target: lookAtTarget)
        // why does this not work?
        cameraNode.constraints = [constraint]
    }
    cameraNode.position = camPos
    node3d.pointOfView = cameraNode

    return node3d
}

On a side note: the camera lookAt constraint does not seem to work either.

Code to add the nodes to the scene:

    // Add SceneKit nodes
    let node3d_1 = make3DNode(camPos: SCNVector3(x: -5, y: -2, z: 14))
    node3d_1.name = "3d_1"
    node3d_1.position = CGPoint(x: 150, y: 110)
    spriteScene.addChild(node3d_1)

    let node3d_2 = make3DNode(camPos: SCNVector3(x: 5, y: 2, z: 14))
    node3d_2.name = "3d_2"
    node3d_2.position = CGPoint(x: 150, y: -110)
    spriteScene.addChild(node3d_2)

Full project source: https://github.com/biochill/test-spritekit

It includes both the SpriteKit scene file and the SceneKit file. The SK3DNodes are added programmatically. I can't paste code for the scenes because I have created scene files both for SpriteKit and SceneKit. Best if you download the GitHub project and hit Cmd+R in Xcode for some iPad simulator.

Thanks for any help in advance.


Solution

  • Here is what I did, to make both scenes (upper SpriteKit embedded and lower part SceneKit of the screen) to look the same:

    Enabled: autoenablesDefaultLighting (you could also add lighting manually and then set autoenablesDefaultLighting to false)

    in file: ContentView.swift

    func make3DNode(camPos: SCNVector3) -> SK3DNode {
            // Add SceneKit scene in 3D node
            let node3d = SK3DNode(viewportSize: CGSize(width: 300, height: 200))
            
            node3d.scnScene = scnScene
            node3d.autoenablesDefaultLighting = true // <-- set to true
    

    I also re-enabled the code-line: MySceneView(scene: scnScene)...

    in file: MySceneView.swift

    struct MySceneView: UIViewRepresentable {
        var scene: SCNScene
    //  var visibleSize: CGSize
        
        func makeUIView(context: Context) -> SCNView {
            //      SLog.print("DiceView.makeUIView size=\(visibleSize)")
            
            let scnView = context.coordinator
            scnView.scene = scene
            scnView.delegate = scnView
            scnView.autoenablesDefaultLighting = true // <-- set to true
    

    The most important part comes here, which effectively fixed the issue in my testings.

    You need to add a valid info.plist file to the project.

    Add this content: SCNDisableLinearSpaceRendering = YES/true

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>SCNDisableLinearSpaceRendering</key>
        <true/>
    </dict>
    </plist>
    

    SCNDisableLinearSpaceRendering set to YES

    Note: you will not see this setting listed here:

    Setting not visible on the Info tab

    And this is how it looks like on my iPad mini:

    Both scenes look the same

    Further adjustments: As already mentioned, you could manage the Lighting manually.

    I hope this resolves the issue with the dark scene.