algorithm - 将平面网格变形为球体

标签 algorithm math projection mercator

很好的一天,
现在我正试着把一个平面弯曲成一个球体。
我已经准备好把Mercator projectionlla to ecef一起试了所以结果会延迟,但它不像球体(半球体)。
最成功的变体是这样的(更像一个帐篷,而不是半个球体):
enter image description here
Code for this tent (pastebin)。我正在使用three.js进行渲染。
所以我想听听你的建议。我做错什么了?

最佳答案

使用球面坐标系。角度long,lat是平面中的二维线性u,v坐标,输出为3Dx,y,z
将平面网格的顶点(点)转换为球面
我怀疑你的分数是(x,y,z)形式,所以你需要先计算u,vU,V是位于平面上的垂直单位基向量。通过在平面网格上减去2个点,对尺寸进行正火,并利用交叉积来保证垂直度所以:

u = `dot_product((x,y,z),U)` = x*U.x + y*U.y + z*U.z
v = `dot_product((x,y,z),V)` = x*V.x + y*V.y + z*V.z

现在转换为球体角度:
long=6.2831853*u/u_size_of_mesh
lat =3.1415926*v/v_size_of_mesh

最后计算球面上的new(x,y,z)
x = R*cos(lat)*cos(long)
y = R*cos(lat)*sin(long)
z = R*sin(lat)

网格
平面网格必须具有足够密集的点结构(足够多的三角形/面),否则球体将看起来不像它应该的那样。另一个问题是平面网格没有边,球面没有边。ti-s可能会在平面边缘连接的球面上产生似乎/间隙。如果要避免此问题,可以在平面网格的相对边上的边之间添加面以填充间隙,或者完全丢弃网格,然后使用统一的点网格重新采样平面。
如果要完全重新采样网格,则最好首先创建规则球体网格,例如:
Sphere triangulation by mesh subdivision
然后通过逆过程计算平面上的对应点到1,这样就可以插值点的其他参数(如颜色、纹理坐标等)
[注]
如果要对此设置动画,请在原始平面点P0(x,y,z)和相应的球体曲面点P1(x,y,z)之间使用线性插值,动画参数如下:
P = P0 + (P1-P0)*t

如果t=<0.0,1.0>则输出为平面网格,否则如果t=0则输出为球体。中间的任何地方都是包装过程,因此以足够小的步骤(如t=1)将t增加到1,并在某些计时器中呈现。。。
[Edit1]U,V基向量
这个想法很简单,得到两个非平行矢量,并改变其中一个,使其垂直于第一个,但仍然在同一平面上。
取任何网格面
例如三角形ABC
计算平面上2个非零非平行矢量
这很容易,只要减去任何2对顶点,例如:
U.x=B.x-A.x
U.y=B.y-A.y
V.x=C.x-A.x
V.y=C.y-A.y

把它们按大小分开
ul=sqrt((U.x*U.x)+(U.y*U.y))
vl=sqrt((V.x*V.x)+(V.y*V.y))
U.x/=ul
U.y/=ul
V.x/=vl
V.y/=vl

使它们垂直
所以保持一个向量不变(例如0.01),并计算另一个向量,使其垂直。为此你可以使用交叉积两个单位向量的叉积是垂直于二者的新单位向量。两种可能性中的哪一种仅取决于操作数的顺序(U),因此例如:
// W is perpendicular to U,V
W.x=(U.y*V.z)-(U.z*V.y)
W.y=(U.z*V.x)-(U.x*V.z)
W.z=(U.x*V.y)-(U.y*V.x)
// V is perpendicular to U,W
V.x=(U.y*W.z)-(U.z*W.y)
V.y=(U.z*W.x)-(U.x*W.z)
V.z=(U.x*W.y)-(U.y*W.x)

(U x V) = - (V x U)只是一个临时向量(在图像中称为W),而它是曲面的法向量。
basis vectors
尺寸和对齐
现在由于我没有任何关于你的网格信息,我不知道它的形状,大小等…理想的情况是网格是矩形的并且向量与它的边对齐在这种情况下,只需按每个方向的矩形大小(如左图所示)对坐标进行规格化。
如果网格与此不同,并且使用此方法从面计算V',则结果可能根本不会与边对齐(它们可以以任何角度旋转)。
non aligned
如果无法避免这种情况(通过选择角面),则形状的角点将沿每个边具有不同的坐标限制,并且您需要以某种方式将它们插值或映射到正确的球形间隔(不能更具体,因为我不知道您到底在做什么)。
对于几乎是矩形的形状,有时可以将边用作U,V,即使它们彼此不是完全垂直的。
[Eddi2] C++示例
如果你在Z轴(如高度图)中得到了完全对齐的方形网格和一些噪声,那么这就是我将如何进行网格转换:
//---------------------------------------------------------------------------
struct _pnt // points
    {
    double xyz[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; };*/
    };
struct _fac // faces (triangles)
    {
    int i0,i1,i2;
    double nor[3];
    _fac(){}; _fac(_fac& a){ *this=a; }; ~_fac(){}; _fac* operator = (const _fac *a) { *this=*a; return this; }; /*_fac* operator = (const _fac &a) { ...copy... return this; };*/
    };
