Image-based lighting (e.g., PBR) vs shadows

I’ve recently started using the PBR rendering capabilities of VTK and I’ve hit a surprise, specifically, I seem unable to combine shadow maps with environment map lighting. In some ways, the two features seem orthogonal.

For example, I create a scene with objects that have both phong interpolation as well as pbr interpolation. Both get illuminated by lights I put in the scene. However, if I have a shadow pass, only the phong-interpolated actors will receive shadows (with some very erratic behavior consisting of whether the PBR objects even cast shadows).

This behavior is independent of whether the render’s setting for image-based lighting is on or off (the only difference is that when it is off, the PBR materials reflect a black universe).

I would’ve expected that just as the lighting combines with the image based illumination, the shadows would as well. Have I missed something? Is this by design or does it constitute a defect?

I think my original problem statement is some combination of incomplete and/or incorrect. I started with the Shadows example and added in an environment map (well, I tried two). I find that whether or not I get shadows depends on the environment map. I’ll have to poke deeper. I’m currently operating under the hypothesis that it depends on either the range of the environment map. I’ll have to look at the exponents of the two images to see. Stay tuned.

Yep. I’ve spent an instructional day. For anyone else who trips over this:

  1. The efficacy of the shadows will be affected by the relative intensity of the lights with the radiant energy in the environment map. In my case, it was a massive ratio.
  2. Tonemapping is your friend in this regard. As is documented in the vtkToneMappingPass:

    Advanced tone mapping like GenericFilmic, Reinhard or Exponential can be useful when several lights are added to the renderer.

While I tinkered with adding a “scale filter” to reduce the intensity of the environment map, I found simply reducing the exposure of the GenericFilmic tone map seems to make things sane again. This works even with the huge disparity between light intensity and map intensity.

1 Like

FYI @lgivord @meak

After the initial confusion, followed by the subsequent realization, I continued digging and found actual defects (one might be up for debate, but I’m presenting it as a defect):

Sometimes, actors with PBR interpolation will not receive shadows.

vtkOpenGLPolyDatamapper.cxx and vtkShadowMappass.cxx work in conjunction to define the radiance value for when the interpolation is equal to VTK_PBR. They do this by having the mapper define the radiance values and the shadow pass subsequently modifies the radiance definition to include the “shadow factor”. For example:

The simplest solution would be to split the “case 3” generated shader code into two statements: radiance = lightColor#; and radiance *= attenuation;. This would allow the shadow map pass to still successfully find and replace the string using its current substitution logic. Admittedly, this still keeps the brittleness in place, where magical strings generated by one file have to match magical strings expected in another file. But that ship has probably sailed.

“Steepness” constant in the exponential shadow map.

VTK implements Exponential Shadow Maps. Part of that is a constant that tunes the exponential function to more closely approximate the step function that defines the binary state of occlusion. The tuning is handled via a constant named “c” in the paper (and depthC in VTK’s implementation). As the paper says, if c is too low, “we will observe light leaking artifacts”. The paper goes on to discuss the fact that as this constant goes to infinity the approximation mathematically converges to the step function. However, it acknowledges that, due to numerical precision issues, there is a limit to how large it can become before other artifacts are generated. It states: “We empirically determined an optimal value of c = 80 for 32-bit floating point numbers.”

VTK has hard-coded c = 11 for some mysterious reason. In my applications, this led to clear “light leaking artifacts”. This is manifest by casting shadows onto a ground plane from above with numerous objects floating between light and ground at various heights. For a fixed bounding box on the scene, as light occluders get closer to the ground plane, the shadows get paler and paler (eventually disappearing completely). Visually, it looks less like shadows are being cast and more like the depth image is being projected by the light. This is because c = 11 relaxes the exponential function significantly (see figure 2(a) in the linked paper). So, you can get clean, dark shadows from occluders near the light, but not those far from the light.

Perhaps the default value should be revisited? Failing that, making it settable by the user would be a boon. We’ve patched this to c = 80 in our fork and are considering adding a shadow map member that we can set.

Thanks for your deep research @Sean_Curtis

I agree that the vtkShadowPass should be improved and we welcome any such improvements! Feel free to open a MR with proposed changes.

Note: Looks like you are using the github mirror, please note contributions needs to be made in https://gitlab.kitware.com

Best,