Turning a cylinder into a sphere without pinching at the poles

Instead of cylinder to sphere mapping I would do a sphere triangulation…

  1. I would first start with 2 hexagons

    each start at pole and end on the equator or do the half only and mirror the other when all done…

  2. then recursively subdivide the triangles

    so divide lines to half and change the mid point coordinate to align with sphere surface. This will create triangulated sphere. Subdivide to valid number of points to form hexagons and have enough grid points.

  3. change the hexagon mid-point coordinates back to plane of hexagon

    So take the other 6 point and compute average coordinates which gives you the point for middle …

Something like this:

hexagonation

for more ideas look here:

[edit1] triangulation (no hexagon corrections)

//---------------------------------------------------------------------------
#include <math.h>
#include "list.h"
class mesh
    {
public:
    class _pnt { public: double p[3]; _pnt(){}; _pnt(_pnt& a){ *this=a; }; ~_pnt(){}; _pnt* operator = (const _pnt *a) { *this=*a; return this; }; /*_pnt* operator = (const _pnt &a) { ...copy... return this; };*/ };
    class _fac { public: int i0,i1,i2; _fac(){}; _fac(_fac& a){ *this=a; }; ~_fac(){}; _fac* operator = (const _fac *a) { *this=*a; return this; }; /*_fac* operator = (const _fac &a) { ...copy... return this; };*/ };
    List<_pnt> pnt; // mesh points
    List<_fac> fac; // mesh triangles

    mesh()      {}
    mesh(mesh& a)   { *this=a; }
    ~mesh() {}
    mesh* operator = (const mesh *a) { *this=*a; return this; }
    //mesh* operator = (const mesh &a) { ...copy... return this; }

    void draw();            // draws the mesh with OpenGL
    void sphere(int n);     // init mesh with unit sphere from triangles (n recursion layers)
    };
//---------------------------------------------------------------------------
void mesh::draw()
    {
    int i;
    _fac *f;
    // fill
    glColor3f(0.7,0.7,0.7);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glDepthFunc(GL_LEQUAL);
    glBegin(GL_TRIANGLES);
    for (i=0,f=fac.dat;i<fac.num;i++,f++)
        {
        glVertex3dv(pnt.dat[f->i0].p);
        glVertex3dv(pnt.dat[f->i1].p);
        glVertex3dv(pnt.dat[f->i2].p);
        }
    glEnd();
    // wireframe
    glColor3f(0.1,0.3,0.7);
    glLineWidth(2.0);
    for (i=0,f=fac.dat;i<fac.num;i++,f++)
        {
        glBegin(GL_LINE_LOOP);
        glVertex3dv(pnt.dat[f->i0].p);
        glVertex3dv(pnt.dat[f->i1].p);
        glVertex3dv(pnt.dat[f->i2].p);
        glEnd();
        }
    glLineWidth(1.0);
    }
//---------------------------------------------------------------------------
void mesh::sphere(int n)
    {
    // init 2 hexagons
    int i,j,m,i0,i1,i2,j0,j1,j2;
    double a,da=M_PI/3.0;
    double *p0,*p1;
    _pnt p;
    _fac f,*g;
    p.p[0]= 0.0;
    p.p[1]= 0.0;
    p.p[2]=+1.0;
    pnt.add(p);
    p.p[2]=-1.0;
    pnt.add(p);
    for (i=0,a=0.0;i<6;i++,a+=da)
        {
        p.p[0]=cos(a);
        p.p[1]=sin(a);
        p.p[2]= 0.0;
        pnt.add(p);
        }
    // top half
    f.i0=0; f.i1=2; f.i2=3; fac.add(f);
    f.i0=0; f.i1=3; f.i2=4; fac.add(f);
    f.i0=0; f.i1=4; f.i2=5; fac.add(f);
    f.i0=0; f.i1=5; f.i2=6; fac.add(f);
    f.i0=0; f.i1=6; f.i2=7; fac.add(f);
    f.i0=0; f.i1=7; f.i2=2; fac.add(f);
    // botom half
    f.i0=1; f.i1=3; f.i2=2; fac.add(f);
    f.i0=1; f.i1=4; f.i2=3; fac.add(f);
    f.i0=1; f.i1=5; f.i2=4; fac.add(f);
    f.i0=1; f.i1=6; f.i2=5; fac.add(f);
    f.i0=1; f.i1=7; f.i2=6; fac.add(f);
    f.i0=1; f.i1=2; f.i2=7; fac.add(f);
    // subdivide triangles
    for (;n>0;n--)              // recursion layers
     for (m=fac.num,i=0;i<m;i++)// scan through all original faces
        {
        g=&fac[i];
        // point indexes
        i0=g->i0; j0=pnt.num;   //     i0
        i1=g->i1; j1=j0+1;      //   j0  j2
        i2=g->i2; j2=j0+2;      // i1  j1  i2
        // genere mid points + sphere surface correction distance from (0,0,0) must be 1.0 (radius)
        for (j=0;j<3;j++) p.p[j]=0.5*(pnt[i0].p[j]+pnt[i1].p[j]); a=1.0/sqrt((p.p[0]*p.p[0])+(p.p[1]*p.p[1])+(p.p[2]*p.p[2])); for (j=0;j<3;j++) p.p[j]*=a; pnt.add(p);
        for (j=0;j<3;j++) p.p[j]=0.5*(pnt[i1].p[j]+pnt[i2].p[j]); a=1.0/sqrt((p.p[0]*p.p[0])+(p.p[1]*p.p[1])+(p.p[2]*p.p[2])); for (j=0;j<3;j++) p.p[j]*=a; pnt.add(p);
        for (j=0;j<3;j++) p.p[j]=0.5*(pnt[i2].p[j]+pnt[i0].p[j]); a=1.0/sqrt((p.p[0]*p.p[0])+(p.p[1]*p.p[1])+(p.p[2]*p.p[2])); for (j=0;j<3;j++) p.p[j]*=a; pnt.add(p);
        // change original fac
        g->i0=j0; g->i1=j1; g->i2=j2;
        //  add 3 x fac
        f.i0=i0; f.i1=j0; f.i2=j2; fac.add(f);
        f.i0=j0; f.i1=i1; f.i2=j1; fac.add(f);
        f.i0=j2; f.i1=j1; f.i2=i2; fac.add(f);
        }
    }
//---------------------------------------------------------------------------

Was a bit curious so I tried to encode this usage is simple:

mesh obj;       // somewhere global...
obj.sphere(3); // init (call once or on change of n...) 
obj.draw();    // inside your gl draw scene routine/event...

So here is the result overview

result

top and bottom poles looks good enough, some distortion is present along the equators but partially it can be caused also by fish-eye. If the initial shape is feed with closer to wanted result start geometry then it may have much better results

Leave a Comment