4

I'm using camera transform matrices in PyVista to do an animation. But my object is shadowed:

enter image description here

I apply the same transformation matrices to the lights, as you can see below.

circles = fractal3(4)
for i, matrix in enumerate(matrices):
    pltr = pv.Plotter(window_size=[512, 512], off_screen=True)
    pltr.set_background("#363940")
    pltr.set_position(satellite0)
    pltr.camera.zoom(0.9)
    pltr.camera.model_transform_matrix = matrix
    for circle in circles:
        center, radius = circle
        sphere = pv.Sphere(radius, center = center+(0,))
        pltr.add_mesh(sphere, smooth_shading=True, color="red", specular=10)
    pltr.set_focus((0,0,0))
    for light in pltr.renderer.lights:
        light.transform_matrix = matrix
    pngname = "zzpic%03d.png" % i
    pltr.show(screenshot=pngname)

EDIT

Here is the animation I get when I don't do anything to the lights:

enter image description here

Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225
  • The default lights are camera lights, so they should transform together with the camera. What happens when you don't touch the lights and only transform the camera? And what is the desired outcome? – Andras Deak -- Слава Україні Dec 16 '21 at 18:11
  • @AndrasDeak I edited my post to show you the result when I don't touch the lights. On some frames the object is almost black. This is what I don't want. – Stéphane Laurent Dec 16 '21 at 21:44
  • Thanks for the update. Just to cover every base: is there a specific reason why you are using transform matrices rather than [orbiting](https://docs.pyvista.org/examples/02-plot/orbit.html#sphx-glr-examples-02-plot-orbit-py) on a custom path? We can probably figure out your question too, but I think it would Just Work^TM with orbiting. – Andras Deak -- Слава Україні Dec 16 '21 at 21:48
  • @AndrasDeak Thanks for your attention. I noticed that orbiting produces bad quality gifs. – Stéphane Laurent Dec 16 '21 at 21:49
  • I would probably save to an `mp4` file and then try converting that to a gif. Or you could do [what `orbit_on_path` does under the hood](https://github.com/pyvista/pyvista/blob/b76c8ce08424d16c893248537f042fe1d60c98d6/pyvista/plotting/plotting.py#L4163-L41820), which is basically setting a new position in a loop, resetting the camera clipping range and taking a screeshot for your png. – Andras Deak -- Слава Україні Dec 16 '21 at 21:54
  • @AndrasDeak Moreover, orbiting is restricted to simple rotations. In my animation, I use a [satellite curve](https://mathcurve.com/courbes3d.gb/satellite/satellite.shtml) for the path of the camera. – Stéphane Laurent Dec 16 '21 at 22:02
  • Ah no, I took a look at the code, it seems that one can use a custom path. – Stéphane Laurent Dec 16 '21 at 22:06
  • Yes, `orbit_on_path` will move your camera along an arbitrary line. If your transform matrices are more complicated (e.g. they involve rolling around the view axis) then orbiting is probably not an option. Doing it manually yourself would be, but then you have to take care of rolling the camera (possibly by setting the viewup on each frame). – Andras Deak -- Слава Україні Dec 16 '21 at 22:07

1 Answers1

1

As I noted in comments, you are probably better off using something like orbit_on_path(). You noted that the gif ends up being low-quality which is why you were looking for other options.

You could still either save to an MP4 file and convert that to a gif with whatever quality requirements you have, or you can replicate what orbit_on_path() does under the hood. Moving the camera (rather than manipulating its transform matrix) should be a lot more robust.

Anyway, to answer your exact question here, you would have to transform the lights in a non-trivial manner. Here's a minimal example to replicate your situation, based on your incomplete code:

mport numpy as np
import pyvista as pv
from pyvista.examples import download_bunny

# example data
mesh = download_bunny()
mesh.rotate_x(90)
matrices = [
    np.array([
        [np.cos(phi), -np.sin(phi), 0, 0],
        [np.sin(phi), np.cos(phi), 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 1]
    ])
    for phi in np.linspace(0, 2*np.pi, 22, endpoint=False)
]

for i, matrix in enumerate(matrices):
    plotter = pv.Plotter(window_size=[512, 512], off_screen=True)
    plotter.set_background("#363940")
    plotter.camera.zoom(3)
    plotter.camera.model_transform_matrix = matrix
    plotter.add_mesh(mesh, smooth_shading=True, color="red", specular=10)
    plotter.set_focus(mesh.center)
    #for light in plotter.renderer.lights:
    #    pass  # option 1
    #    light.transform_matrix = matrix  # option 2
    #    light.transform_matrix = np.linalg.inv(matrix)  # option 3
    pngname = f"zzpic{i:03d}.png"
    plotter.show(screenshot=pngname)

In there I left 3 options for manipulating the renderer's default lights.

The first option is to leave the lights' transform matrix alone. The resulting animation looks like this: animation with rotating bunny, lights rotate with it

As you can see, the lights are static with respect to the scene (the bunny). The bunny has a dark side and a light side. This is somewhat surprising, because the default lights are camera lights which move together with the camera. But setting the transform matrix seems to bypass this linkage, probably moving the camera's effective position away from its physical position.

The second option is what you have in your question: applying the same transform matrix to the lights: rotating bunny with weirdly wobbling lights It's less obvious than in your example, but the lights move around, neither linked to the bunny nor the camera. With some squinting one might notice that there are two dark and two light instances during a complete turn of the bunny.

Which brings me to the last option: applying the inverse transform to the lights! Here's how the result looks: rotating bunny which is always well-lit

It seems that this setup keeps the bunny consistently well-lit. On your fractaly example and satellite path it will be very obvious if this indeed fixes the lighting. I would expect this to put the effective position of the lights back with the effective position of the camera.