前言
这一部分我主要是写一下大致的思路,然后给一个我的代码,由于代码写得有点混乱,文档的思路也没有完全写清楚,大家凑和着看吧,有问题可以再讨论
问题描述
全景球如果直接画在地球上,那么效果是画在不同经纬度位置的球朝向是一致的,只要平移球心就可以完全吻合。说白了,如果全景球在北极显示的是天空朝头上,那么在南极画的时候就天空朝脚下了。如图(图中箭头指示天空的方向)因此需要有一种方法,实现针对不同的经纬度位置,对该处显示的全景球加以翻转。
根据在移动测量中通常采用的平面坐标系(北方为Y轴、头顶的方向为Z轴)的情况,建立平面坐标系XYZaxis的方式表示。XYZ坐标系表示地球坐标系。
在这里,Yaxis的定义是:北极点N和全景球心点P的连线在地平面(即通过点P与地球相切的平面)的投影。因此,问题归结为3步来解决
(1) 计算出地面坐标系的三个轴的方向
(2) 找到一个变换矩阵,使得一个点Q在地球坐标系的坐标与它经过这个变换以后的新坐标点Q’在地面坐标系中相同。
(3) 按照(2)的变换矩阵,对全景球面的各个点坐标做变换,那么就可以保证变换后全景球的位置在P点,朝向是天空朝头顶,且以前的XY轴对应的变换到新的Xaxis Yaxis位置。
计算原理:
(1)计算地面坐标系的三个轴的方向
设北极点N在过P点的切平面上的投影点为G,则必有向量NG平行于Zaxis方向,可以设G点坐标为 (N.x,N.y,N.z)+(Zaxis.x,Zaxis.y,Zaxis.z)*t 其中t是一个待定的系数,而N点坐标容易知道,Zaxis的坐标就是要放置全景球的位置,也是已知的。这个t怎么确定呢?需要再用到一个条件,即向量PG垂直于Zaxis,因为内积为0.于是有
( (N.x,N.y,N.z)+(Zaxis.x,Zaxis.y,Zaxis.z)*t –(Zaxis.x,Zaxis.y,Zaxis.z)) *
(Zaxis.x,Zaxis.y,Zaxis.z) = 0 (1)
据此可以计算出G点位置,进而计算Yaxis方向的单位向量,而Xaxis方向的单位向量可以通过Zaxis和Yaxis做外积再归一化得到。
(2)>找到一个变换矩阵,使得一个点Q在地球坐标系的坐标与它经过这个变换以后的新坐标点Q’在地面坐标系中相同。
至于这个变换矩阵得到的原理,参考《两个直角坐标系间坐标变换关系的研究》
另外,绘制全景球的原理,参考《基于openGL的全景图片三维浏览技术研究与实现》
osg::Node *makePanoView(osg::Vec3d v3d ,osg::Vec3d north ) { //输入north时北极点的三维坐标 //输入v3d是全景球球心的坐标 //中间变量有Xaxis Yaxis Zaxis double t=1-(v3d.x()*north.x()+v3d.y()*north.y()+v3d.z()*north.z())/(v3d.x()*v3d.x()+v3d.y()*v3d.y()+v3d.z()*v3d.z()); //计算Yaxis的方向 osg::Vec3d yAxis,zAxis(v3d);//Zaxis就是点P处的法向量,其实值就是P点的坐标//值 yAxis._v[0]=north.x()+v3d.x()*(t-1); yAxis._v[1]=north.y()+v3d.y()*(t-1); yAxis._v[2]=north.z()+v3d.z()*(t-1); // 由公式(1)计算Yaxis osg::Vec3d xAxis; guiyi(yAxis);guiyi(zAxis); //归一化的意思是把向量变成单位向量 waiji(yAxis,zAxis,xAxis); //由yaxis和Zaxis计算外积,得到Xaxis的方向 guiyi(xAxis); double trans_mat[3][3]; for(int i=0;i<3;i++) { trans_mat[0][i]=xAxis._v[i]; trans_mat[1][i]=yAxis._v[i]; trans_mat[2][i]=zAxis._v[i]; } //把归一化后的 Xaxis、 yaxis和Zaxis的坐标摆成一个矩阵,就是我们想要的旋转矩阵,//我们接下来要做的,是加上平移变换 int ox=v3d.x(),oy=v3d.y(),oz=v3d.z(); int i, j; float lev[] = {0.0,15.0,30.0,45.0,60.0,75.0,90.0,105.0, 120.0,135.0, 150.0, 165,180.0 }; float x, y, z; float alpha, theta; float radius = 100; int nlev = sizeof( lev )/sizeof(float); osg::Geometry *geom = new osg::Geometry; osg::Vec3Array& coords = *(new osg::Vec3Array(19*nlev)); osg::Vec4Array& colors = *(new osg::Vec4Array(19*nlev)); osg::Vec2Array& tcoords = *(new osg::Vec2Array(19*nlev)); int ci = 0; for( j = 0; j <= 18; j++ ) { alpha = osg::DegreesToRadians(lev[nlev-i-1]); theta = osg::DegreesToRadians((float)(j*20)); double x0 = radius * sin( alpha ) * cosf( theta ); double z0 = radius * sin( alpha ) * sinf( theta ); double y0 = radius * cos( alpha ); x=x0; y=z0; z=-y0; double p[3]={x,y,z}; double d[3]; Array_Multipy31(trans_mat,p,d); //相当于d=trans_mat*p coords[ci][0] = d[0]+ox; coords[ci][1] = d[1]+oy; coords[ci][2] = d[2]+oz; tcoords[ci][0] = (float)j/18.0; tcoords[ci][1] = (float)(nlev-1-i)/(float)(nlev-1); ci++; } } //省略若干行 return geode; }