再会,
目前我正在尝试将飞机弯曲成球体。我已经准备好将墨卡托投影与lla 一起尝试到 ecef。所以结果会延迟,但它不像球体(半球体)。最成功的变体看起来像这样(更像是一个帐篷,而不是半个球体):
这个帐篷的代码(pastebin)。我正在使用three.js 进行渲染。
所以我在寻求一些建议。我做错了什么?
再会,
目前我正在尝试将飞机弯曲成球体。我已经准备好将墨卡托投影与lla 一起尝试到 ecef。所以结果会延迟,但它不像球体(半球体)。最成功的变体看起来像这样(更像是一个帐篷,而不是半个球体):
这个帐篷的代码(pastebin)。我正在使用three.js 进行渲染。
所以我在寻求一些建议。我做错了什么?
使用球坐标系。角度long,lat
是平面中的2D线性u,v
坐标,输出是3D x,y,z
。
将平面网格的顶点(点)转换为球面
我怀疑你在形式上得到了积分,(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
现在转换为球体角度:
long=6.2831853*u/u_size_of_mesh
lat =3.1415926*v/v_size_of_mesh
(x,y,z)
最后在球面上计算 new :
x = R*cos(lat)*cos(long)
y = R*cos(lat)*sin(long)
z = R*sin(lat)
网
平面网格必须具有足够密集的点结构(足够多的三角形/面),否则球体将不会看起来应有的样子。另一个问题是平面网格确实有边,而球面没有。Ti s 可能会在平面边缘连接的球体表面上产生外观/间隙。如果您想避免这种情况,您可以在平面网格的相对两侧的边缘之间添加面以填充间隙,或者完全丢弃您的网格并使用均匀的点网格重新采样平面。
如果您想完全重新采样您的网格,那么您可以做的最好的事情是首先创建常规球体网格,例如:
然后通过逆过程计算平面上的对应点到#1,这样你就可以插值点的其他参数(如颜色、纹理坐标等)
[笔记]
如果要对此进行动画处理,则只需在原始平面点P0(x,y,z)
和相应的球体表面点之间使用线性插值P1(x,y,z)
,动画参数t=<0.0,1.0>
如下:
P = P0 + (P1-P0)*t
如果t=0
则输出为平面网格,否则t=1
为球体。介于两者之间的任何地方都是包装过程,因此t
以1
足够小的步长(如0.01
)递增并在某个计时器中渲染...
[Edit1] U,V 基向量
想法很简单,获取 2 个非平行向量并更改其中一个,使其垂直于第一个但仍位于同一平面上。
取任何网格面
例如三角形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
使它们垂直
因此,保留一个向量(例如U
)并计算另一个向量,使其垂直。为此,您可以使用交叉产品。两个单位向量的叉积是垂直于两者的新单位向量。两种可能性中的哪一种仅取决于操作数 ( (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)
这W
只是一个临时向量(在图像中它被称为V'
)btw 它是表面的法线向量。
大小和对齐
现在,由于我没有关于您的网格的更多信息,因此我不知道它的形状、大小等......理想的情况是网格是矩形并且U,V
矢量与其边缘对齐。在这种情况下,您只需按每个方向的矩形大小对坐标进行归一化(如左上图所示)。
如果您的网格不是这样,并且您U,V
通过这种方法从面计算,那么结果可能根本不会与边缘对齐(它们可以旋转任何角度)......
如果这种情况无法避免(通过选择角面),那么您的形状的角点将沿每个边缘具有各种坐标限制并且您需要以某种完整的方式将它们内插或映射到正确的球面间隔(不能更具体,因为我不知道你到底在做什么)。
对于几乎矩形的形状,有时可以使用边缘,U,V
即使它们不是完全垂直的。
[Edit2] 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();
}
//---------------------------------------------------------------------------
我使用了我的动态列表模板,所以:
List<double> xxx;
是相同的double xxx[];
xxx.add(5);
添加5
到列表末尾xxx[7]
访问数组元素(安全)xxx.dat[7]
访问数组元素(不安全但快速的直接访问)xxx.num
是数组的实际使用大小xxx.reset()
清除数组并设置 xxx.num=0xxx.allocate(100)
100
为项目预分配空间这个的用法是这样的:
mesh_init();
mesh_sphere();
结果如下:
左边是生成的带有噪声的平面网格,右边是转换后的结果。
该代码反映了上面的所有内容 + 将 Z - 噪声添加到球体半径以保留特征。法线以标准方式从几何图形中重新计算。对于整个 TBN 矩阵,您需要拓扑中的连接信息并从中重新计算(或利用球体几何形状并从中使用 TBN。
顺便说一句,如果你想映射到球体而不是网格转换,你应该看看相关的QA s: