OpenGL中Camera类的设计以及使用

来源:互联网 发布:java游戏编程入门pdf 编辑:程序博客网 时间:2024/06/02 04:07

简介:

       首先,camera类是什么?它相当于摄像机镜头,通过设定合适的位置和角度可以实现对3D绘制图形的观察,而camera类的优势就在于可以围绕绘制的物体进行漫游,唯一需要完成的工作就是如何使用该类的操作完成有效的漫游。

       很多带3D图形显示的软件其实都有自己的camera类,很多时候我们可以使用鼠标即可完成3D图形的各个方向360度的观察,并且最好还可以拉近或者拉远摄像头来实现对模型的缩放功能。本章内容通过对自己近期的实践展示源自《计算机图形学》中基于openGL的一个camera类以及操作方法来实现便捷的绕物体各方位观察和缩放。

工作内容:

       使用了openGL的相关库,对书中类的函数进行的重排版实现,在QT环境中运行对已有模型进行了测试,运行效果良好。虽然是在QT中测试的,但是移植到其他环境中只要加入相关的openGL库都是没有问题的。

       本文默认你已经对camera的属性有一定了解,其中eye指的是摄像头在世界坐标系中的坐标,look指的是摄像头所望的方向的一点,up指的是摄像头上方一坐标,初始化的时候设定好这三个点,然后计算出初始的u、v、n单位三个向量。camera类可以实现的操作有平移和旋转,以摄像头为中心有上述三个两辆垂直的向量作为坐标轴,所以一共有三个平移和三个旋转操作,也就是六个自由度。roll对应的是翻滚角,yaw对应的是偏航角,pitch对应的是俯仰角。每种操作有两个方向,所以其实一共12种可能的变化。如果用键盘操作则需要12个键盘,这相当不好控制。

       所以本文使用鼠标的两个键即实现了上述功能,并具有较小局限性,具体原理将在后面介绍。下面贴出camera.h和camera.cpp的代码,其中有详细注释。

camera.h文件

[cpp] view plain copy
  1. #ifndef CAMERA_H  
  2. #define CAMERA_H  
  3.   
  4. #include <QtOpenGL>  
  5. #include <math.h>  
  6.   
  7. class Point3  
  8. {  
  9. public:  
  10.     float x,y,z;  
  11.     void set(float dx,float dy,float dz)  
  12.     {  
  13.         x=dx; y=dy; z=dz;  
  14.     }  
  15.     void set(Point3& p)  
  16.     {  
  17.         x=p.x; y=p.y; z=p.z;  
  18.     }  
  19.     Point3(float xx,float yy,float zz)  
  20.     {  
  21.         x=xx; y=yy; z=zz;  
  22.     }  
  23.     Point3()  
  24.     {  
  25.         x=y=z=0;  
  26.     }  
  27.     void build4tuple(float v[])  
  28.     {  
  29.         v[0]=x; v[1]=y; v[2]=z; v[3]=1.0f;  
  30.     }  
  31. };  
  32.   
  33. class Vector3  
  34. {  
  35. public:  
  36.     float x,y,z;  
  37.     void set(float dx,float dy,float dz)  
  38.     {  
  39.         x=dx; y=dy; z=dz;  
  40.     }  
  41.     void set(Vector3& v)  
  42.     {  
  43.         x=v.x; y=v.y; z=v.z;  
  44.     }  
  45.     void flip()  
  46.     {  
  47.         x=-x; y=-y; z=-z;  
  48.     }  
  49.     void setDiff(Point3& a,Point3& b)  
  50.     {  
  51.         x=a.x-b.x; y=a.y-b.y; z=a.z-b.z;  
  52.     }  
  53.     void normalize()  
  54.     {  
  55.         float base=pow(x,2)+pow(y,2)+pow(z,2);  
  56.         x=x/pow(base,0.5);  
  57.         y=y/pow(base,0.5);  
  58.         z=z/pow(base,0.5);  
  59.     }  
  60.     Vector3(float xx, float yy, float zz)  
  61.     {  
  62.         x=xx; y=yy; z=zz;  
  63.     }  
  64.     Vector3(Vector3 &v)  
  65.     {  
  66.         x=v.x; y=v.y; z=v.z;  
  67.     }  
  68.     Vector3()  
  69.     {  
  70.         x=0; y=0; z=0;  
  71.     }  
  72.   
  73.     Vector3 cross(Vector3 b)  
  74.     {  
  75.         float x1,y1,z1;  
  76.         x1=y*b.z-z*b.y;  
  77.         y1=z*b.x-x*b.z;  
  78.         z1=x*b.y-y*b.x;  
  79.         Vector3 c(x1,y1,z1);  
  80.         return c;  
  81.     }  
  82.   
  83.     float dot(Vector3 b)  
  84.     {  
  85.         float d=x*b.x+y*b.y+z*b.z;  
  86.         return d;  
  87.     }  
  88. };  
  89.   
  90. class Camera  
  91. {  
  92. public:  
  93. /* 构造函数和析构函数 */  
  94. Camera();  
  95. ~Camera();  
  96.   
  97. /* 设置摄像机的位置, 观察点和向上向量 */  
  98. void setCamera( float eyeX, float eyeY, float eyeZ,  
  99.                 float lookX, float lookY, float lookZ,  
  100.                 float upX, float upY, float upZ);  
  101. void roll(float angle);  
  102. void pitch(float angle);  
  103. void yaw(float angle);  
  104. void slide(float du, float dv, float dn);  
  105. float getDist();  
  106. void setShape(float viewAngle,float aspect,float Near,float Far);  
  107.   
  108.   
  109. private:  
  110. /* 摄像机属性 */  
  111. Point3         eye,look,up;  
  112. Vector3        u,v,n;  
  113. float          viewAngle, aspect, nearDist, farDist;  
  114. void           setModelViewMatrix();  
  115.   
  116. };  
  117.   
  118. #endif //__CAMERA_H__  

       其中inlude的QtOpenGL文件是QT中封装的opengl类,如果不是在QT下运行,则需要inlude "gl\glut.h" 。Point3和Vector3类的定义主要是考虑camera中坐标点和向量的使用而设计。

