Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lightposition behaves weirdly #2477

Open
alexcjohnson opened this issue Mar 15, 2018 · 6 comments · Fixed by #3593
Open

lightposition behaves weirdly #2477

alexcjohnson opened this issue Mar 15, 2018 · 6 comments · Fixed by #3593
Assignees

Comments

@alexcjohnson
Copy link
Collaborator

lightposition behaves similarly for both mesh3d and surface, but it's nearly unusable. Generally x is left (-) to right (+) and y is bottom (-) to top(+), but z has effects that defy easy categorization other than z=1 being "special". Sometimes x and y flip randomly, and anyway they don't seem to have the same scales, ie x=y does not put the light at 45 degrees.

@etpinard
Copy link
Contributor

#3415 will bring mesh3d and surface lightposition to the same level.

It is still broken, but we might want to wait until v2 or the regl rewrite (cc ##949) before spending more time on this.

@archmoj
Copy link
Contributor

archmoj commented Oct 28, 2019

As illustrated in this demo,
if we choose to apply lightposition.y for surface similar to other gl3d traces, and enable flat shading for mesh3d traces the result looks OK.
I can imagine finding such a setup could be pretty frustrating for users.
What about revising the defaults of those parameters?
I don't think we should enable smoothing for mesh3d by default.
And why surface plot should have a different light position by default?

@alexcjohnson
Copy link
Collaborator Author

flatshading definitely helps - in fact without it I can't really figure out any physical meaning to the lighting of mesh3d traces, but with it there does seem to be, for any given lightposition, a light that looks like it's coming from somewhere fixed wrt the scene as the scene is rotated. And moreover that fixed somewhere is the same for all traces. So based on that alone I'd probably enable flatshading by default. And I'm highly in favor of standardizing all the lightposition defaults to something with intelligible consequences, with all traces matching each other. In fact, can we add a scene.lightposition that all the traces in a scene inherit from if not provided at the trace level?

Now, as to where this light appears to be, my description above still seems to hold. I'd add that when both x and y are fairly small (±hundreds or less) or when z is significant (<-10 or >1) it no longer seems possible to assign the light a fixed position as the scene rotates - I can align the cubes or saddle surfaces in what should be symmetric viewpoints (90-degree rotations of the cube, 180-degree rotations of the saddle) and I get different light regions.

So I definitely would not close this issue. To me this would be closed when the meaning of lightposition.x/y/z can be precisely defined, something like:

x is the position of the light source left (-) or right (+) in screen-space relative to the center of the scene.
y is its position below (-) or above(+), and z is its position behind (-) or in front of (+) the screen.
Only the normalized x, y, z vector matters, the light always behaves as if it is coming from a point source infinitely far away.

I don't mind waiting on this - doesn't seem like we have anyone who particularly cares about this. But it's definitely still a bug.

Here's the dash app I'm using to play with this, based on your codepen @archmoj, plus sliders for `lightposition.x/y/z`. It's actually quite responsive, even though I'm sending the whole figure up with every mousemove. Go `Plotly.react`!
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output


app = dash.Dash(__name__)


app.layout = html.Div([
    html.Div([
        html.P(id='xout'),
        dcc.Slider(id='x', value=1000, min=-10000, max=10000, updatemode='drag'),
        html.P(id='yout'),
        dcc.Slider(id='y', value=1000, min=-10000, max=10000, updatemode='drag'),
        html.P(id='zout'),
        dcc.Slider(id='z', value=0, min=-100, max=100, updatemode='drag'),
    ]),
    dcc.Graph(id='fig')
])


@app.callback(
    [
        Output('xout', 'children'),
        Output('yout', 'children'),
        Output('zout', 'children'),
        Output('fig', 'figure')
    ],
    [Input('x', 'value'), Input('y', 'value'), Input('z', 'value')]
)
def fig(x, y, z):
    camera1 = {"eye": {"x": 1.2, "y": 1.2, "z": 1.2}}
    camera2 = {"eye": {"x": 1, "y": 2, "z": 1}}

    lightposition = {"x": x, "y": y, "z": z}
    color = "yellow"
    return 'x: {}'.format(x), 'y: {}'.format(y), 'z: {}'.format(z), {
      "data":  [{
        "x": [0, 1, 0, 1, 0, 1, 0, 1],
        "y": [0, 0, 1, 1, 0, 0, 1, 1],
        "z": [0, 0, 0, 0, 1, 1, 1, 1],
        "i": [0, 3, 4, 7, 0, 6, 1, 7, 0, 5, 2, 7],
        "j": [1, 2, 5, 6, 2, 4, 3, 5, 4, 1, 6, 3],
        "k": [3, 0, 7, 4, 6, 0, 7, 1, 5, 0, 7, 2],
        "flatshading": True,
        "lightposition": lightposition,
        "color": color,
        "type": "mesh3d",
        "scene": "scene1"
      }, {
        "x": [0, 1, 0, 1, 0, 1, 0, 1],
        "y": [0, 0, 1, 1, 0, 0, 1, 1],
        "z": [0, 0, 0, 0, 1, 1, 1, 1],
        "i": [0, 3, 4, 7, 0, 6, 1, 7, 0, 5, 2, 7],
        "j": [1, 2, 5, 6, 2, 4, 3, 5, 4, 1, 6, 3],
        "k": [3, 0, 7, 4, 6, 0, 7, 1, 5, 0, 7, 2],
        "flatshading": True,
        "lightposition": lightposition,
        "color": color,
        "type": "mesh3d",
        "scene": "scene2"
      }, {
        "x": [0, 1],
        "y": [0, 1],
        "z": [[0, 1], [1, 0]],
        "lightposition": lightposition,
        "colorscale": [[0, color], [1, color]],
        "showscale": False,
        "type": "surface",
        "scene": "scene3"
      }, {
        "x": [0, 1],
        "y": [0, 1],
        "z": [[0, 1], [1, 0]],
        "lightposition": lightposition,
        "colorscale": [[0, color], [1, color]],
        "showscale": False,
        "type": "surface",
        "scene": "scene4"
      }, {
        "x": [0, 0, 1, 1, 0, 0, 1, 1],
        "y": [0, 1, 1, 0, 0, 1, 1, 0],
        "z": [0, 0, 0, 0, 1, 1, 1, 1],
        "u": [0, 0, 1, 1, 0, 0, 1, 1],
        "v": [0, 1, 1, 0, 0, 1, 1, 0],
        "w": [0, 0, 0, 0, 1, 1, 1, 1],
        "lightposition": lightposition,
        "colorscale": [[0, color], [1, color]],
        "showscale": False,
        "type": "cone",
        "scene": "scene5"
      }, {
        "x": [0, 0, 1, 1, 0, 0, 1, 1],
        "y": [0, 1, 1, 0, 0, 1, 1, 0],
        "z": [0, 0, 0, 0, 1, 1, 1, 1],
        "u": [0, 0, 1, 1, 0, 0, 1, 1],
        "v": [0, 1, 1, 0, 0, 1, 1, 0],
        "w": [0, 0, 0, 0, 1, 1, 1, 1],
        "lightposition": lightposition,
        "colorscale": [[0, color], [1, color]],
        "showscale": False,
        "type": "cone",
        "scene": "scene6"
      }, {
        "x": [0, 0, 1, 1, 0, 0, 1, 1],
        "y": [0, 1, 1, 0, 0, 1, 1, 0],
        "z": [0, 0, 0, 0, 1, 1, 1, 1],
        "u": [0, 0, 1, 1, 0, 0, 1, 1],
        "v": [0, 1, 1, 0, 0, 1, 1, 0],
        "w": [0, 0, 0, 0, 1, 1, 1, 1],
        "lightposition": lightposition,
        "colorscale": [[0, color], [1, color]],
        "showscale": False,
        "type": "streamtube",
        "scene": "scene7"
      }, {
        "x": [0, 0, 1, 1, 0, 0, 1, 1],
        "y": [0, 1, 1, 0, 0, 1, 1, 0],
        "z": [0, 0, 0, 0, 1, 1, 1, 1],
        "u": [0, 0, 1, 1, 0, 0, 1, 1],
        "v": [0, 1, 1, 0, 0, 1, 1, 0],
        "w": [0, 0, 0, 0, 1, 1, 1, 1],
        "lightposition": lightposition,
        "colorscale": [[0, color], [1, color]],
        "showscale": False,
        "type": "streamtube",
        "scene": "scene8"
      }],
      "layout": {
        "paper_bgcolor": "lightblue",
        "uirevision": True,
        "width": 1200,
        "height": 600,
        "scene1": {
          "domain": {
            "x": [0, 0.25],
            "y": [0, 0.25]
          },
          "camera": camera1
        },
        "scene2": {
          "domain": {
            "x": [0, 0.25],
            "y": [0.5, 1]
          },
          "camera": camera2
        },
        "scene3": {
          "domain": {
            "x": [0.25, 0.5],
            "y": [0, 0.5]
          },
          "camera": camera1
        },
        "scene4": {
          "domain": {
            "x": [0.25, 0.5],
            "y": [0.5, 1]
          },
          "camera": camera2
        },
        "scene5": {
          "domain": {
            "x": [0.5, 0.75],
            "y": [0, 0.5]
          },
          "camera": camera1
        },
        "scene6": {
          "domain": {
            "x": [0.5, 0.75],
            "y": [0.5, 1]
          },
          "camera": camera2
        },
        "scene7": {
          "domain": {
            "x": [0.75, 1],
            "y": [0, 0.5]
          },
          "camera": camera1
        },
        "scene8": {
          "domain": {
            "x": [0.75, 1],
            "y": [0.5, 1]
          },
          "camera": camera2
        }
      }
    }


if __name__ == '__main__':
    app.run_server(debug=True)

@archmoj
Copy link
Contributor

archmoj commented Aug 10, 2020

@aniruddhkb
Copy link

I would like to report an interim fix for some of the issues being faced:

From what I could gather based on my experiments, there the default reference lightposition does depend on the 3D axes' origin (0,0,0).

I deduced this by plotting the surface of a sphere, cranking down the ambient and diffuse and upping the specular until a reflection was visible at a specific orientation. I plotted this sphere centered at various locations in XYZ. In each case, the light seemed to come from a different location WRT the default center of the plot (the center of the sphere)

The light source (as far as I can tell) is at (0,0,+Z), where +Z is a positive Z value.

In the meantime, if you are finding some aspects of lighting unsatisfactory (e.g. some regions aren't getting lit properly no matter what), then a fix may be to normalize or standardize the X, Y, Z ranges of your data and then reapply custom labels.

@aniruddhkb
Copy link

Further, it seems that there is some degree of control available to the light's position in Z, but I don't see any change in X, Y

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants