将圆柱体变成球体而不在极点处捏合

4
我正在生成一个由六边形网格构成的星球。极点不是必需的,这使得操作变得更加容易。有没有更好的方法将圆柱体转化为具有均匀六边形/三角形的球体?
以下是所需步骤:
1. 生成一个二维六边形平面(完成) 2. 将该平面变成圆柱体(完成) 3. 将圆柱体变成球体/地球体(有点效果)
对于第2步,我只是使用正弦和余弦将顶点移动到圆形中。 对于第3步,现在我只是使用:vertices[i] = vertices[i].normalized * radius; 下图可视化了当前问题。

请注意,极点被故意削减了。红色部分显示一个六边形网格的样子。我需要保持它们大致相同的大小和方向,因为它们用于游戏玩法和视觉元素。每个六边形都有一个邻居列表,基本上像一个图形一样工作。

2
http://en.wikipedia.org/wiki/Map_projection#Metric_properties_of_maps 对你可能有用。它说明了当将平面网格映射到球体上时,只能保留一些方面,并提供了哪些量可能需要保留的一些想法。对于许多合理的组合,存在适合的地图投影方式。也许你不能通过圆柱体进行转换,但可以从正二十面体的网络开始,最终得到一个测地线球,如[Math SE讨论的那样] (https://dev59.com/dHA75IYBdhLWcg3w7tt6)。 - MvG
只是想搭建网格吗?如果是的话,六边形已经有40年没有使用了-现在你用三角形来构建网格。(一些注释。)但出于什么可能的原因会担心网格 - 你只需点击一个按钮即可获取任意分辨率的完美网格球。 - Fattie
相反,您的意思是您希望在球体的纹理上看到许多六边形吗?就像您在六边形平铺游戏中使用的那样。如果是这种情况,网格是完全无关紧要的,您甚至不需要知道正在使用哪种类型的网格。请告诉我们它是哪种情况! - Fattie
有关编程的内容翻译如下:经典的回答链接到有关此问题的经典博客文章... https://dev59.com/J2455IYBdhLWcg3wD_6Z#8427705 AndreasKahler的代码存在一些小错误(多年前确实如此),请注意。另外,请参考http://answers.unity3d.com/questions/311412/generating-a-grid-on-a-sphere.html和http://answers.unity3d.com/questions/35313/texturing-a-icosphere.html。 - Fattie
你在吗 @tsiiffi - Fattie
@tsiiffi 看看这个:半球的六边形平铺。通过一些调整,它可能可以转换为你的地图用途。 - Spektre
1个回答

8

我建议使用球面三角网格而不是圆柱形到球形的映射...

  1. 首先从两个六边形开始

    每个六边形从极点开始,到赤道结束,或者只做一半,在全部完成后镜像另一半...

  2. 然后递归地将三角形细分

    所以将线段分成一半,并将中点坐标更改为与球面表面对齐。这将创建一个三角形球体。将其细分为合法的网格点数以形成六边形并具有足够的网格点。

  3. 将六边形中点坐标改回六边形平面上

    因此,需要取另外6个点并计算平均坐标,这将为您提供中心点...

类似这样:

hexagonation

更多想法请查看此处:

[编辑1] 三角剖分(无六边形修正)

//---------------------------------------------------------------------------
#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);
        }
    }
//---------------------------------------------------------------------------

我有点好奇,所以尝试编码,使用方法很简单:

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

以下是结果概述:

result

顶部和底部极点看起来足够好,赤道周围存在一些畸变,但这可能也是由于鱼眼造成的。如果将初始形状与所需结果的起始几何形状更接近,则可能会有更好的结果。


1
干得好!看起来这仍然会导致一些五边形(我猜这是一个数学事实,无法避免)。看起来三角剖分比圆柱体映射更好作为起点。 - tsiiffi
愚蠢的问题:是否有可能将所有五边形移动到极点?然后我可以切掉极点并使其无法访问。 - tsiiffi
@tsiiffi 我不这么认为,因为每层需要不同数量的六边形(在极点最少,在赤道最多),但只用六边形无法实现这一点。因此,每层至少需要一个五边形,以便在单个六边形计数更改时移动,并且只能沿着经度角移动... - Spektre
但这是针对Unity3D的,Spektre。 - Fattie
很棒的回答@Spektre - 为什么没有被采纳呢? - Reblochon Masque
1
@ReblochonMasque 谢谢,嗯,用户已经离线了...但我习惯于不接受(即使是HQ)答案,在这里并不少见...此外,如果您想要更好的球体,请使用二十面体作为起点(这是为更好地适应OP情况而开发的)。我得到了许多与此主题相关的答案(其中2个链接在此答案中),但多年来只有一些新的回答,比如cube/sphere mappingHyper spiral aHyper spiral b - Spektre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接