从命令形式上看,在很多图形软件中,三维图元的输出只是在原来二维图元输出命令的基础上增加一个后缀3,如move的3维形式的:move-3(x,y,z),line的三维形式为:dine-3(x,y,z),Polyline的三维形式为:polyline-3(n,x-array,y_array,z_array)。实际上,要在图形输出设备上输出一个三维图元,必须进行投影变换,为与前面所提供的投影变换相一致。我们假定其视域坐标系统(Viewig coordinate System简称VCS)定义为:投影面与xoy坐标而重合;z轴正向指间要表达的物体;透视投影的中心位于z轴的负轴之上。
要将三维图元输出到图形设备上,必须通过以下步骤:
1 定义一个视域坐标系统VCS,为此需指定如下数:
·VCS坐标原点,也称View reference pont;
·VCS的z轴方向,也称view plane normal, xoy平面称为vierpin。
·VCS的y轴方向,又称view up vector。
2将投影物体的世界坐标变换为视域坐标。在完成这两个前奏步骤之后,投影便可进行。然后,从二维视域坐标转换为NDC,便可在荧光屏上显示了。后两个步骤如下:
3选择平行投影或透视投影进行投影变换。使得VCS的三维对象变为VCS中在yox平面中的二维对象。
4在VCS的yox平面中取一个窗口,并定义一个相应的视区,进行标准化变换。
上述4步构成本维图元输出的全过程,它是三个坐标系统,世界坐标、视域坐标、标准设备坐标的依次递变过程(图316)。其中标准化变换和投影变换已在上两节中介绍过。下面介绍视域变换的过程。世界坐标WCS 视域变换 视域坐标VCS 投影 变换 标准化变换 标准设备坐标
(一)视域变换
视域变换(view transformation)是将一个三维对象在世界坐标系统中的坐标转变为在视域坐标系统中的坐标过程。这一过程可以看成将WCS中的三个坐标轴转换为与VCS的三个坐标轴对应重合的逆变换。下面给出视域变换的一个统一步骤。
将一个任意方向的VCS变换到与WCS重合的过程可由四步完成:1)将左手坐标改为右手坐标系统;2)原点重合;3)z轴重合;4)其余两轴重合。其中中间两步与绕任一轴旋转中的几个步骤相同。四步应完成的运算如下:
设VCS的坐标原点为(x0,y0,z0);z轴方向矢量为(xn,yn,zn);h轴方向矢量为(xu,yu,zu);图3.34(a)示出WCS和VCS的原来情况。xv,yv,zv是VCS的三根坐标轴;xw,yw,zw是WCS的坐标轴。视域变换的过程是将xv,yv,zv变换到与xw,yw,zw三轴重合,这也就是将WCS中的物体坐标变换到VCS中去的过程。
1左手系统改为右手系统的变换由zv轴改变方向而完成,(图3.34(b))。变换矩阵为S(1,1,-1)。
2原点重合的变换是平移(图3.34(c)),为T(-x0,-y0,-z0)。
3zv轴与zw轴重合的变换为先绕xw轴旋轴VCS α角,再绕yw旋转VCSβ角(图3.34(d),(e)),运算矩阵为Rx(α)和Ry(β)的变换矩阵参数值之求法见旋转变换公式的推导。
4将VCS绕zw轴旋转γ角,使得VCS的三根轴与WCS三轴对应重合,变换矩阵为Rz(γ)。其中矩阵的参数求法如下:
设经过上述变换,yw方向的矢量为(x′u,y′u,z′u),它是单位矢量为u=(au,bu,cu),其值可以由上述变换中得出。其中cu=0,因为u在ywoxw的平面上。令yw为yw轴之单位矢量:γ为yv与yw之夹角(图3.34(e)),故有:
cosγ= =bu
其中bu=
并有:u×yw=zw|u||yw|·sinγ
由矢量积的行列式运算,又得:
u×yw=zw·au
并因为|u|·|yw|=1,因此有:
sinγ=au
其中,au=
绕z轴γ角的变换矩阵为:
Rz(γ)=
综上所述,视域变换由下述级联矩阵变换实现:
p′=p·s(1,1,-1)·T(-x0,-y0,-z0)·Rx(α)·Ry(θ)·Rz(γ)
其中,p是变换前物体上的三维齐次坐标;
p′是变换后物体上的三维齐次坐标。
(二)定义三维视域命令
在图形软件的使用中,三维物体显示所需的三级变换:视域变换、投影变换、标准化变换都由图形软件自动完成,但用户必须指定三级变换所需的参数。下面两条指令用定义三维对象的三级变换的参数:
1定义视域变换参数:
Creat_view_maatrix(x0,y0,z0,xn,yn,zn,xu,yu,zu,view_matrix);
其中,x0,y0,z0是VCS的原点坐标;
xn,yn,zn是VCS的z轴正向矢量;
xu,yu,zu是VCS的y轴正向矢量;
view_matrix是输出的视域变换矩阵。
这条指令完成如下上节所述的功能:
view_matrix=S(1,1,-1)·T(-x0,-y0,-z0)·Rx(α)·Ry(θ)·Rz(γ)
2定义投影和标准化变换的参数:
Set_view_representation(view_index,view_matrix,projection_type,xp,yp,zp,Xw-min,Xw-max,Yw-min,Yw-max,near,far,Xv-min,Xv-max,Yv-max);
其中,view_index是该变换的编号;
view_matrix是所输入的视域变换矩阵;
projection_type是投影类型(透视,或平行)的选择;
xp,yp,zp对于平行投影,它们表示投影线的方向矢量;对于透视投影,它们表示投影中心的位置;
Xw-min,Xw-max,Yw-min,Yw-max,near, far是表示窗口的大小和前(near)后(far)的深度范围;
Xv-min,Xv-max,Yv-min,Yv-max是视区的大小。
上面两条指令定义了编号为view_index视域的详细特征。在这之后,用户便可使用下面的指令来选择视域。
3Set_view_index(vi);
其中vi是已经定义了的视域的编码。
程序3.1给出了Creat_view_matrix指令的实现全过程。
##include〈stdio.h〉
##include〈math.h〉
#define MAX 10
struct pointtype{
float x;
float y;
float z;
};
#define POINT struct pointtype;
POINT point[MAX];
Creat_view_matrix(x0,y0,z0,xn,yn,zn,xu,yu,zu,p1)
float x0,y0,z0;
float xn,yn,zn;
float xu,yu,zu;
float pf1[4][4];
{
float a, b, c, d, r, r1;
float sin1, cos1, sin2, cos2, sin3, cos3;
float trans1[4][4],trans2[4][4],p2[4][4];
int i, j;
POINT point;
/*---------------------Move transformation----------------------*/
for(i=0;i<4;i++)
for(j=0;j<4;j++)
trans1[i][j]=(i==j);
trans1[0][0]=1;
trans1[0][0]=1;
trans1[2][2]=1;
trans1[3][3]=1;
trans1[0][3]=-x0;
trans1[1][3]=-y0;
trans1[2][3]=-z0;
xn-=x0;
yn-=y0;
zn-=z0;
/*------------------------From right_hand to left_hand-----------*/
for(i=0;i<4;i++)
for(j=0;j<4;j++)
trans2[i][j]=(i==j);
trans2[0][0]=1;
trans2[1][1]=1;
trans2[2][2]=-1;
trans2[3][3]=1;
zn=-zn;
accumulate_matrix(trans1,trans2,p1);
/*-----------------------Transformtion around X-------------------*/
r=sqrt(xn*xn+yn*yn+zn*zn);
a=xn/r;
b=yn/r;
c=zn/r;
d=sqrt(b*b+c*c);
sin1=b/d;
cos1=c/d;
sin2=-a;
cos2=d;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
trans1[i][j]=(i==j);
trans1[0][0]=1;
trans1[0][0]=cos1;
trans1[2][1]=sin1;
trans1[1][2]=-sin1;
trans1[2][2]=cos1;
trans1[3][3]=1;
trans1[2][3]=-z0;
accumulate_matrix(p1,trans1,p1);
*/----------------------Transformation around Y----------*/
for(i=0;i<4;i++)
for(j=0;j<4;j++)
trans1[i][j]=(i==j);
trans1[0][0]=cos2;
trans1[2][0]=-sin2;
trans1[1][1]=1;
trans1[0][2]=sin2;
trans1[2][2]=cos2;
trans1[3][3]=1;
accumulate_matrix(p1,trans1,p1);
*/---------------------------Transformtion around Z--------------*/
point.x=xu;
point.y=yu;
point.z=zu;
realize_trans(p1,&point);
xu=point.x;
yu=point.y;
zu=point.z;
r1=sqrt(xu*xu+yu*yu);
sin3=xu/r1;
cos3=yu/r1;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
trans1[i][j]=(i==j);
trans1[0][0]=cos3;
trans1[1][0]=sin3;
trans1[0][1]=-sin3;
trans1[1][1]=cos3;
trans1[2][2]=1;
trans1[3][3]=1;
accumulate_matrix(p1,trans1,p1);
realize_trans(m,p)
}
realize_trans(m,p)
POINT *p;
float m[4][4];
{
float x,y,z;
x=(m[0][0]p->x+m[1][0]*p->y+m[2][0]*p
->z+m[3][0]);
y=(m[0][1]p->x+m[1][1]*p->y+m[2][1]*p
->z+m[3][1]);
z=(m[0][2]p->x+m[1][2]*p->y+m[2][2]*p
->z+m[3][2]);
p->x=x;
p->y=y;
p->z=z;
}
accumulate_matrix(m1,m2,m3)
float m1[4][4],m2[4][4],m3[4][4];
{
flaot m[4][4];
int i,j,k;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
m[i][j]=m1[i][j];
for(i=0;i<4;i++)
for(j=0;j<4;j++){
m3[i][j]=0;
for(k=0,k<4,k++)
m3[i][j]+=m[k][j]*m2[i][k];
}
}