这个东西写出来,基本上从程序的效果上看应该没有问题,可以实现一个有6个自由度的相机,对地面拍摄的程序。但是里面的素材很渣,没有光线所以谨慎观看。
先说为什么写这个,很久以前当我发现写程序其实跟写作文没区别的时候,我发现我自己错了,后来学一些计算机图形学,至少用到了一些线性代数的知识,这个立刻与写作文区别开了。这里的6个自由度的相机其实只是把线性代数的乘法应用了一下,只是简单的应用一下乘法。
先说一下,下面的公式我参考了这个思密达,好像是一个挺不错的哥们。可以看他关于旋转矩阵的解说 http://www.songho.ca/opengl/gl_anglestoaxes.html
但是他只给出了关于绕X,Y,Z旋转轴旋转的变化矩阵,而且如果旋转的顺序不同,得到的结果也不同,这个大家可以考虑一个相机镜头先绕X轴旋转90度,再绕Y轴旋转90度的所看到的景观与先绕Y轴旋转90度,再绕X轴旋转90度的看到景观不同。理论上的解释是,如果A,B,C,D四个坐标系,A经过M1转换到B,B进过M2到C,C进过M3到D,那么一个A坐标的点P1需要进过M1,M2,M3这3个坐标系转换到达D坐标系P2,所以:
P2=P1*M1*M2*M3
由于矩阵乘法不具备交换交换律,(其实很好证明,大家随便拿两个矩阵算一下就可以得到一个反例)所以当你想先绕X轴转动60度,再绕Y轴转动90,又绕Z轴转动30度可以利用下面的方法来做。
一共6种排序组合。所以当你已知转动顺序和转动角度的时候完全可以用这种方法。
但是在现实中,转动的顺序你提前并不知道,所以只能通过增量的方法来实现对绕任意轴任意角度进行变化。那这个方法其实也很简单,如下介绍,
比如我举个例子,用户首先绕Z转动90度,用户再绕Y转动90度。
用户输入 先绕Z转动90度
由于程序不知道用户行为,所以先转Z轴90度的,当前栈顶矩阵变为:
0 -1 0 0
1 0 0 0
0 0 1 0
0 0 0 0
1. 利用这个矩阵进行视模转换,渲染必要的场景
2. 保存这个当前的旋转矩阵。(关键在这里)
用户再绕Y转动90度
1. 导入上一步保存的选择矩阵,当前视图模型矩阵为:
0 -1 0 0
1 0 0 0
0 0 1 0
0 0 0 0
2. 绕Y轴90度的,当前栈顶矩阵变为:
0 -1 0 0
0 0 -1 0
1 0 0 0
0 0 0 1
3. 利用这个矩阵进行视模转换,渲染必要的场景
4. 再次保存这个当前的旋转矩阵。
上面的方法核心就是通过保存上一次的旋转矩阵,来进行下一次的视图模型转换。下面有两个代码,一个是一开始说的 http://www.songho.ca/opengl/gl_anglestoaxes.html视图模型转换代码,还有是刚刚说的这个代码。
///
// convert Euler angles(x,y,z) to axes(left, up, forward)
// Each column of the rotation matrix represents left, up and forward axis.
// The order of rotation is Roll->Yaw->Pitch (Rx*Ry*Rz)
// Rx: rotation about X-axis, pitch
// Ry: rotation about Y-axis, yaw(heading)
// Rz: rotation about Z-axis, roll
// Rx Ry Rz
// |1 0 0| | Cy 0 Sy| |Cz -Sz 0| | CyCz -CySz Sy |
// |0 Cx -Sx|*| 0 1 0|*|Sz Cz 0| = | SxSyCz+CxSz -SxSySz+CxCz -SxCy|
// |0 Sx Cx| |-Sy 0 Cy| | 0 0 1| |-CxSyCz+SxSz CxSySz+SxCz CxCy|
///</span>
void anglesToAxes2(const Vector3 angles, Vector3& left, Vector3& up, Vector3& forward)
{
float sx, sy, sz, cx, cy, cz, theta;
theta = angles.x * DEG2RAD;
sx = sinf(theta);
cx = cosf(theta);
theta = angles.y * DEG2RAD;
sy = sinf(theta);
cy = cosf(theta);
theta = angles.z * DEG2RAD;
sz = sinf(theta);
cz = cosf(theta);
left.x = cy*cz;
left.y = sx*sy*cz + cx*sz;
left.z = -cx*sy*cz + sx*sz;
up.x = -cy*sz;
up.y = -sx*sy*sz + cx*cz;
up.z = cx*sy*sz + sx*cz;
forward.x = sy;
forward.y = -sx*cy;
forward.z = cx*cy;
}
/************************************************************************/
/* the increment version method */
/************************************************************************/
void anglesToAxes(Vector3& angles,Vector3& left, Vector3& up, Vector3& forward){
//save the current gl_modelview_matrix
//calculate the rotate matrix by using the opengl
glPushMatrix();
glLoadMatrixf(mat);
log("the increment process ");
printCurrentMatrix(GL_MODELVIEW_MATRIX);
if ((angles.x>0)||(angles.x<0))
{
glRotated(angles.x,1,0,0);
}else if((angles.y>0)||(angles.y<0)){
glRotated(angles.y,0,1,0);
}else if((angles.z>0)||(angles.z<0)){
glRotated(angles.z,0,0,1);
}else{}
angles.x=0;
angles.y=0;
angles.z=0;
glGetFloatv(GL_MODELVIEW_MATRIX, mat);
log("the increment process ");
printCurrentMatrix(GL_MODELVIEW_MATRIX);
glPopMatrix();
//update the An
left.x=mat[0];
left.y=mat[1];
left.z=mat[2];
up.x=mat[4];
up.y=mat[5];
up.z=mat[6];
forward.x=mat[8];
forward.y=mat[9];
forward.z=mat[10];
}