VTK Incorrect Shadows

I’ve been playing around with shadows in VTK as of lately. I may be wrong, but I’m pretty sure they have issues. Here’s a slightly modified example from the VTK examples. Reduced to one light directly above the sphere.

Shadows.cxx (4.8 KB)

You’ll see despite a light being positioned directly above the sphere it produces an ellipse shadow, no matter the view angle. Even worse if you swap lines 143-149 with 153-158. (Or attach a timer and post modified on the light). You’ll find the ellipse shadow varies with the aspect ratio of the window.

I’ve also toyed with the light a great deal (with varying spotlights, point lights, directional lights) all producing the same issues.

Here’s a video of the resize behavior. This could be two issues. One relating to resizing the window. The other with rendering correct shadows in the first place from differing views. This is currently preventing us from using Shadows in iMSTK.

@Lfx_Paul ?

See also 12000.

I’m having similar artifacts. If I start recompile so that the render window is square, I get good shadows; and it survives resizing the window (although my window is not as well behaved as in the video above). But if the render window is not square, I get cropped regions of illumination.

My use case survives resizing the window but only if not zooming out/in sufficiently far. As soon as I zoom far enough out I get the squished shadow. Full replication possible in the script below which simulates the moon casting a shadow on the earth, I’m using python vtk 9.5.1. I have a movie file too but apparently new users can’t upload those.

#!/usr/bin/env python3.11

# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkLight,
    vtkPolyDataMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer
)
from vtkmodules.vtkRenderingOpenGL2 import (
    vtkCameraPass,
    vtkRenderPassCollection,
    vtkSequencePass,
    vtkShadowMapPass
)

def main():
    colors = vtkNamedColors()

    # Earth parameters
    earth_radius = 6371.0  # km
    moon_radius = 1737.0   # km
    earth_moon_distance = 384400.0  # km

    renderer = vtkRenderer()
    renderer.SetBackground(0.0, 0.0, 0.0)  # Black background for space

    width = 640
    height = 480
    renderWindow = vtkRenderWindow()
    # THIS MUST BE SQUARE SEE BUG and workaround HERE:
    # https://discourse.vtk.org/t/invalid-shadow-shape-affected-by-the-aspect-ratio-of-the-window/12000/3
    renderWindow.SetSize(width, width)
    renderWindow.AddRenderer(renderer)

    interactor = vtkRenderWindowInteractor()
    interactor.SetRenderWindow(renderWindow)

    # Sun light: directional at infinite distance
    sun_light = vtkLight()
    sun_light.SetColor(colors.GetColor3d('White'))
    sun_light.SetIntensity(1.0)
    sun_light.SetPositional(0)  # Directional light
    sun_light.SetPosition(0, 0, -1)  # Position defines direction with focal point
    sun_light.SetFocalPoint(0, 0, 0)
    renderer.AddLight(sun_light)

    # Earth
    earth_source = vtkSphereSource()
    earth_source.SetCenter(0, 0, 0)
    earth_source.SetRadius(earth_radius)
    earth_source.SetThetaResolution(100)
    earth_source.SetPhiResolution(100)
    earth_source.Update()

    earth_mapper = vtkPolyDataMapper()
    earth_mapper.SetInputConnection(earth_source.GetOutputPort())

    earth_actor = vtkActor()
    earth_actor.SetMapper(earth_mapper)
    earth_actor.GetProperty().SetColor(colors.GetColor3d('Blue'))
    earth_actor.GetProperty().SetSpecular(0.5)
    earth_actor.GetProperty().SetSpecularPower(20.0)
    earth_actor.GetProperty().SetDiffuse(0.8)
    earth_actor.GetProperty().SetAmbient(0.1)
    renderer.AddActor(earth_actor)

    # Moon
    moon_source = vtkSphereSource()
    moon_source.SetCenter(0, 0, -earth_moon_distance)
    moon_source.SetRadius(moon_radius)
    moon_source.SetThetaResolution(100)
    moon_source.SetPhiResolution(100)
    moon_source.Update()

    moon_mapper = vtkPolyDataMapper()
    moon_mapper.SetInputConnection(moon_source.GetOutputPort())

    moon_actor = vtkActor()
    moon_actor.SetMapper(moon_mapper)
    moon_actor.GetProperty().SetColor(colors.GetColor3d('LightGrey'))
    moon_actor.GetProperty().SetSpecular(0.5)
    moon_actor.GetProperty().SetSpecularPower(20.0)
    moon_actor.GetProperty().SetDiffuse(0.8)
    moon_actor.GetProperty().SetAmbient(0.1)
    renderer.AddActor(moon_actor)

    renderWindow.SetMultiSamples(0)

    shadows = vtkShadowMapPass()

    seq = vtkSequencePass()

    bp = shadows.GetShadowMapBakerPass()
    #bp.SetResolution(2048)
    bp.SetResolution(8192)
    passes = vtkRenderPassCollection()
    passes.AddItem(bp)
    passes.AddItem(shadows)
    seq.SetPasses(passes)

    cameraP = vtkCameraPass()
    cameraP.SetDelegatePass(seq)

    # Tell the renderer to use our render pass pipeline
    glrenderer = renderer
    glrenderer.SetPass(cameraP)

    # Set camera to view the scene, focused on Earth, to see the shadow
    camera = renderer.GetActiveCamera()
    camera.SetPosition(earth_radius * 2, earth_radius * 5, -earth_moon_distance / 2)
    camera.SetFocalPoint(0, 0, 0)
    camera.SetViewUp(0, 1, 0)
    renderer.ResetCameraClippingRange()

    renderWindow.SetWindowName('Earth and Moon with Sun Light and Shadows')
    renderWindow.Render()
    # This is the workaround, but it doesn't hold if you zoom way out and in again
    #renderWindow.SetSize(width, height)

    interactor.Start()

if __name__ == '__main__':
    main()