分享

实现opengl 中gluLookAt函数

 南氏珍藏 2014-08-22

opengl Distilled 中介绍了view 坐标转换的原理,先列一下原著中的介绍:

The view transformation transforms vertices into eye coordinates and is the inverse of atransformation to place and orient the viewer in world coordinates. The view transformation hastwo components: a 4 x 4 orientation matrix, O, and a 4 x 4 translation matrix, T. The viewtransformation matrix, V, is the concatenation of the two 4 x 4 matrices:V = OTThe orientation matrix O is an orthonormal basis derived from the view direction and upvectors. Given an [ x y z ] view direction d and an [ x y z ] up vector u, compute their crossproduct c as:c = d x uDiscard u and compute the actual up vector u' as:u' = c x dNormalize d, c, and u' to unit length, and create the orientation matrix O as follows:

The translation matrix T is simply the inverse translation to the eye location. If you alreadyhave the eye location stored in your application, you can use it directly in a call to glTranslated(), as shown in the code below. If you don't have the eye location, scale the view directionvector by the distance to the eye, negate it, and add it to the center location. The result is the

eye location.So if an application creates a double-precision orientation matrix called orientMatrix using thesteps above and has the eye location stored in a double-precision [ x y z ] array called eye, thecode to create the view transformation isglMatrixMode( GL_MODELVIEW );glLoadMatrixd( orientMatrix );glTranslated( -eye[0], -eye[1], -eye[2] );In fact, gluLookAt() does essentially the same thing internally. It computes the view directionby subtracting eye from center and then follows the above method to create the orientationmatrix. For this reason, if your application already stores the center location, view direction,and view distance, gluLookAt() is slightly less efficient than creating the view transformation inyour application code.

下面我大体翻译一下:

视图坐标转换实际上就是把定点的坐标转换进眼睛(摄像机)坐标系中,这个操作与将眼睛转换到相应位置并设定朝向的操作相反。

来完成视图变换操作的矩阵V由两部分矩阵连乘形成的,一部分是朝向矩阵O,一部分是平移矩阵T,所以我们有:

V= OT;

朝向矩阵是由视线朝向D和眼睛的上方向U决定的,这两个方向向量叉乘得到向量C,向量C和向量D叉乘得到U‘,C,U,D(对应x,y,z),形成了眼睛坐标系的三个方向,它们属于右手坐标系,

相关的公式如下:

c = d x u,

u' = c x d,

将得到的三个变量相应的单位化,分别用这三个变量传到下面的矩阵中,就得到了转换需要的朝向矩阵O:

,注意向量d的各个分量都被取了原值的负值。

而平移矩阵,则是将眼睛移动操作的反操作,如果你有眼睛的位置,直接将眼睛的位置全取原值负值就OK了,否则可以用Center位置加一个向量就得到眼睛的位置了。

有了上面的处理后,后面的处理就很简单了,将矩阵加载的opengl中,根据眼睛的位置平移就OK了。代码如下:

glMatrixMode( GL_MODELVIEW );glLoadMatrixd( orientMatrix );glTranslated( -eye[0], -eye[1], -eye[2] );

实际上glLookAt做了一样的事情。

下面上我的代码:


  1. class FVector3  
  2. {  
  3. public:  
  4.     union  
  5.     {  
  6.         struct  
  7.         {  
  8.             GLdouble X, Y, Z;  
  9.         };  
  10.         struct  
  11.         {  
  12.             GLdouble x, y, z;  
  13.         };  
  14.         GLdouble v[3];  
  15.     };  
  16. public:  
  17.     FVector3(){}  
  18.     FVector3(GLdouble x1,GLdouble y1,GLdouble z1):x(x1),y(y1),z(z1)  
  19.     {}  
  20.     FVector3(const FVector3& InV);  
  21.   
  22.     FVector3 operator^(const FVector3& V) const;  
  23.     FVector3& Normalize();  
  24.   
  25. };  
  26.   
  27. inline  
  28. FVector3::FVector3(const FVector3& InV)  
  29. {  
  30.     x = InV.x;  
  31.     y = InV.y;  
  32.     z = InV.z;  
  33. }  
  34.   
  35. inline GLdouble appInvSqrt(GLdouble f) { return 1.f/sqrt(f); }  
  36.   
  37. inline FVector3&  
  38. FVector3::Normalize()  
  39. {  
  40.     GLdouble SquareSum = X*X + Y*Y + Z*Z;  
  41.     if( SquareSum < 0.000001 )  
  42.         return *this;  
  43.     GLdouble Scale = appInvSqrt(SquareSum);  
  44.     X *= Scale;  
  45.     Y *= Scale;  
  46.     Z *= Scale;  
  47.     return *this;  
  48. }  
  49.   
  50. inline FVector3   
  51. FVector3::operator^( const FVector3& V ) const  
  52. {  
  53.     return FVector3(Y * V.Z - Z * V.Y, Z * V.X - X * V.Z, X * V.Y - Y * V.X );  
  54. }  

上面的是工具类,下面是实现函数:

  1. static void suLookAt(GLdouble eyeX,GLdouble eyeY,GLdouble eyeZ,GLdouble centerX,GLdouble centerY,GLdouble centerZ,GLdouble upX,GLdouble upY,GLdouble upZ)  
  2. {  
  3.     GLdouble directMat[16];  
  4.   
  5.     for (int i = 0 ;i<16;i++)  
  6.     {  
  7.         directMat[i] = 0;  
  8.     }  
  9.   
  10.     directMat[15]= 1;  
  11.     FVector3 fvDirect(centerX-eyeX,centerY-eyeY,centerZ-eyeZ);  
  12.     fvDirect.Normalize();  
  13.     FVector3 fvUpD(upX,upY,upZ);  
  14.     fvUpD.Normalize();  
  15.     FVector3 fvC = fvDirect^fvUpD;  
  16.     fvC.Normalize();  
  17.       
  18.     FVector3 fvUp = fvC^fvDirect;  
  19.     fvUp.Normalize();  
  20.       
  21.     fvDirect.x = -fvDirect.x;  
  22.     fvDirect.y = -fvDirect.y;  
  23.     fvDirect.z = -fvDirect.z;  
  24.   
  25.     directMat[0] = fvC.x;  
  26.     directMat[4] = fvC.y;  
  27.     directMat[8] = fvC.z;  
  28.     directMat[1] = fvUp.x;  
  29.     directMat[5] = fvUp.y;  
  30.     directMat[9] = fvUp.z;  
  31.     directMat[2] = fvDirect.x;  
  32.     directMat[6] = fvDirect.y;  
  33.     directMat[10] = fvDirect.z;  
  34.   
  35.     glLoadMatrixd(directMat);  
  36.       
  37.     glTranslated(-eyeX,-eyeY,-eyeZ);  
  38. }  


调用方法跟gluLookAt一样


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多