OpenGL进阶(十四)-UVNCamera实现-创新互联

提要

   3D游戏中最基本的一个功能就是3D漫游了,玩家可以通过键盘或者鼠标控制自己的视角。

创新互联公司是专业的六合网站建设公司,六合接单;提供成都做网站、网站设计,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行六合网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!

   之前我们也学习过一个相关的函数,glLookAt,用来制定摄像机的位置,摄像机观察目标位置,还有摄像机的放置方式,我们可以通过不断地调用这个函数来实现3D漫游,但更方便的是抽象出一个摄像机类,实现一些摄像机的方法。

UVN相机

UVN使用三个相互垂直的向量来表示相机的位置与朝向:

1) 相机注视的向量N
2) 相机的上方向向量V
3) 相机的右方向向量U

如下图,是在世界坐标系下的UVN相机的向量表示:

OpenGL进阶(十四) - UVN Camera实现

绿色轴为N,蓝色轴为V,红色轴为U。

当要改变相机位置和朝向的时候,只需要将uvn矩阵和相应的变换矩阵相乘即可。

代码实现

这里借助了一个第三方矩阵向量库 - eigen。Ubuntu下的安装的过程非常简单,下载源码之后,解压,cd进目录:

mkdir build
cd build 
cmake ..
sudo make install

写一个头文件:

eigen.h

#ifndef EIGEN_H #define EIGEN_H  #include "eigen3/Eigen/Dense" #include "eigen3/Eigen/LU" #include "eigen3/Eigen/Core"  #endif // EIGEN_H

放在工程目录下面,使用的时候包含进来就可以了。

看类声明:glcamera.h

#ifndef GLCAMERA_H #define GLCAMERA_H #include "eigen.h" #include  #include   using namespace Eigen; class GLCamera { public:     GLCamera();     GLCamera(const Vector3d& pos, const Vector3d& target, const Vector3d& up);     void setModelViewMatrix();     void setShape(float viewAngle,float aspect,float Near,float Far);     void slide(float du, float dv, float dn);     void roll(float angle);     void yaw(float angle);     void pitch(float angle);     float getDist();  private:     Vector3d m_pos;     Vector3d m_target;     Vector3d m_up;     Vector3d u,v,n;  };  #endif // GLCAMERA_H

setModelViewMatrix:加载将当前MV矩阵。

setShape:设置摄像机的视角。

roll,yaw,pitch相当于绕N,V,U轴的旋转,如下图:

OpenGL进阶(十四) - UVN Camera实现

下面是相机的实现:

#include "glcamera.h"  GLCamera::GLCamera() {  }  GLCamera::GLCamera(const Vector3d &pos, const Vector3d &target, const Vector3d &up) {     m_pos = pos;     m_target = target;     m_up = up;     n = Vector3d( pos.x()-target.x(), pos.y()-target.y(), pos.z()-target.z());     u = Vector3d(up.cross(n).x(), up.cross(n).y(), up.cross(n).z());     v = Vector3d(n.cross(u).x(),n.cross(u).y(),n.cross(u).z());       n.normalize();     u.normalize();     v.normalize();      setModelViewMatrix(); }  void GLCamera::setModelViewMatrix() {     double m[16];     m[0]=u.x(); m[4]=u.y(); m[8]=u.z(); m[12]=-m_pos.dot(u);     m[1]=v.x(); m[5]=v.y(); m[9]=v.z(); m[13]=-m_pos.dot(v);     m[2]=n.x(); m[6]=n.y(); m[10]=n.z(); m[14]=-m_pos.dot(n);     m[3]=0;  m[7]=0;  m[11]=0;  m[15]=1.0;     glMatrixMode(GL_MODELVIEW);     glLoadMatrixd(m);     //用M矩阵替换原视点矩阵 }  void  GLCamera::setShape(float viewAngle, float aspect, float Near, float Far) {     glMatrixMode(GL_PROJECTION);     glLoadIdentity();                                   //设置当前矩阵模式为投影矩阵并归一化     gluPerspective(viewAngle,aspect, Near, Far);        //对投影矩阵进行透视变换 }  void GLCamera::slide(float du, float dv, float dn) {     //std::cout<<"u.x:"<  

没什么好说的,都是矩阵的一些计算。

这样就可以将你的摄像机融入到OpenGL工程中了,比如说放进一个Qt的工程,用一个GLWifget类来显示OpenGL。

在initializeGL() 中,初始化camera

  Vector3d pos(0.0, 0.0, 12.0);     Vector3d target(0.0, 0.0, 0.0);     Vector3d up(0.0, 1.0, 0.0);     camera = new GLCamera(pos, target, up);

在paintGL的时候,设置当前矩阵:

 glLoadIdentity();     camera->setModelViewMatrix();

在resizeGL中调整视角:

camera->setShape(45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0);

添加相应的鼠标事件:

void GLWidget::mousePressEvent(QMouseEvent *event) {     lastPos = event->pos(); }  void GLWidget::mouseMoveEvent(QMouseEvent *event) {     int dx = event->x() - lastPos.x();     int dy = event->y() - lastPos.y();     if (event->buttons() & Qt::LeftButton)     {         RotateX(dx);         RotateY(dy);     }     else if(event->buttons() & Qt::RightButton)     {         camera->roll(dx);         //camera->pitch(dy);         //camera->slide(0,0,-dy);     }     else if(event->buttons() & Qt::MiddleButton)     {         camera->slide(-dx,dy,0);     }     lastPos = event->pos();     updateGL(); }  void GLWidget::RotateX(float angle) {     float d=camera->getDist();     int cnt=100;     float theta=angle/cnt;     float slide_d=-2*d*sin(theta*3.14159265/360);     camera->yaw(theta/2);     for(;cnt!=0;--cnt)     {         camera->slide(slide_d,0,0);         camera->yaw(theta);     }     camera->yaw(-theta/2); }  void GLWidget::RotateY(float angle) {     float d = camera->getDist();     int cnt=100;     float theta=angle/cnt;     float slide_d=2*d*sin(theta*3.14159265/360);     camera->pitch(theta/2);     for(;cnt!=0;--cnt)     {         camera->slide(0,slide_d,0);         camera->pitch(theta);     }     camera->pitch(-theta/2); }

效果就像这样(gif 有点大,耐心等待):

OpenGL进阶(十四) - UVN Camera实现

参考

openGL中camera类的设计以及使用 - http://blog.csdn.net/hobbit1988/article/details/7956838

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


网页名称:OpenGL进阶(十四)-UVNCamera实现-创新互联
本文来源:http://ybzwz.com/article/cdhjhe.html