加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

swift – 透明SCNFloor上的SceneKit阴影()

发布时间:2020-12-14 04:56:39 所属栏目:百科 来源:网络整理
导读:我有一个地板节点,我需要从定向光投射阴影.此节点需要透明(在AR环境中使用). 当我使用ARKit时这个工作正常,但使用SceneKit的相同设置显示没有阴影或反射.我怎样才能像这样在SceneKit中投射阴影? SceneKit的问题是由于我设置了sceneView.backgroundColor = .
我有一个地板节点,我需要从定向光投射阴影.此节点需要透明(在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,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
    }
}

以下是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,z: 15)

        let testNode = SCNNode(geometry: SCNBox(width: 1,z: -5)
        scene.rootNode.addChildNode(testNode)

        let animation = SCNAction.rotateBy(x: 0,z: 0)
        scene.rootNode.addChildNode(floorNode)

        let light = SCNLight()
        light.type = .directional
        light.shadowColor = NSColor(red: 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,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
    }
}

示例项目:

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为白色时,以及下面 – 清除时的样子.在清晰版本上没有阴影.

Here you can see this effect with white background color of sceneView - shadow is visible

And this if version with sceneView.backgroundColor == .clear. There is UIImageView under this view. I need to use this version,but there is no shadow visible

解决方法

There are two steps to get a transparent shadow :

第一:您需要将其作为节点连接到场景,而不是几何类型.

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)

隐形SCNFloor()上的阴影:

enter image description here

可见SCNPlane()上的阴影和我们的相机在SCNFloor()下:

enter image description here

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

第二:对于macOS,必须像这样设置阴影颜色:

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

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

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

此处的Alpha分量(alpha:0.5)是阴影的不透明度,RGB分量(白色:0)是阴影的黑色.

enter image description here

enter image description here

附:

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

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

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

现在这是一个可容忍的解决方法(在解决方案部分查找下面的解决方案):

@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)
}

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

enter image description here

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.

这是OVER操作的公式:

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

enter image description here

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读