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

Texture mapping to a sphere #257

Closed
KDarshan20 opened this issue Oct 7, 2020 · 21 comments
Closed

Texture mapping to a sphere #257

KDarshan20 opened this issue Oct 7, 2020 · 21 comments
Labels
texture General topics around the use of textures

Comments

@KDarshan20
Copy link

KDarshan20 commented Oct 7, 2020

Hello,
I found PyVista while surfing the internet for a library that would help me in my project, a 3D solar system animation. So while working on the project I came across a problem: I couldn't get the textures mapped correctly. For instance,

import pyvista
sphere = pyvista.Sphere()
tex = pyvista.read_texture("Earth.png")
sphere.texture_map_to_plane(inplace=True)
sphere.plot(texture=tex)

Gave the following output:
image.png
While I want it to look like the in-built globe example:
1602034348488_image.png
Earth.png:
Earth.png

@akaszynski
Copy link
Member

akaszynski commented Oct 7, 2020

@banesullivan has done a lot of the implementation of texture mapping, so he'll probably correct me if I'm wrong, but I think the issue is that there's no way for pyvista/vtk to know the mapping between your earth png and the sphere. The texture_map_to_plane method works great on planes:

import pyvista
mesh = pyvista.Plane()
tex = pyvista.read_texture("Earth.png")
mesh.texture_map_to_plane(inplace=True)
mesh.plot(texture=tex)

image

What the texture_map_to_plane is doing is taking each coordinate of the image and interpolating it onto each coordinate of the plane mesh. This is easy to do in 2 dimensions, but in 3 dimensions, there's too many possible solutions. If you plot the mesh of the sphere, you can see that there's a non-uniform distribution of points:

image

So, what you'd have to do if you wanted it properly mapped is to get the right relationship of the points on the sphere to a plane (effectively, UV Mapping), and then use that projection to apply your texture.

If you do choose to project to a sphere, please post your solution here so others can benefit from it.

@KDarshan20
Copy link
Author

Thanks, that helped me understand why the texture gets mapped so.
How do I implement uv mapping?

@KDarshan20
Copy link
Author

I searched the documentation site and found out that texture coordinates can be defined using the t_coords property. So I have to implement UV mapping by defining t_coords by determining a relation between the XYZ coordinates and texture coordinates?

@banesullivan
Copy link
Member

So I have to implement UV mapping by defining t_coords by determining a relation between the XYZ coordinates and texture coordinates?

That's correct! Keep your UV coordinates scaled between 0 and 1 and map each vertex to its coordinates in the UV image space.

FYI, there is a vtkTextureMapToSphere that we could implement much like the texture_map_to_plane filter

@banesullivan
Copy link
Member

I created the globe example manually with the code in this PVGeo filter

Basically, I reproject the sphere's coordinates to a Lat/Lon space (thus a 2D plane) and essentially map the UV coordinates linearly there.

On a sphere, you HAVE to make sure there is an open edge along one longitude or else you will get artifacts of the texture folding back on itself.

I outlined all of this in this ParaView discourse thread

@KDarshan20
Copy link
Author

KDarshan20 commented Oct 8, 2020

I used this slightly modified form of the relation given in Wikipedia:
665757524997177365
And also applied the solution you had given to remove the distortion at the seam, and came up with this code:

import pyvista
import math
import numpy
sphere = pyvista.Sphere(radius=1, theta_resolution=120, phi_resolution=120, start_theta=270.001, end_theta=270)
sphere.t_coords = numpy.zeros((sphere.points.shape[0], 2))
for i in range(sphere.points.shape[0]):
    sphere.t_coords[i] = [0.5 + math.atan2(-sphere.points[i, 0], sphere.points[i, 1])/(2 * math.pi), 0.5 + math.asin(sphere.points[i, 2])/math.pi]
tex = pyvista.read_texture("Earth.png")
sphere.plot(texture=tex)

(I've used a sphere of radius 1, so I don't have to explicitly calculate the unit vector)
And got this satisfactory output:
image
Thanks for the help! I'll close this issue now.

@KDarshan20 KDarshan20 changed the title Texture Mapping Texture mapping to a sphere Oct 8, 2020
@banesullivan
Copy link
Member

Awesome work, @KDarshan20! Thanks for sharing this

@banesullivan banesullivan added add-to-gallery For examples that need to be added to the gallery in the docs texture General topics around the use of textures labels Oct 8, 2020
@KDarshan20
Copy link
Author