camera.cpp文件:

[cpp] view plain copy
  1. #include "camera.h"  
  2.   
  3.   
  4.  /* 构造函数 */  
  5. Camera::Camera()   {}  
  6.   
  7. Camera::~Camera()  {}  
  8.   
  9. /* 设置摄像机的位置,朝向和向上向量 */  
  10. void Camera::setCamera( float eyeX, float eyeY, float eyeZ,  
  11.                         float lookX, float lookY, float lookZ,  
  12.                         float upX, float upY, float upZ)  
  13. {  
  14. /* 构造向量 */  
  15.     eye.set(eyeX, eyeY, eyeZ);  
  16.     look.set(lookX, lookY, lookZ);  
  17.     up.set(upX, upY, upZ);  
  18.     Vector3 upvec(up.x-eye.x,up.y-eye.y,up.z-eye.z);  
  19.   
  20. /* 计算n、u、v并归一化*/  
  21.     n.set(eye.x-look.x, eye.y-look.y, eye.z-look.z);  
  22.     u.set(upvec.cross(n).x,upvec.cross(n).y,upvec.cross(n).z);  
  23.     v.set(n.cross(u).x,n.cross(u).y,n.cross(u).z);  
  24.   
  25.     u.normalize();  
  26.     v.normalize();  
  27.     n.normalize();  
  28.     setModelViewMatrix();  
  29. }  
  30. /* 获取eye到坐标原点的距离 */  
  31. float Camera::getDist()  
  32. {  
  33.     float dist=pow(eye.x,2)+pow(eye.y,2)+pow(eye.z,2);  
  34.     return pow(dist,0.5);  
  35. }  
  36. /* 计算变换后的视点矩阵*/  
  37. void Camera::setModelViewMatrix()  
  38. {  
  39.     float m[16];  
  40.     Vector3 eVec(eye.x, eye.y,eye.z);  
  41.     m[0]=u.x; m[4]=u.y; m[8]=u.z; m[12]=-eVec.dot(u);  
  42.     m[1]=v.x; m[5]=v.y; m[9]=v.z; m[13]=-eVec.dot(v);  
  43.     m[2]=n.x; m[6]=n.y; m[10]=n.z; m[14]=-eVec.dot(n);  
  44.     m[3]=0;  m[7]=0;  m[11]=0;  m[15]=1.0;  
  45.     glMatrixMode(GL_MODELVIEW);  
  46.     glLoadMatrixf(m);     //用M矩阵替换原视点矩阵  
  47. }  
  48. /* 摄像机绕n、v、u轴旋转的计算函数*/  
  49. void Camera::roll(float angle)  
  50.   
  51. {  
  52.     float cs=cos(angle*3.14159265/180);  
  53.     float sn=sin(angle*3.14159265/180);  
  54.     Vector3 t(u);  
  55.     Vector3 s(v);  
  56.     u.set(cs*t.x-sn*s.x, cs*t.y-sn*s.y, cs*t.z-sn*s.z);  
  57.     v.set(sn*t.x+cs*s.x, sn*t.y+cs*s.y, sn*t.z+cs*s.z);  
  58.     setModelViewMatrix();          //每次计算完坐标轴变化后调用此函数更新视点矩阵  
  59. }  
  60.   
  61. void Camera::yaw(float angle)  
  62. {  
  63.     float cs=cos(angle*3.14159265/180);  
  64.     float sn=sin(angle*3.14159265/180);  
  65.     Vector3 t(n);  
  66.     Vector3 s(u);  
  67.     n.set(cs*t.x-sn*s.x, cs*t.y-sn*s.y, cs*t.z-sn*s.z);  
  68.     u.set(sn*t.x+cs*s.x, sn*t.y+cs*s.y, sn*t.z+cs*s.z);  
  69.     setModelViewMatrix();  
  70. }  
  71.   
  72. void Camera::pitch(float angle)  
  73.   
  74. {  
  75.     float cs=cos(angle*3.14159265/180);  
  76.     float sn=sin(angle*3.14159265/180);  
  77.     Vector3 t(v);  
  78.     Vector3 s(n);  
  79.     v.set(cs*t.x-sn*s.x, cs*t.y-sn*s.y, cs*t.z-sn*s.z);  
  80.     n.set(sn*t.x+cs*s.x, sn*t.y+cs*s.y, sn*t.z+cs*s.z);  
  81.     setModelViewMatrix();  
  82. }  
  83. /* 摄像机绕三个轴平移的计算函数*/  
  84. void Camera::slide(float du, float dv, float dn)  
  85. {  
  86.     eye.x+=du*u.x+dv*v.x+dn*n.x;  
  87.     eye.y+=du*u.y+dv*v.y+dn*n.y;  
  88.     eye.z+=du*u.z+dv*v.z+dn*n.z;  
  89.     look.x+=du*u.x+dv*v.x+dn*n.x;  
  90.     look.y+=du*u.y+dv*v.y+dn*n.y;  
  91.     look.z+=du*u.z+dv*v.z+dn*n.z;  
  92.     setModelViewMatrix();  
  93. }  
  94. /* 摄像机初始化*/  
  95. void Camera::setShape(float viewAngle, float aspect, float Near, float Far)  
  96. {  
  97.     glMatrixMode(GL_PROJECTION);  
  98.     glLoadIdentity();                                   //设置当前矩阵模式为投影矩阵并归一化  
  99.     gluPerspective(viewAngle,aspect, Near, Far);        //对投影矩阵进行透视变换  
  100. }  

使用:

         假设物体坐标中心在世界坐标原点,那么可以把摄像头设定在物体以外稍远的地方让方向朝向物体即可。调用cam.setCamera和cam.setShape函数来对摄像机进行初始化。

        如何用鼠标实现对类的使用呢?这里基于QT中鼠标的事件控制,按下鼠标左键沿着屏幕左右移动实现沿着u轴slide和绕着v轴yaw;上下移动实现沿着v轴slide和绕着u轴pitch。注意,每次移动鼠标都要使摄像头绕这物体的中心点(一般设定为look)点旋转,即eye和look点的距离保持不变。这需要把每次移动看作多次平移和旋转的结合,下面的代码给出每次微小平移和旋转的几何关系,此关系读者可以通过画图分析得到。

        通过右键的左右实现roll,上下实现沿着n轴的slide也就是缩放。

首先给出QT中鼠标事件的代码:

[cpp] view plain copy
  1. void GLWidget::mousePressEvent(QMouseEvent *event)  
  2. {  
  3.     lastPos = event->pos();  
  4. }  
  5.   
  6. void GLWidget::mouseMoveEvent(QMouseEvent *event)  
  7. {  
  8.     int dx = event->x() - lastPos.x();  
  9.     int dy = event->y() - lastPos.y();  
  10.     if (event->buttons() & Qt::LeftButton)  
  11.     {  
  12.         RotateX(dx);  
  13.         RotateY(dy);  
  14.     }  
  15.     else if(event->buttons() & Qt::RightButton)  
  16.     {  
  17.         cam.roll(dx);  
  18.         cam.slide(0,0,-dy);  
  19.     }  
  20.     lastPos = event->pos();  
  21. }  


RotateX()和RotateY()函数实现环绕物体漫游的操作,代码如下:

[cpp] view plain copy
  1. void GLWidget::RotateX(float angle)  
  2. {  
  3.     float d=cam.getDist();  
  4.     int cnt=100;  
  5.     float theta=angle/cnt;  
  6.     float slide_d=-2*d*sin(theta*3.14159265/360);  
  7.     cam.yaw(theta/2);  
  8.     for(;cnt!=0;--cnt)  
  9.     {  
  10.         cam.slide(slide_d,0,0);  
  11.         cam.yaw(theta);  
  12.     }  
  13.     cam.yaw(-theta/2);  
  14. }  
  15.   
  16. void GLWidget::RotateY(float angle)  
  17. {  
  18.     float d=cam.getDist();  
  19.     int cnt=100;  
  20.     float theta=angle/cnt;  
  21.     float slide_d=2*d*sin(theta*3.14159265/360);  
  22.     cam.pitch(theta/2);  
  23.     for(;cnt!=0;--cnt)  
  24.     {  
  25.         cam.slide(0,slide_d,0);  
  26.         cam.pitch(theta);  
  27.     }  
  28.     cam.pitch(-theta/2);  
  29. }  

通过上面两个函数可以实现对物体任意角度的观察,其中d是eye和物体中心点的距离,这里假设物体的中心点就是坐标原点。


原文链接:http://blog.csdn.net/hobbit1988/article/details/7956838