algorithm - Deforming plane mesh to sphere -
good day,
currently i'm trying bend plane sphere. i've ready tried mercator projection lla ecef. result defers isn't sphere (half sphere). successful variant looked (more tent, not half sphere):
code tent (pastebin). i'm using three.js rendering.
so i'm asking advice. i'm doing wrong?
use spherical coordinate system. angles long,lat
2d linear u,v
coordinates in plane , output 3d x,y,z
.
convert vertexes (points) of planar mesh sphere surface
i suspect got points in form
(x,y,z)
need first computeu,v
. letu,v
perpendicular unit basis vectors lying on plane. can obtained substracting 2 point on plane mesh , normalizing size , exploiting cross product ensure perpendicularity. so: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
now convert sphere angles:
long=6.2831853*u/u_size_of_mesh lat =3.1415926*v/v_size_of_mesh
and compute new
(x,y,z)
on sphere surface:x = r*cos(lat)*cos(long) y = r*cos(lat)*sin(long) z = r*sin(lat)
mesh
the planar mesh must have dense enough points structure (enough triangles/faces) otherwise sphere not should. problem planar mesh have edges , sphere surface not. ti s possibly create seems/gaps on surface of sphere edges of plane connect. if want avoid can either add faces between edges on opposite sides of plane mesh fill gaps or throw away mesh , re-sample plane uniform grid of points.
if want re-sample mesh best can first create regular sphere mesh example with:
sphere triangulation mesh subdivision
and compute corresponding point on plane inverse process #1 can interpolate other parameters of points (like color, texture coordinate etc)
[notes]
if want animate use linear interpolation between original plane point p0(x,y,z)
, corresponding sphere surface point p1(x,y,z)
animation parameter t=<0.0,1.0>
this:
p = p0 + (p1-p0)*t
if t=0
output planar mesh else if t=1
sphere. anywhere in between wrapping process increment t
1
small enough step (like 0.01
) , render in timer ...
[edit1] u,v basis vectors
the idea simple obtain 2 non parallel vectors , change 1 of them perpendicular first still on same plane.
take mesh face
for example triangle abc
compute 2 nonzero non parallel vectors on plane
that easy substract 2 pairs of vertexes example:
u.x=b.x-a.x u.y=b.y-a.y v.x=c.x-a.x v.y=c.y-a.y
and make them unit in size divide them size
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
make them perpendicular
so left 1 vector (for example
u
) , compute other perpendicular. can use cross product. cross product of 2 unit vectors new unit vector perpendicular both. 1 2 possibilities depends on order of operands ((u x v) = - (v x u)
) example:// w perpendicular 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 perpendicular 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)
the
w
temporary vector (in image calledv'
) btw normal vector surface.size , alignment
now not have more info mesh not know shape, size etc... ideal case if mesh rectangular ,
u,v
vectors aligned edges. in such case normalize coordinates rectangle size in each direction (as in above image on left).if mesh not , computing
u,v
face approach result may not aligned edges @ (they can rotated angle)...if such case can not avoided (by selecting corners face) corner points of shape have various coordinate limits along each edges , need interpolate or map them correct spherical interval in meaning full way (can't more specific have no idea doing).
for rectangular shapes ok use edges
u,v
if not perpendicular each other.
[edit2] c++ example
well if got aligned square shape mesh noise in z axis (like height map) how mesh conversion:
//--------------------------------------------------------------------------- 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]; (f=&fac[0],i=0;i<fac.num;i++,f++) { vector_sub(a,pnt[f->i1].xyz,pnt[f->i0].xyz); // = pnt1 - pnt0 vector_sub(b,pnt[f->i2].xyz,pnt[f->i0].xyz); // b = pnt2 - pnt0 vector_mul(a,a,b); // = x b vector_one(f->nor,a); // nor = / |a| } } //--------------------------------------------------------------------------- void mesh_init() // generate plane mesh (your square 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 avoid realocation (p.xyz[0]=-1.0,u=0;u<n;u++,p.xyz[0]+=d) // x=<-1.0,+1.0> (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); (u=1;u<n;u++) (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 sphere { int i; _pnt *p; double u,v,lon,lat,r,r=1.0; // know generated mesh aligned so: double u[3]={ 1.0,0.0,0.0 }; double v[3]={ 0.0,1.0,0.0 }; (p=&pnt[0],i=0;i<pnt.num;i++,p++) // process points { // u,v coordinates u=vector_mul(p->xyz,u); v=vector_mul(p->xyz,v); // know limits <-1,+1> conversion 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 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); (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(); } //---------------------------------------------------------------------------
i used mine dynamic list template so:
list<double> xxx;
samedouble xxx[];
xxx.add(5);
adds5
end of listxxx[7]
access array element (safe)xxx.dat[7]
access array element (unsafe fast direct access)xxx.num
actual used size of arrayxxx.reset()
clears array , set xxx.num=0xxx.allocate(100)
preallocate space100
items
the usage of this:
mesh_init(); mesh_sphere();
here results:
on left generated planar mesh noise , on right result after conversion.
the code reflects stuff above + add z - noise sphere radius preserve features. normals recomputed geometry in standard way. whole tbn matrix need connection info topology , recompute (or exploit sphere geometry , use tbn it.
btw if want mapping onto sphere instead of mesh conversion should take @ related qas:
Comments
Post a Comment