透明SCNFloor上的SceneKit阴影()

Dam*_*ycz 7 shadow augmented-reality scenekit swift arkit

我有一个floor node,我需要投射阴影directional light.此节点需要透明(在AR环境中使用).这在我使用时工作正常ARKit,但使用相同的设置SceneKit不显示阴影或反射.我怎么能SceneKit像这样投下阴影?SceneKit的问题是由我设置的事实引起的sceneView.backgroundColor = .clear- 但我在这个应用程序中需要这种行为.这可以以某种方式避免吗?

示例代码,演示此问题(仅适用于设备,不适用于模拟器):

@IBOutlet weak var sceneView: SCNView! {
    didSet {

        sceneView.scene = SCNScene()

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        sceneView.pointOfView = cameraNode

        let testNode = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
        testNode.position = SCNVector3(x: 0, y: 0, z: -5)
        sceneView.scene!.rootNode.addChildNode(testNode)

        let animation = SCNAction.rotateBy(x: 0, y: .pi, z: 0, duration: 3.0)
        testNode.runAction(SCNAction.repeatForever(animation), completionHandler: nil)

        let floor = SCNFloor()
        floor.firstMaterial!.colorBufferWriteMask = []
        floor.firstMaterial!.readsFromDepthBuffer = true
        floor.firstMaterial!.writesToDepthBuffer = true
        floor.firstMaterial!.lightingModel = .constant
        let floorNode = SCNNode(geometry: floor)
        floorNode.position = SCNVector3(x: 0, y: -2, z: 0)
        sceneView.scene!.rootNode.addChildNode(floorNode)

        let light = SCNLight()
        light.type = .directional
        light.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
        light.color = UIColor.white
        light.castsShadow = true
        light.automaticallyAdjustsShadowProjection = true
        light.shadowMode = .deferred
        let sunLightNode = SCNNode()
        sunLightNode.position = SCNVector3(x: 1_000, y: 1_000, z: 0)
        sunLightNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: .pi * 1.5)
        sunLightNode.light = light
        sceneView.scene!.rootNode.addChildNode(sunLightNode)

        let omniLightNode: SCNNode = {
            let omniLightNode = SCNNode()
            let light: SCNLight = {
                let light = SCNLight()
                light.type = .omni
                return light
            }()
            omniLightNode.light = light
            return omniLightNode
        }()
        sceneView.scene!.rootNode.addChildNode(omniLightNode)
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    let tapGR = UITapGestureRecognizer(target: self, action: #selector(toggleTransparent))
    view.addGestureRecognizer(tapGR)
}

@objc func toggleTransparent() {
    transparent = !transparent
}

var transparent = false {
    didSet {
        sceneView.backgroundColor = transparent ? .clear : .white
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是macOS的相同示例,构建在SceneKit游戏项目之上:

import SceneKit
import QuartzCore

class GameViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!

        // create and add a camera to the scene
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)

        // place the camera
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

        let testNode = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
        testNode.position = SCNVector3(x: 0, y: 0, z: -5)
        scene.rootNode.addChildNode(testNode)

        let animation = SCNAction.rotateBy(x: 0, y: .pi, z: 0, duration: 3.0)
        testNode.runAction(SCNAction.repeatForever(animation), completionHandler: nil)

        let floor = SCNFloor()
        floor.firstMaterial!.colorBufferWriteMask = []
        floor.firstMaterial!.readsFromDepthBuffer = true
        floor.firstMaterial!.writesToDepthBuffer = true
        floor.firstMaterial!.lightingModel = .constant
        let floorNode = SCNNode(geometry: floor)
        floorNode.position = SCNVector3(x: 0, y: -2, z: 0)
        scene.rootNode.addChildNode(floorNode)

        let light = SCNLight()
        light.type = .directional
        light.shadowColor = NSColor(red: 0, green: 0, blue: 0, alpha: 0.5)
        light.color = NSColor.white
        light.castsShadow = true
        light.automaticallyAdjustsShadowProjection = true
        light.shadowMode = .deferred
        let sunLightNode = SCNNode()
        sunLightNode.position = SCNVector3(x: 1_000, y: 1_000, z: 0)
        sunLightNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: .pi * 1.5)
        sunLightNode.light = light
        scene.rootNode.addChildNode(sunLightNode)

        let omniLightNode: SCNNode = {
            let omniLightNode = SCNNode()
            let light: SCNLight = {
                let light = SCNLight()
                light.type = .omni
                return light
            }()
            omniLightNode.light = light
            return omniLightNode
        }()
        scene.rootNode.addChildNode(omniLightNode)

        // retrieve the SCNView
        let scnView = self.view as! SCNView

        // set the scene to the view
        scnView.scene = scene

        // allows the user to manipulate the camera
        scnView.allowsCameraControl = true

        // configure the view
        scnView.backgroundColor = .clear
//        scnView.backgroundColor = .white // shadow works in this mode, but I need it to be clear
    }
}
Run Code Online (Sandbox Code Playgroud)

