Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot Display Sprites Above SK3DNode

I'm displaying some basic 3D geometry within my SpriteKit scene using an instance of SK3DNode to display the contents of a SceneKit scene, as explained in Apple's article here.

I have been able to position the node and 3D contents as I want using SceneKit node transforms and the position/viewport size of the SK3DNode.

Next, I want to display some other sprites in my SpriteKit scene overlaid on top of the 3D content, but I am unable to do so: The contents of the SK3DNode are always drawn on top.

I have tried specifying the zPosition property of both the SK3DNode and the SKSpriteNode, to no avail.

From Apple's documentation on SK3DNode:

Use SK3DNode objects to incorporate 3D SceneKit content into a SpriteKit-based game. When SpriteKit renders the node, the SceneKit scene is animated and rendered first. Then this rendered image is composited into the SpriteKit scene. Use the scnScene property to specify the SceneKit scene to be rendered.

(emphasis mine)

It is a bit ambiguous withv regard to z-order (it only seems to mention the temporal order in which rendering takes place).

I have put together a minimal demo project on GitHub; the relevant code is:

1. SceneKit Scene

import SceneKit
class SceneKitScene: SCNScene {

    override init() {
        super.init()

        let box = SCNBox(width: 10, height: 10, length: 10, chamferRadius: 0)
        let material = SCNMaterial()
        material.diffuse.contents = UIColor.green
        box.materials = [material]

        let boxNode = SCNNode(geometry: box)
        boxNode.transform = SCNMatrix4MakeRotation(.pi/2, 1, 1, 1)

        self.rootNode.addChildNode(boxNode)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

2. SpriteKit Scene

import SpriteKit
class SpriteKitScene: SKScene {

    override init(size: CGSize) {
        super.init(size: size)

        // Scene Background
        self.backgroundColor = .red

        // 3D Node
        let objectNode = SK3DNode(viewportSize: size)
        objectNode.scnScene = SceneKitScene()
        addChild(objectNode)
        objectNode.position = CGPoint(x: size.width/2, y: size.height/2)

        let camera = SCNCamera()
        let cameraNode = SCNNode()
        cameraNode.camera = camera
        objectNode.pointOfView = cameraNode
        objectNode.pointOfView?.position = SCNVector3(x: 0, y: 0, z: 60)
        objectNode.zPosition = -100

        // 2D Sprite
        let sprite = SKSpriteNode(color: .yellow, size: CGSize(width: 250, height: 60))
        sprite.position = objectNode.position
        sprite.zPosition = +100
        addChild(sprite)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

...And the rendered result is:

enter image description here

(I want the yellow rectangle above the green box)

like image 342
Nicolas Miari Avatar asked Oct 23 '25 19:10

Nicolas Miari


1 Answers

I made a Technical Support Incident with Apple about this and they just got back to me. The solution is actually very very simple.

If you want 2D sprites to render on top of SK3DNodes, you need to stop the contents of the SK3DNodes from writing to the depth buffer.


To do this, you just need to set writesToDepthBuffer to false on the SCNMaterial.

...

let material = SCNMaterial()
material.diffuse.contents = UIColor.green
material.writesToDepthBuffer = false

...

Boom. Works.

like image 106
cabogie Avatar answered Oct 26 '25 08:10

cabogie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!