代码之家  ›  专栏  ›  技术社区  ›  Damian Dudycz

透明地板上的场景阴影()

  •  5
  • Damian Dudycz  · 技术社区  · 6 年前

    我有一个 floor node directional light . 此节点需要是透明的(用于 AR 环境)。 ARKit ,但使用相同的设置 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
        }
    }
    

    以下是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
        }
    }
    

    示例项目:

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

    网间网操作系统: https://www.dropbox.com/s/fk71oay1sopc1vp/Test.zip?dl=1

    在macOS中,你可以改变ViewController最后一行的背景颜色-我需要它清晰,这样我就可以在它下面显示相机预览。

    在下面的图片上,你可以看到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

    1 回复  |  直到 6 年前
        1
  •  3
  •   Community Marino Di Clemente    4 年前

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

    弗斯特 :您需要将其作为 node scene ,而不是作为 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)
    

    enter image description here

    可见屏幕上的阴影()我们的相机在屏幕下方(): enter image description here

    为了得到一个 transparent shadow shadow color ,而不是 object's transparency itself

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

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

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

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

    alpha: 0.5 )是一个 opacity white: 0 )是黑影的颜色。

    enter image description here

    enter image description here

    附笔。

    sceneView.backgroundColor 切换 .clear .white 颜色 .

    sceneView.backgroundColor = .clear RGBA=1,1,1,1 ( 白色模式 :白色,α=1)和 RGBA=0,0,0,0 ( :黑色,α=0)。

    为了在背景上看到半透明阴影,组件应该 RGB=1,1,1 A=0.5 ,但由于SceneKit的内部合成机制,这些值会使图像变白。但当我开始 RGB=1,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(white: 0, alpha: 1) 我去拿 在背景图像上,但是 在白色上。

    enter image description here

    解决方案 :

    你应该抓取一个三维对象的渲染,使其具有预乘的RGBA图像及其有用的Alpha通道。之后,你就可以合成了 rgba image of cube and its shadow 结束 image of nature 使用经典 OVER

    这里有一个公式 结束

    enter image description here

        2
  •  2
  •   Casey Fleser    5 年前

    这已经有一段时间,因为这是张贴,但也许有人会发现这个替代解决方案有用。我遇到了一个类似的情况,我最终做的是渲染使用多个通过 SCNTechnique . 首先我把地板涂成白色 diffuse 然后我渲染了没有地板的场景的其余部分。为此,我设置了 categoryBitMask SCNFloor 设置为3,其余的默认值为1。

    接下来我创建了我的 SCN技术 此定义将地板和场景的其余部分渲染到单独的缓冲区中,然后将它们合并到最终场景中:

    self.sceneView.technique = SCNTechnique(dictionary: [
      "passes" : [
        "store-floor": [
          "draw" : "DRAW_NODE",
          "node" : "floor-node",
          "inputs" : [],
          "outputs" : [ "color" : "color_floor" ]
        ],
        "store-scene": [
          "draw" : "DRAW_SCENE",
          "excludeCategoryMask" : 2,
          "inputs" : [],
          "outputs" : [ "color" : "color_scene" ]
        ],
        "recall-scene": [
          "draw" : "DRAW_QUAD",
          "metalVertexShader" : "vertex_tecnique_basic",
          "metalFragmentShader" : "fragment_tecnique_merge",
          "inputs" : [ "alphaTex" : "color_floor", "sceneTex" : "color_scene" ],
          "outputs" : [ "color" : "COLOR" ]
        ]
      ],
      "symbols" : [
        "vertexSymbol" : [ "semantic" : "vertex" ]
      ],
      "targets" : [
        "color_floor" : [ "type" : "color" ],
        "color_scene" : [ "type" : "color" ],
      ],
      "sequence" : [
        "store-floor",
        "store-scene",
        "recall-scene"
      ]
    ])
    

    using namespace metal;
    #include <SceneKit/scn_metal>
    
    struct TechniqueVertexIn
    {
        float4 position [[attribute(SCNVertexSemanticPosition)]];
    };
    
    struct TechniqueVertexOut
    {
      float4 framePos [[position]];
      float2 centeredLoc;
    };
    
    constexpr sampler s = sampler(coord::normalized, address::repeat, filter::linear);
    
    vertex TechniqueVertexOut vertex_tecnique_basic(
      TechniqueVertexIn     in [[stage_in]],
      constant SCNSceneBuffer&  scnFrame [[buffer(0)]])
    {
      TechniqueVertexOut vert;
    
      vert.framePos = float4(in.position.x, in.position.y, 0.0, 1.0);
      vert.centeredLoc = float2((in.position.x + 1.0) * 0.5 , (in.position.y + 1.0) * -0.5);
    
      return vert;
    }
    
    fragment half4 fragment_tecnique_merge(
      TechniqueVertexOut  vert [[stage_in]],
      texture2d<float>  alphaTex [[texture(0)]],
      texture2d<float>  sceneTex [[texture(1)]])
    {
        float4 alphaColor = alphaTex.sample(s, vert.centeredLoc);
        float4 sceneColor = sceneTex.sample(s, vert.centeredLoc);
      float alpha     = 1.0 - max(max(alphaColor.r, alphaColor.g), alphaColor.b); // since floor should be white, could just pick a chan
    
      alpha *= alphaColor.a;
      alpha = max(sceneColor.a, alpha);
    
      return half4(half3(sceneColor.rgb * alpha), alpha);
    }
    

    enter image description here