KDarshan20 commented Oct 8, 2020 via email

@banesullivan banesullivan removed the add-to-gallery For examples that need to be added to the gallery in the docs label Oct 9, 2020
@bluetyson
Copy link

Nice, now need to find a Mars!

@akaszynski
Copy link
Member

Found Mars.

mars_pv

import pyvista
import math
import numpy
import numpy as np

sphere = pyvista.Sphere(radius=1, theta_resolution=120, phi_resolution=120,
                        start_theta=270.001, end_theta=270)
sphere.t_coords = numpy.zeros((sphere.points.shape[0], 2))


sphere.t_coords[:, 0] = 0.5 + np.arctan2(-sphere.points[:, 0], sphere.points[:, 1])/(2 * math.pi)
sphere.t_coords[:, 1] = 0.5 + np.arcsin(sphere.points[:, 2]) / math.pi


tex = pyvista.read_texture("mars.jpg")
# sphere.plot(texture=tex, smooth_shading=False, background='black', show_axes=False)

# with stars
pl = pyvista.Plotter()
pl.add_background_image('stars.jpg')
pl.add_mesh(sphere, texture=tex, smooth_shading=False)
pl.show()

Mars

http://www.solarviews.com/raw/mars/marscyl1l.jpg

Stars

https://pngio.com/PNG/a44278-space-stars-png.html

@bluetyson
Copy link

Great, thanks Alex!

@banesullivan
Copy link
Member

Nice! @akaszynski, let's add that to pyvista/pyvista#940

@KDarshan20
Copy link
Author

KDarshan20 commented Oct 10, 2020

A source of textures for all planets, the sun, etc.: https://www.solarsystemscope.com/textures/

@banesullivan
Copy link
Member

Awesome! I think I have to make a solar system example then!

@BrandonEscamilla
Copy link

BrandonEscamilla commented Mar 2, 2021

@akaszynsk If the radius needs to be defined as 1738 km, what I need to do? I already tried your code but it doesn't work.

@akaszynski
Copy link
Member

Hello,

Please post the full code you used.

@BrandonEscamilla
Copy link

BrandonEscamilla commented Mar 7, 2021

Hi @akaszynski,

I am sorry for my late response but here is the code and the output. I am creating a sphere with the real radius of the moon. (1738 km)

`
p = pv.Plotter()
sphere = pv.Sphere(radius=1738)

sphere.t_coords = np.zeros((sphere.points.shape[0], 2))

sphere.t_coords[:, 0] = 0.5 + np.arctan2(-sphere.points[:, 0], sphere.points[:, 1])/(2 * math.pi)
sphere.t_coords[:, 1] = 0.5 + np.arcsin(sphere.points[:, 2]) / math.pi
print(sphere.t_coords)

tex = pv.read_texture("2k_moon.jpg")

p.add_mesh(sphere, texture=tex)
p.add_axes()
p.show_grid()
p.show()
`

Captura de Pantalla 2021-03-07 a la(s) 15 00 39

@akaszynski
Copy link
Member

BTW, if anyone is using this with pyvista>=0.33:

import pyvista
import math
import numpy
import numpy as np

from pyvista import examples

sphere = pyvista.Sphere(radius=1, theta_resolution=120, phi_resolution=120,
                        start_theta=270.001, end_theta=270)
sphere.active_t_coords = numpy.zeros((sphere.points.shape[0], 2))


sphere.active_t_coords[:, 0] = 0.5 + np.arctan2(-sphere.points[:, 0], sphere.points[:, 1])/(2 * math.pi)
sphere.active_t_coords[:, 1] = 0.5 + np.arcsin(sphere.points[:, 2]) / math.pi

mars = pyvista.Texture(examples.download_mars_jpg())
stars = examples.download_stars_jpg()

# with stars
pl = pyvista.Plotter()
pl.add_background_image(stars)
pl.add_mesh(sphere, texture=mars, smooth_shading=False)
pl.show()

@RichardScottOZ
Copy link

Definitely will try that one!

@RichardScottOZ
Copy link

image

@f123-maker
Copy link

Are there any methods to control the mapping position with coordinates? Many of the methods found are full package methods. I want a method to use coordinates for local mapping

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
texture General topics around the use of textures
Projects
None yet
Development

No branches or pull requests

7 participants