3
$\begingroup$

Is it possible to map the attached image of the Cosmic Microwave Background (CMB) onto the exterior of a sphere in Blender? The few tutorials I saw use either sphere projection or mark seam, but those do not seem to reproduce the shape of this projection in the UV/Image Editor. By the way this is called an Aitoff projection.

Thanks a lot in advance.

CMB

$\endgroup$
3
  • $\begingroup$ Do you need a UV map or a material nodes setting to do it? $\endgroup$
    – lemon
    Aug 12, 2019 at 18:12
  • $\begingroup$ @lemon What would be the difference? Sorry, I have little experience with Blender. $\endgroup$
    – Pxx
    Aug 12, 2019 at 18:42
  • $\begingroup$ Actually, I went around my problem by converting this projection to an equilateral projection using the NASA software G.Projector, and then I could follow other tutorials for the mapping. $\endgroup$
    – Pxx
    Aug 12, 2019 at 18:43

2 Answers 2

3
$\begingroup$

I've found a way to do, it's like a bypass so maybe you won't like it, but at least it works not too bad ah ah.

  • Find an Aitoff projection with longitudes and latitudes, for example the earth (if you come from this planet).

enter image description here

  • Create a UV sphere with half the amount of longitudes and latitudes: here it makes a 12 segment and 6 ring sphere.
  • Mark a vertical seam.

enter image description here

  • Unwrap.
  • On the UV/Image Editor stick each vertex to the point it is supposed to stick to. As there are half the amount of longitudes and latitudes it won't be tedious.

enter image description here

  • If you want more precisions you can pin this first unwrap, add edge loops around your sphere and re-unwrap (here in my picture, more latitudes). Doesn't need a lot of adjustments this time.

enter image description here

  • Give your earth a Subsurf modifier. Check if it's about correct in Rendered mode, i.e. if it looks like the actual earth (whew, it does).

enter image description here

  • Duplicate your sphere, give it a new material, this time load your CMB picture in the Image Texture node.

enter image description here

$\endgroup$
5
  • $\begingroup$ Oh yes, this is a great workaround! Better than what I did until now in any case. $\endgroup$
    – Pxx
    Aug 12, 2019 at 18:44
  • $\begingroup$ glad you like it because it's a kind of trick, hard to find a way to properly unwrap, it looks like the Aitoff is pretty particular, but maybe someone will find another way? $\endgroup$
    – moonboots
    Aug 12, 2019 at 18:47
  • $\begingroup$ I think if it's a one time thing (as it is the case for me), you could maybe double the number of segments and rings and spend more time on adjusting the vertices to obtain a very smooth map, isn't it? $\endgroup$
    – Pxx
    Aug 12, 2019 at 18:49
  • $\begingroup$ Yes, I've edited, use the pin tool if you want to add edge loops, it will make it more precise ;) $\endgroup$
    – moonboots
    Aug 12, 2019 at 18:51
  • $\begingroup$ Here is by the way how it looks like after converting the projection to equilateral, as I mentioned in the comments of the question: ibb.co/GVpD8HY $\endgroup$
    – Pxx
    Aug 12, 2019 at 19:01
6
$\begingroup$

Hammer Projection

enter image description here Nodes on left, UV map on right

A while back, sh*t like 6 years ago, I wrote a number of map projections, including Mercator and among them the Hammer, close relation to Aitoff which I've cleaned up for blender 2.8

enter image description here UVMap result on default UV sphere

To use, run the script in text editor, or install as addon. Adds a Hammer projection entry to the UV mesh edit mode menu.

enter image description here

from numpy import sin, cos, sinc, arccos
from mathutils import Vector, Matrix
from math import degrees, pi, sqrt
import bmesh
import bpy
bl_info = {
    "name": "Hammer Project",
    "author": "batFINGER",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "View3D > Mesh > UV UnWrap > Hammer Project",
    "description": "UV Hammer Projection",
    "warning": "",
    "wiki_url": "",
    "category": "UV",
}


class Spherical:

    def __init__(self, vert, north=(0, 0, 1), long0=(0, -1)):
        self.vert = vert
        self.north, self.long0 = Vector(north), Vector(long0)
        v = vert.co
        R = v.length
        lat = pi / 2 - self.north.angle(v)
        is_pole = v.xy.length < 0.0001

        long = 0 if is_pole else v.xy.angle_signed(long0)

        self.R = R
        self.lat = lat
        self.long = long

    def __repr__(self):
        return "%.3f, %.3f, %.3f" % (self.R,
                                     degrees(self.lat), degrees(self.long))


