我有一个地板节点
,我需要在其上转换来自定向光
的阴影。这个节点需要是透明的(在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
}
}
这里是 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
}
}
示例项目:
苹果操作系统: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 为白色时的样子,而下面的图片 - 清晰。透明版没有阴影。
最佳答案
获得透明阴影有两个步骤:
首先:您需要将其连接为 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)
阴影在可见的 SCNPlane() 上,我们的相机在 SCNFloor() 下:
For getting a
transparent shadow
you need to set ashadow color
, not theobject's transparency itself
.
第二个:A shadow color
对于 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 分量 ( alpha: 0.5
) 是一个 opacity
阴影和 RGB 分量 ( white: 0
) 的颜色是阴影的黑色。
附言
sceneView.backgroundColor
switching between.clear
colour and.white
colour.
在这种特殊情况下,当 sceneView.backgroundColor = .clear
时我无法捕捉到强烈的阴影, 因为你需要在 RGBA=1,1,1,1
之间切换(白色模式:白色,alpha=1)和RGBA=0,0,0,0
(清晰模式:黑色,alpha=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)
我会在 BG 图像上得到令人满意的阴影,但在白色图像上得到纯黑色阴影。
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
overimage of nature
using classicalOVER
compositing operation in another View.
这是 OVER
的公式操作:
(RGB1 * A1) + (RGB2 * (1 – A1))
关于swift - 透明 SCNFloor() 上的 SceneKit 阴影,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52654624/