// dynamic mesh
List<_pnt> pnt;
List<_fac> fac;
//---------------------------------------------------------------------------
void mesh_normals() // compute normals
    {
    int i;
    _fac *f;
    double a[3],b[3];
    for (f=&fac[0],i=0;i<fac.num;i++,f++)
        {
        vector_sub(a,pnt[f->i1].xyz,pnt[f->i0].xyz);    // a = pnt1 - pnt0
        vector_sub(b,pnt[f->i2].xyz,pnt[f->i0].xyz);    // b = pnt2 - pnt0
        vector_mul(a,a,b);                              // a = a x b
        vector_one(f->nor,a);                           // nor = a / |a|
        }
    }
//---------------------------------------------------------------------------
void mesh_init()    // generate plane mesh (your square with some z noise)
    {
    int u,v,n=40;   // 40x40 points
    double d=2.0/double(n-1);
    _pnt p;
    _fac f;
    Randomize();
    RandSeed=13;
    // create point list
    pnt.allocate(n*n); pnt.num=0;               // preallocate list size to avoid realocation
    for (p.xyz[0]=-1.0,u=0;u<n;u++,p.xyz[0]+=d) // x=<-1.0,+1.0>
     for (p.xyz[1]=-1.0,v=0;v<n;v++,p.xyz[1]+=d)// y=<-1.0,+1.0>
        {
        p.xyz[2]=0.0+(0.05*Random());           // z = <0.0,0.05> noise
        pnt.add(p);
        }
    // create face list
    vector_ld(f.nor,0.0,0.0,1.0);
    for (u=1;u<n;u++)
     for (v=1;v<n;v++)
        {
        f.i0=(v-1)+((u-1)*n);
        f.i1=(v-1)+((u  )*n);
        f.i2=(v  )+((u-1)*n);
        fac.add(f);
        f.i0=(v  )+((u-1)*n);
        f.i1=(v-1)+((u  )*n);
        f.i2=(v  )+((u  )*n);
        fac.add(f);
        }
    mesh_normals();
    }
//---------------------------------------------------------------------------
void mesh_sphere()  // convert to sphere
    {
    int i;
    _pnt *p;
    double u,v,lon,lat,r,R=1.0;
    // I know my generated mesh is aligned so:
    double U[3]={ 1.0,0.0,0.0 };
    double V[3]={ 0.0,1.0,0.0 };
    for (p=&pnt[0],i=0;i<pnt.num;i++,p++)   // process all points
        {
        // get the u,v coordinates
        u=vector_mul(p->xyz,U);
        v=vector_mul(p->xyz,V);
        // I also know the limits are <-1,+1> so conversion to spherical angles:
        lon=M_PI*(u+1.0);               // <-1.0,+1.0> -> <0.0,6.28>
        lat=M_PI*v*0.5;                 // <-1.0,+1.0> -> <-1.57,+1.57>
        // compute spherical position (superponate z to r preserve noise)
        r=R+p->xyz[2];
        p->xyz[0]=r*cos(lat)*cos(lon);
        p->xyz[1]=r*cos(lat)*sin(lon);
        p->xyz[2]=r*sin(lat);
        }
    mesh_normals();
    }
//---------------------------------------------------------------------------
void mesh_draw()    // render
    {
    int i;
    _fac *f;
    glColor3f(0.2,0.2,0.2);
    glBegin(GL_TRIANGLES);
    for (f=&fac[0],i=0;i<fac.num;i++,f++)
        {
        glNormal3dv(f->nor);
        glVertex3dv(pnt[f->i0].xyz);
        glVertex3dv(pnt[f->i1].xyz);
        glVertex3dv(pnt[f->i2].xyz);
        }
    glEnd();
    }
//---------------------------------------------------------------------------

我使用了我的动态列表模板,因此:
U,VU,V相同
List<double> xxx;double xxx[];添加到列表末尾
xxx.add(5);访问数组元素(安全)
5访问数组元素(不安全但快速的直接访问)
xxx[7]是数组的实际使用大小
xxx.dat[7]清除数组并设置xxx.num=0
xxx.numxxx.reset()项预先分配空间
其用法如下:
mesh_init();
mesh_sphere();

结果如下:
overview
左边是生成的带有噪波的平面网格,右边是转换后的结果。
代码反映了上面的所有内容,并将z-噪声添加到球体半径以保留特征。从几何体以标准方式重新计算法线。对于整个TBN矩阵,您需要拓扑中的连接信息并从中重新计算(或者利用球体几何体并从中使用TBN)。
顺便说一下,如果你想映射到球体而不是网格转换,你应该看看相关的QA:
Normal mapping gone horribly wrong
Bump-map a sphere with a texture map
Applying map of the earth texture a Sphere
make seamless height-map texture for sphere (planet)
Convert satellite photos of Earth into texture maps on a sphere (OpenGL ES)

关于algorithm - 将平面网格变形为球体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39440390/

相关文章:

c - 检查数字是否可以被 3 整除的逻辑?

java - 计算具有较大 xy 值的点之间的距离

c++ - 为什么 C++ 将范数定义为欧几里得范数的平方?

3d 到 2d 投影矩阵

algorithm - 欧拉计划 - 68

java - 内存似乎没有按预期工作

Hibernate:标准中多对一的投影

ios - 修改相机的透视投影

algorithm - 具有大量局部最小值的多参数优化

python - 算法,列表元素之间的最近点