-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Description
Bug report
Bug summary
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
x, y = np.mgrid[-10:11, -10:11]
z = -(x ** 2 + y ** 2)
ax = fig.add_subplot(121, projection="3d")
pc = ax.plot_surface(x, y, z)
ax = fig.add_subplot(122, projection="3d")
pc = ax.plot_surface(x, y, z / 10)
plt.show()In the right plot, the shading is basically constant throughout the image; in the left one, it changes very sharply at the tip of the paraboloid -- in fact, it nearly looks like the patches immediately at the left and the right are close to being the darkest and the brightest ones! Given that these two patches have nearly the same orientation, how is that possible?
From a quick investigation, I believe the root of the issue is the following: the shading is computed as the dot product of the patch normal(*) and the illumination direction, but this dot product is taken in data space (immediately when the Poly3DCollection is built in plot_surface) rather than after projection to "3d screen space" (at draw time). In data space, the angle between the patches at the tip of the paraboloid is indeed much larger (the z-scale is much wider than the x/y-scale), hence the large change in dot product.
(*) The patch normal is approximated using just three of the vertices of each patch, but this is not the source of the issue -- using e.g. the average of the normals computed at each vertex of the patch doesn't help.
Therefore, I believe the shading should instead be computed at draw time, after projection to "3d screen space". As another argument in favor of such behavior, consider also that if we ever manage to support log-scale in mplot3d, we'd certainly have to compute the dot products after scale application (because they are even more meaningless before scale application). As usual, this could possibly be opt-in via a flag... or perhaps not.
Matplotlib version
- Operating system: linux
- Matplotlib version: master
- Matplotlib backend (
print(matplotlib.get_backend())): qt5agg - Python version: 38
- Jupyter version (if applicable):
- Other libraries:
