将平面网格变形为球体

Que*_*ino 1 algorithm math projection mercator

美好的一天,

目前,我正在尝试将飞机弯曲到一个球体。我已经准备好尝试将Mercator投影lla一起使用到efef。因此结果有所延缓,但它不像球形(半球形)。最成功的变体如下所示(更像是帐篷,而不是半球):

在此处输入图片说明

该帐篷的代码(pastebin)。我正在使用three.js进行渲染。

因此,我需要一些建议。我做错了什么?

Spe*_*tre 5

使用球坐标系。角度long,lat是平面中的2D线性u,v坐标,输出是3D x,y,z

  1. 将平面网格的顶点(点)转换为球面

    我怀疑您的表格有积分,(x,y,z)因此您需要先计算u,v。设U,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
    
    Run Code Online (Sandbox Code Playgroud)

    现在转换为球面角:

    long=6.2831853*u/u_size_of_mesh
    lat =3.1415926*v/v_size_of_mesh
    
    Run Code Online (Sandbox Code Playgroud)

    最后(x,y,z)在球面上计算新的:

    x = R*cos(lat)*cos(long)
    y = R*cos(lat)*sin(long)
    z = R*sin(lat)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 啮合

    平面网格必须具有足够密集的点结构(足够的三角形/面),否则球体将无法正常显示。另一个问题是平面网格确实没有边缘和球体表面。Ti s可能会在连接平面边缘的球体表面上产生外观/间隙。如果要避免这种情况,可以在平面网格的相对两侧的边缘之间添加面以填充间隙,也可以完全抛弃网格并使用均匀的点网格重新采样平面。

    如果要完全重新采样网格,则最好的方法是首先创建常规球体网格,例如:

    通过网格细分对球进行三角剖分

    然后通过逆过程计算到#1平面上的对应点,以便您可以插值点的其他参数(例如颜色,纹理坐标等)

[笔记]

如果要对此进行动画处理,则只需在原始平面点P0(x,y,z)和相应的球面点之间P1(x,y,z)使用带有动画参数的线性插值,t=<0.0,1.0>如下所示:

P = P0 + (P1-P0)*t
Run Code Online (Sandbox Code Playgroud)

如果是,t=0则输出为平面网格,否则t=1为球面。包装过程之间的任何地方都可以,因此请t1足够小的步长(例如0.01)递增,并在某个计时器中进行渲染...

[Edit1] U,V基向量

这个想法很简单,获得2个非平行向量并更改其中一个向量,使其垂直于第一个但仍在同一平面上。

  1. 采取任何网状的面孔

    例如三角形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
    
    Run Code Online (Sandbox Code Playgroud)

    并使其尺寸统一,因此将其除以尺寸

    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
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使它们垂直

    因此,一个向量保持不变(例如U),然后计算另一个向量,使其垂直。为此,您可以使用交叉产品。两个单位向量的叉积是垂直于两个单位向量的新单位向量。这2种可能性中的哪一种仅取决于操作数((U x V) = - (V x 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)
    
    Run Code Online (Sandbox Code Playgroud)

    W只是暂时的矢量(图像中它被称为V')顺便说一句它是法向矢量的表面。

    基本向量

  4. 大小和对齐方式

    现在,因为我没有有关您的网格的更多信息,所以我不知道其形状,大小等信息。理想的情况是网格是矩形且U,V矢量与边缘对齐。在这种情况下,您只需按每个方向上的矩形大小对坐标进行归一化(如左图所示)。

    如果您的网格不是这样,并且您是U,V通过这种方法从面计算的,那么结果可能根本不与边缘对齐(它们可以旋转任何角度)...

    不结盟

    如果无法避免这种情况(通过选择拐角面),则形状的拐角点将在每个边缘上具有各种坐标限制,并且您需要以某种完整的含义将它们插值或映射到正确的球面间隔(不能更具体一点,因为我不知道您到底在做什么。

    对于几乎为矩形的形状,有时可以使用边缘,U,V即使它们彼此之间并非完全垂直。

[Edit2] C ++示例

好吧,如果您得到的Z轴上有一些噪声的完全对齐的方形网格(如高度图),那么这就是我进行网格转换的方式:

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
Run Code Online (Sandbox Code Playgroud)

我使用了我的动态列表模板,因此:

  • List<double> xxx; 是相同的 double xxx[];
  • xxx.add(5);添加5到列表的末尾
  • xxx[7] 访问数组元素(安全)
  • xxx.dat[7] 访问数组元素(不安全但快速直接访问)
  • xxx.num 是数组的实际使用大小
  • xxx.reset() 清除数组并设置xxx.num = 0
  • xxx.allocate(100)100项目预分配空间

用法如下:

mesh_init();
mesh_sphere();
Run Code Online (Sandbox Code Playgroud)

结果如下:

概观

左侧是生成的带有噪声的平面网格,右侧是转换后的结果。

该代码反映了以上所有内容,并在球体半径上添加了Z-噪声以保留要素。以标准方式从几何体重新计算法线。对于整个TBN矩阵,您需要拓扑中的连接信息并从中进行重新计算(或利用球体几何体并从中使用TBN。

顺便说一句,如果要映射到球体而不是网格转换,则应查看相关的质量检查