class HammerUV:
    def x2(self, lat, long):
        a = 2 * sqrt(2) * cos(lat) * sin(long / 2)
        b = sqrt(1 + cos(lat) * cos(long / 2))
        return a / b

    def y2(self, lat, long):
        a = sqrt(2) * sin(lat)
        b = sqrt(1 + cos(lat) * cos(long / 2))
        return a / b

    # Aitoff equations (no joy)
    def x(self, lat, long):
        a = arccos(cos(lat) * cos(long / 2))
        b = 2 * cos(lat) * sin(long / 2)
        return b / sinc(a)

    def y(self, lat, long):
        a = arccos(cos(lat) * cos(long / 2))
        b = sin(lat)
        return b / sinc(a)

    def uv(self, face, uv_layer):
        ''' Map a UV face '''
        # see if face is east / west
        c = face.calc_center_median()

        for l in face.loops:
            luv = l[uv_layer]
            # apply the location of the vertex as a UV
            p = self.pts[l.vert]

            lat, long = p.lat, p.long  # % longfix
            # quick hack for now.
            if l.vert.co.y > 0:
                if c.x < 0 and long > 0:
                    long = -long
                elif c.x > 0 and long < 0:
                    long = -long

            u, v = self.x2(lat, long), self.y2(lat, long)
            uv = self.translate + self.scale @ Vector((u, v))
            luv.uv = uv


    def calc_uv(self):
        bm = self.bm

        uv_layer = bm.loops.layers.uv.verify()

        bm.select_mode = {'FACE', 'VERT'}
        bm.select_flush_mode()
        bm.select_flush(True)
        # adjust UVs
        for f in bm.faces:
            self.uv(f, uv_layer)

    def __init__(self, me, bm):
        def pt(v):
            s = Spherical(v)
            return s

        # add a new uv map
        uv = me.uv_layers.new(name="Hammer")
        me.uv_layers.active = uv
        self.bm = bm
        # spherical coords for verts
        self.pts = {v: pt(v) for v in self.bm.verts}
        # radius average of calc'd s.R
        self.R = sum(s.R for s in self.pts.values()) / len(self.pts)

        # scale UV to [0, 1] make scale matrix
        scale_x = 0.5 / self.x2(0, pi)
        scale_y = 0.5 / self.y2(pi / 2, 0)
        self.scale = Matrix([[scale_x, 0], [0, scale_y]])
        # and transform vector
        self.translate = Vector((0.5, 0.5))


class UV_OT_HammerProject(bpy.types.Operator):
    """Create a Hammer Projection UV Map"""
    bl_idname = "uv.hammer_project"
    bl_label = "Hammer Project"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return (context.mode == 'EDIT_MESH')

    def execute(self, context):
        obj = context.edit_object
        me = obj.data
        bm = bmesh.from_edit_mesh(me)
        merc = HammerUV(me, bm)
        merc.calc_uv()
        bmesh.update_edit_mesh(me)
        return {'FINISHED'}


def unwrapmenu(self, context):
    ''' menu item '''
    self.layout.operator("uv.hammer_project")


def register():
    # add to edit mesh > UV menu
    bpy.types.VIEW3D_MT_uv_map.prepend(unwrapmenu)
    bpy.utils.register_class(UV_OT_HammerProject)


def unregister():
    bpy.types.VIEW3D_MT_uv_map.remove(unwrapmenu)
    bpy.utils.unregister_class(UV_OT_HammerProject)


if __name__ == "__main__":
    register()

Since it is a UV mapping can also use it on other spherical meshes like the icosphere

enter image description here

I added the code for the Aitoff projection, but have not quite managed to get that one "steady", not sure about the sinc(...) method required for that one.

Using Nodes

In https://blender.stackexchange.com/a/159492/15543 dabbling in nodes I came up with a node setup to generate latitude longitude using object texture coordinates.

Basically the equivalent node setup to the script above, generating the UV and feeding into texture node.

enter image description here

Tissot index hammer projection image from wiki by Justin Kunimune CC BY-SA 4.0

$\endgroup$
6
  • $\begingroup$ nice! but I can't make it work on my 2.79 version, when I press U to unwrap I can see Hammer Project in the pop-up menu but nothing happens... $\endgroup$
    – moonboots
    Aug 13, 2019 at 6:36
  • 2
    $\begingroup$ Clearly marked as being for 2.8. There are changes in API regarding uv layer name. $\endgroup$
    – batFINGER
    Aug 13, 2019 at 10:02
  • $\begingroup$ Yes but it doesn't work for me in 2.8 :/ $\endgroup$
    – moonboots
    Aug 13, 2019 at 11:06
  • 1
    $\begingroup$ Added gif of me using it in 2.81. There's not much I can do about it not working for you based on the comment "it doesn't work for me" $\endgroup$
    – batFINGER
    Aug 13, 2019 at 11:57
  • 1
    $\begingroup$ Thanks, that looks great! I will give it a try tonight and see if it works on my 2.8 version. $\endgroup$
    – Pxx
    Aug 13, 2019 at 17:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .