SceneKit shadow on a transparent SCNFloor()

前端 未结 2 1979
独厮守ぢ
独厮守ぢ 2021-01-05 10:51

I have a floor node, on which I need to cast shadow from directional light. This node needs to be transparent (used in AR environment)

2条回答
  •  时光取名叫无心
    2021-01-05 10:59

    There are two steps to get a transparent shadow :

    First : You need to connect it as a node to the scene, not as a geometry type.

    let floor = SCNNode()
    floor.geometry = SCNFloor()
    floor.geometry?.firstMaterial!.colorBufferWriteMask = []
    floor.geometry?.firstMaterial!.readsFromDepthBuffer = true
    floor.geometry?.firstMaterial!.writesToDepthBuffer = true
    floor.geometry?.firstMaterial!.lightingModel = .constant
    scene.rootNode.addChildNode(floor)
    

    Shadow on invisible SCNFloor():

    Shadow on visible SCNPlane() and our camera is under SCNFloor():

    For getting a transparent shadow you need to set a shadow color, not the object's transparency itself.

    Second : A shadow color must be set like this for macOS:

    lightNode.light!.shadowColor = NSColor(calibratedRed: 0,
                                                   green: 0, 
                                                    blue: 0, 
                                                   alpha: 0.5)
    

    ...and for iOS it looks like this:

    lightNode.light!.shadowColor = UIColor(white: 0, alpha: 0.5)
    

    Alpha component here (alpha: 0.5) is an opacity of the shadow and RGB components (white: 0) is black color of the shadow.

    P.S.

    sceneView.backgroundColor switching between .clear colour and .white colour.

    In this particular case I can't catch a robust shadow when sceneView.backgroundColor = .clear, because you need to switch between RGBA=1,1,1,1 (white mode: white colour, alpha=1) and RGBA=0,0,0,0 (clear mode: black colour, alpha=0).

    In order to see semi-transparent shadow on a background the components should be RGB=1,1,1 and A=0.5, but these values are whitening the image due to internal compositing mechanism of SceneKit. But when I set RGB=1,1,1 and A=0.02 the shadow is very feeble.

    Here's a tolerable workaround for now (look for solution below in SOLUTION section):

    @objc func toggleTransparent() {
        transparent = !transparent
    }  
    var transparent = false {
        didSet {
            // this shadow is very FEEBLE and it's whitening BG image a little bit
            sceneView.backgroundColor = transparent ? 
                                        UIColor(white: 1, alpha: 0.02) : 
                                        .white
        }
    }
    
    let light = SCNLight()
    light.type = .directional
    
    if transparent == false {
        light.shadowColor = UIColor(white: 0, alpha: 0.9)
    }
    

    If I set light.shadowColor = UIColor(white: 0, alpha: 1) I'll get satisfactory shadow on BG image but solid black shadow on white.

    SOLUTION:

    You should grab a render of 3D objects to have premultiplied RGBA image with its useful Alpha channel. After that, you can composite rgba image of cube and its shadow over image of nature using classical OVER compositing operation in another View.

    Here's a formula for OVER operation :

    (RGB1 * A1) + (RGB2 * (1 – A1))

提交回复
热议问题