示例项目:

MacOS:https://www.dropbox.com/s/1o50mbgzg4gc0fg/Test_macOS.zip? dl = 1

iOS:https://www.dropbox.com/s/fk71oay1sopc1vp/Test.zip?dl = 1

在macOS中你可以在ViewController的最后一行更改backgroundColor - 我需要它清楚,所以我可以在它下面显示相机预览.

在下面的图片中,您可以看到当sceneView.backgroundColor为白色时,以及下面 - 清除时的样子.在清晰版本上没有阴影.

在这里你可以看到这个效果与sceneView的白色背景颜色 - 阴影是可见的

如果版本与sceneView.backgroundColor == .clear. 在这个视图下有UIImageView. 我需要使用这个版本,但没有可见的阴影

ARG*_*Geo 4

获得透明阴影有两个步骤:

\n

首先:您需要将其作为 a 连接node到 the scene,而不是作为 a geometry type

\n
let floor = SCNNode()\nfloor.geometry = SCNFloor()\nfloor.geometry?.firstMaterial!.colorBufferWriteMask = []\nfloor.geometry?.firstMaterial!.readsFromDepthBuffer = true\nfloor.geometry?.firstMaterial!.writesToDepthBuffer = true\nfloor.geometry?.firstMaterial!.lightingModel = .constant\nscene.rootNode.addChildNode(floor)\n
Run Code Online (Sandbox Code Playgroud)\n

不可见的 SCNFloor() 上的阴影:\n在此输入图像描述

\n

可见 SCNPlane() 上的阴影,并且我们的相机位于 SCNFloor() 下:\n在此输入图像描述

\n
\n

为了获得 atransparent shadow你需要设置 a shadow color,而不是object\'s transparency itself.

\n
\n

第二shadow color对于 macOS,A 必须这样设置:

\n
lightNode.light!.shadowColor = NSColor(calibratedRed: 0,\n                                               green: 0, \n                                                blue: 0, \n                                               alpha: 0.5)\n
Run Code Online (Sandbox Code Playgroud)\n

...对于 iOS 来说,它看起来像这样:

\n
lightNode.light!.shadowColor = UIColor(white: 0, alpha: 0.5)\n
Run Code Online (Sandbox Code Playgroud)\n

这里的 Alpha 分量 ( alpha: 0.5) 是opacity阴影的颜色,RGB 分量 ( white: 0) 是阴影的黑色。

\n

在此输入图像描述

\n

在此输入图像描述

\n

聚苯乙烯

\n
\n

sceneView.backgroundColor.clear颜色与.white颜色之间切换

\n
\n

在这种特殊情况下,我无法捕捉到强大的阴影sceneView.backgroundColor = .clear,因为您需要在RGBA=1,1,1,1白色模式:白色,alpha = 1)和RGBA=0,0,0,0清晰模式:黑色,alpha = 0)之间切换。

\n

为了在背景上看到半透明阴影,组件应该是RGB=1,1,1A=0.5,但由于 SceneKit 的内部合成机制,这些值会使图像变白。但当我设置时RGB=1,1,1A=0.02影子非常微弱。

\n

这是目前可以接受的解决方法(在“解决方案”部分中查找下面的解决方案):

\n
@objc func toggleTransparent() {\n    transparent = !transparent\n}  \nvar transparent = false {\n    didSet {\n        // this shadow is very FEEBLE and it\'s whitening BG image a little bit\n        sceneView.backgroundColor = \n                        transparent ? UIColor(white: 1, alpha: 0.02) : .white\n    }\n}\n\nlet light = SCNLight()\nlight.type = .directional\n\nif transparent == false {\n    light.shadowColor = UIColor(white: 0, alpha: 0.9)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果我设置,light.shadowColor = UIColor(white: 0, alpha: 1)我将在 BG 图像上获得令人满意的阴影,但在白色上获得纯黑色阴影

\n

在此输入图像描述

\n
\n

解决方案

\n

您应该抓取 3D 对象的渲染,以使用其有用的 Alpha 通道预乘 RGBA 图像。之后,您可以在另一个视图中使用经典合成操作rgba image of cube and its shadow进行合成。image of natureOVER

\n
\n

这是一个OVER运算公式:

\n
\n

(RGB1 * A1) + (RGB2 * (1 \xe2\x80\x93 A1))

\n
\n

在此输入图像描述

\n