SceneKit shadow on a transparent SCNFloor()
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 ashadow color
, not theobject'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
overimage of nature
using classicalOVER
compositing operation in another View.
Here's a formula for OVER
operation :
(RGB1 * A1) + (RGB2 * (1 – A1))
Is it possible to project light on a transparent SCNFloor in ARKit?
Yes, you can do that by casting a white (or bleeded color) shadow instead of a black one. For that you have to use deferred shadows type that are rendered in a post-processing pass.
Consider, you must use two separate lights in your scene – first one for white shadows casting, and a second one for lighting objects that cast these white shadows. You're able to include and exclude objects from lighting scheme implementing categoryBitMask property.
// SETTING A SHADOW CATCHER
let plane = SCNPlane(width: 10, height: 10)
plane.firstMaterial?.isDoubleSided = true
plane.firstMaterial?.lightingModel = .shadowOnly
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = -.pi / 2
scene.rootNode.addChildNode(planeNode)
// SETTING A LIGHT
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .directional
lightNode.light?.intensity = 50
lightNode.light?.color = UIColor.black
lightNode.eulerAngles.x = -.pi / 2
scene.rootNode.addChildNode(lightNode)
// DEFERRED SHADOWS
lightNode.light?.castsShadow = true
lightNode.light?.shadowMode = .deferred // important
lightNode.light?.forcesBackFaceCasters = true // important
lightNode.light?.shadowRadius = 10
lightNode.light?.shadowColor = UIColor.white
Can I make shadow that can look through transparent object with scenekit and arkit?
A known issue with deferred shading is that it doesn’t work with transparency so you may have to remove that line and use the default forward shading again. That said, the “simple property” you are looking for is the .renderingOrder property on the SCNNode. Set it to 99 for example. Normally the rendering order doesn’t matter because the z buffer is used to determine what pixel is in front of others. For the shadow to show up through the transparant part of the object you need to make sure the object is rendered last.
On a different note, assuming you used some of the material settings I posted on your other question, try setting the shininess value to something like 0.4.
Note that this will still create a shadow as if the object was not transparent at all, so it won’t create a darker shadow for the label and cap. For additional realism you could opt to fake the shadow entirely, as in using a texture for the shadow and drop that on a plane which you rotate and skew as needed. For even more realism, you could fake the caustics that way too.
You may also want to add a reflection map to the reflective property of the material. Almost the same as texture map but in gray scale, where the label and cap are dark gray (not very reflective) and a lighter gray for the glass portion (else it will look like the label is on the inside of the glass). Last tip: use a Shell modifier (that’s what it’s called in 3Ds max anyway) to give the glass model some thickness.
Hide SCNFloor but show shadow with SceneKit (swift)
In your screenshots I don't see any shadow. I only see the reflection. For shadows you need either a directional or spot light. For the reflections over your map did you try to the the map texture to your SCNFloor? Another option is to use a SCNFloor with a material transparency of 0 but that will have a cost due to the overdraw.
Issue with invisible Shadow Plane in SceneKit / ARKit
There are 3 more instance properties to take into consideration:
var shadowRadius: CGFloat { get set }
var shadowCascadeCount: Int { get set }
var shadowCascadeSplittingFactor: CGFloat { get set }
If you don't setup these ones they definitely cause rendering artifacts.
let lightNode = SCNNode()
lightNode.light = SCNLight()
// POSITION OF DIRECTIONAL LIGHT ISN'T IMPORTANT.
// ONLY DIRECTION IS CRUCIAL FOR DIRECTIONAL LIGHTS.
lightNode.rotation = SCNVector4(x: 0, y: 0, z: 0, w: 1)
lightNode.light!.type = .directional
lightNode.light!.castsShadow = true
lightNode.light?.shadowMode = .deferred
/* THREE INSTANCE PROPERTIES TO SETUP */
lightNode.light?.shadowRadius = 3.25
lightNode.light?.shadowCascadeCount = 3
lightNode.light?.shadowCascadeSplittingFactor = 0.09
lightNode.light?.shadowColor = UIColor(white: 0, alpha: 0.75)
scene.rootNode.addChildNode(lightNode)
And one more thing – when
Auto Adjust
is off:Light, like a camera, has
near
andfar
clipping planes for setup.
lightNode.light?.zNear = 0
lightNode.light?.zFar = 1000000 // Far Clipping Plane is important
Hope this helps.
Showing a shadow on a transparent floor/plane in scenekit
Ok, so check out this example project on Github:
https://github.com/carolight/Metal-Shadow-Map/blob/master/Shadows/Shader.metal#L67
That link is to the shader that performs the shadow map testing. Basically, it normalizes the Z position of the fragment being rendered and compares it to the Z-Buffer of the shadow map. If it is less than the shadow-Z, the pixel is fully lit (line 67) else it is tinted slightly (line 69).
What you want to do instead is write (0,0,0, shadow_opacity) for line 69 and (0,0,0,0) for line 67. This should emit transparent pixels. Set shadow_opacity as a uniform from [0.0..1.0].
The rest of the setup is shown in the example. Tilt the plane/camera to the desired setting, skip drawing the cube/vase/whatever in the main pass
if you don't really want it in the scene (line 279). (However, note the shadow pass
and keep it there).
Related Topics
How to Change the Uinavigationbar Title's Position
Timedmetadata' Deprecated. Another Method? <Updated>
Cannot Read the Nfc Chip of the Epassport Using iOS13
What Is Other Option Available in Swift Instead of Refactoring and Renaming Class or Attribute Name
How Is a Type-Erased Generic Wrapper Implemented
What Is the Role of Avcapturedevicetype.Builtindualcamera
Create Skscene Subclasses Programmatically, Without Size Info
Swift Lazy Subscript Ignores Filter
How to Block Users on Firebase in a Social Media App? for iOS
How to Use Dispatch Groups to Wait to Call Multiple Functions That Depend on Different Data
Convert Float Value to String in Swift
How to Setup a Second Component with a Uipickerview
Able to Write/Read File But Unable to Delete File Swift
Generic Swift Dictionary Extension for Nil Filtering
How to Make Protocol Associated Type Require Protocol Inheritance and Not Protocol Adoption