Search code examples
opengl-es-2.0scenekitmetal

Showing a shadow on a transparent floor/plane in scenekit


trying to figure out how I can render a shadow on an invisible plane, so the background of my sceneView shows through.

THREE.js has a ShadowMaterial which does exactly this - only the shadow is rendered.

Current thinking is to make a custom Metal shader which looks straightforward, but i'm unsure how to go about knocking out the floor to show everything aside from the shadow.

Here's an example of a shadow catcher: https://knowledge.autodesk.com/search-result/caas/sfdcarticles/sfdcarticles/Maya-2015-Shadow-Catching-with-Use-Background-material.html


Solution

  • 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).