Note
Go to the end to download the full example code.
Attenuation#
Attenuation is the phenomenon of light’s intensity being gradually dampened as it propagates through a medium. In PyVista positional lights can show attenuation. The quadratic attenuation model uses three parameters to describe attenuation: a constant, a linear and a quadratic parameter. These parameters describe the decrease of the beam intensity as a function of the distance, I(r). In a broad sense the constant, linear and quadratic components correspond to I(r) = 1, I(r) = 1/r and I(r) = 1/r^2 decay of the intensity with distance from the point source. In all cases a larger attenuation value (of a given kind) means stronger dampening (weaker light at a given distance).
So the constant attenuation parameter corresponds roughly to a constant intensity component. The linear and the quadratic attenuation parameters correspond to intensity components that decay with distance from the source. For the same parameter value the quadratic attenuation produces a beam that is shorter in range than that produced by linear attenuation.
Three spotlights with three different attenuation profiles each:
from __future__ import annotations
import pyvista as pv
plotter = pv.Plotter(lighting='none')
billboard = pv.Plane(direction=(1, 0, 0), i_size=6, j_size=6)
plotter.add_mesh(billboard, color='white')
all_attenuation_values = [(1, 0, 0), (0, 2, 0), (0, 0, 2)]
offsets = [-2, 0, 2]
for attenuation_values, offset in zip(all_attenuation_values, offsets):
light = pv.Light(position=(0.1, offset, 2), focal_point=(0.1, offset, 1), color='cyan')
light.positional = True
light.cone_angle = 20
light.intensity = 15
light.attenuation_values = attenuation_values
plotter.add_light(light)
plotter.view_yz()
plotter.show()
It’s not too obvious but it’s visible that the rightmost light with quadratic attenuation has a shorter range than the middle one with linear attenuation. Although it seems that even the leftmost light with constant attenuation loses its brightness gradually, this partly has to do with the fact that we sliced the light beams very close to their respective axes, meaning that light hits the surface in a very small angle. Altering the scene such that the lights are further away from the plane changes this:
plotter = pv.Plotter(lighting='none')
billboard = pv.Plane(direction=(1, 0, 0), i_size=6, j_size=6)
plotter.add_mesh(billboard, color='white')
all_attenuation_values = [(1, 0, 0), (0, 2, 0), (0, 0, 2)]
offsets = [-2, 0, 2]
for attenuation_values, offset in zip(all_attenuation_values, offsets):
light = pv.Light(position=(0.5, offset, 3), focal_point=(0.5, offset, 1), color='cyan')
light.positional = True
light.cone_angle = 20
light.intensity = 15
light.attenuation_values = attenuation_values
plotter.add_light(light)
plotter.view_yz()
plotter.show()
Now the relationship of the three kinds of attenuation seems clearer.
For a more practical comparison, let’s look at planes that are perpendicular to the axis of each light (making use of the fact that shadowing between objects is not handled by default):
plotter = pv.Plotter(lighting='none')
# loop over three lights with three kinds of attenuation
all_attenuation_values = [(2, 0, 0), (0, 2, 0), (0, 0, 2)]
light_offsets = [-6, 0, 6]
for attenuation_values, light_x in zip(all_attenuation_values, light_offsets):
# loop over three perpendicular planes for each light
for plane_y in [2, 5, 10]:
screen = pv.Plane(center=(light_x, plane_y, 0), direction=(0, 1, 0), i_size=5, j_size=5)
plotter.add_mesh(screen, color='white')
light = pv.Light(position=(light_x, 0, 0), focal_point=(light_x, 1, 0), color='cyan')
light.positional = True
light.cone_angle = 15
light.intensity = 5
light.attenuation_values = attenuation_values
light.show_actor()
plotter.add_light(light)
plotter.view_vector((1, -2, 2))
plotter.show()
Total running time of the script: (0 minutes 0.644 seconds)