Keep light source fixed in data coordinates

Iโ€™m plotting a sphere with Mesh3d and Iโ€™d like the light source to rotate with the plot. Right now, the specular reflection stays roughly fixed when I rotate the plot. Iโ€™m trying to simulate the Sun shining on a planet, so the Sun needs to rotate with the planet when I change the viewing angle. Is there a way to do this?

@mburger

The light position remains invariant no matter how you are moving the sphere, by mouse (the camera_eye position is also invariant). You can change light position through animation. Because the lighting and lightposition setttings for Mesh3d are similar to those for Surface, I illustrate the lightposition rotation for a parameterized sphere, defined as an instance of go.Surface.

LE: After a few experiments with different initial light positions, I noticed that go.Surface.lightposition, as well as go.Mesh3d.lightposition, has a lot of issues. No matter how the initial lightposition['z'] is set to a zl value, with -1<zl <1, and keeping xl=yl=10000, the light trajectory is the same during zaxis-rotation. When xl=yl=zl, the light spot does not rotate. This seems to be related to the default camera.eye position that has eye.x=eye.y=eye.z. @archmoj could explain whatโ€™s going on. Is lightposition buggy or not?

import numpy as np
from numpy import pi, sin, cos
import plotly.graph_objects as go

#Rotation about zaxis:

def rotate_z(x, y, z, theta):
    w = x+1j*y
    rw = np.exp(1j*theta)*w
    return np.real(rw), np.imag(rw), z

#Sphere parameterization:

lon = np.linspace(0, 2*pi, 200)
lat = np.linspace(-pi/2, pi/2)
llon, llat = np.meshgrid(lon, lat)
x = cos(llon)*cos(llat)
y = sin(llon)*cos(llat)
z = sin(llat)

# constant colorscale
brown = [[0, '#9f3632'],
        [1, '#9f3632']]

#initial light position:
xl = 10000
yl = 10000
zl = 0.75

fig = go.Figure(go.Surface(x=x, y=y, z=z, 
                           colorscale=brown, 
                           showscale=False,
                           lighting=dict(ambient=0.5,
                                         diffuse=1,
                                         fresnel=2,        
                                         specular=0.5,
                                         roughness=0.5),
             lightposition=dict(x=xl,
                                y=yl,
                                z=zl)))

                          
fig.update_layout(dict(title='Animating  Light Position Rotation',
              width=800,
              height=800));
                        

fig.update_scenes( xaxis_visible=False,
                   yaxis_visible=False,
                   zaxis_visible=False,
                   camera_eye=dict(z=zl));

#define animation frames
frames = []

theta = np.linspace(0, 2*pi, 36)
for t in theta:
    new_xl, new_yl, new_zl = rotate_z(xl, yl, zl, -t)
    frames.append(go.Frame(data=[go.Surface(lightposition=dict(x=new_xl, 
                                                           y=new_yl,              
                                                           z=new_zl))],
                           traces=[0]))
fig.update(frames=frames)

fig.update_layout(updatemenus=[dict(type='buttons', 
                                y=0.75,
                                x=1.05,
                                active=0,
                                buttons=[dict(label='Play',
                                              method='animate',
                                              args=[None, 
                                                    dict(frame=dict(duration=50, 
                                                                    redraw=True),
                                                         transition=dict(duration=0),
                                                         fromcurrent=True,
                                                         mode='immediate')])])])    
    ```
1 Like

There is this issue open https://github.com/plotly/plotly.js/issues/2477 which could be related.

Thank you @archmoj. For sure the odd position of the light spot is caused by this issue.