您好,登錄后才能下訂單哦!
3D游戲中最基本的一個(gè)功能就是3D漫游了,玩家可以通過(guò)鍵盤(pán)或者鼠標(biāo)控制自己的視角。
之前我們也學(xué)習(xí)過(guò)一個(gè)相關(guān)的函數(shù),glLookAt,用來(lái)制定攝像機(jī)的位置,攝像機(jī)觀察目標(biāo)位置,還有攝像機(jī)的放置方式,我們可以通過(guò)不斷地調(diào)用這個(gè)函數(shù)來(lái)實(shí)現(xiàn)3D漫游,但更方便的是抽象出一個(gè)攝像機(jī)類(lèi),實(shí)現(xiàn)一些攝像機(jī)的方法。
UVN使用三個(gè)相互垂直的向量來(lái)表示相機(jī)的位置與朝向:
1) 相機(jī)注視的向量N
2) 相機(jī)的上方向向量V
3) 相機(jī)的右方向向量U
如下圖,是在世界坐標(biāo)系下的UVN相機(jī)的向量表示:
綠色軸為N,藍(lán)色軸為V,紅色軸為U。
當(dāng)要改變相機(jī)位置和朝向的時(shí)候,只需要將uvn矩陣和相應(yīng)的變換矩陣相乘即可。
這里借助了一個(gè)第三方矩陣向量庫(kù) - eigen。Ubuntu下的安裝的過(guò)程非常簡(jiǎn)單,下載源碼之后,解壓,cd進(jìn)目錄:
mkdir build
cd build
cmake ..
sudo make install
寫(xiě)一個(gè)頭文件:
eigen.h
#ifndef EIGEN_H #define EIGEN_H #include "eigen3/Eigen/Dense" #include "eigen3/Eigen/LU" #include "eigen3/Eigen/Core" #endif // EIGEN_H
看類(lèi)聲明:glcamera.h
#ifndef GLCAMERA_H #define GLCAMERA_H #include "eigen.h" #include <GL/glu.h> #include <iostream> 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
setShape:設(shè)置攝像機(jī)的視角。
roll,yaw,pitch相當(dāng)于繞N,V,U軸的旋轉(zhuǎn),如下圖:
下面是相機(jī)的實(shí)現(xiàn):
#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矩陣替換原視點(diǎn)矩陣 } void GLCamera::setShape(float viewAngle, float aspect, float Near, float Far) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); //設(shè)置當(dāng)前矩陣模式為投影矩陣并歸一化 gluPerspective(viewAngle,aspect, Near, Far); //對(duì)投影矩陣進(jìn)行透視變換 } void GLCamera::slide(float du, float dv, float dn) { //std::cout<<"u.x:"<<u.x()<<std::endl; m_pos(0) = m_pos(0) + du*u.x()+dv*v.x()+dn*n.x(); m_pos(1) = m_pos(1) + du*u.y() +dv*v.y()+dn*n.y(); m_pos(2) = m_pos(2) + du*u.z()+dv*v.z()+dn*n.z(); m_target(0) = m_target(0)+du*u.x()+dv*v.x()+dn*n.x(); m_target(1) = m_target(0)+du*u.y()+dv*v.y()+dn*n.y(); m_target(2) = m_target(0)+du*u.z()+dv*v.z()+dn*n.z(); setModelViewMatrix(); } void GLCamera::roll(float angle) { float cs=cos(angle*3.14159265/180); float sn=sin(angle*3.14159265/180); Vector3d t(u); Vector3d s(v); u.x() = cs*t.x()-sn*s.x(); u.y() = cs*t.y()-sn*s.y(); u.z() = cs*t.z()-sn*s.z(); v.x() = sn*t.x()+cs*s.x(); v.y() = sn*t.y()+cs*s.y(); v.z() = sn*t.z()+cs*s.z(); setModelViewMatrix(); //每次計(jì)算完坐標(biāo)軸變化后調(diào)用此函數(shù)更新視點(diǎn)矩陣 } void GLCamera::pitch(float angle) { float cs=cos(angle*3.14159265/180); float sn=sin(angle*3.14159265/180); Vector3d t(v); Vector3d s(n); v.x() = cs*t.x()-sn*s.x(); v.y() = cs*t.y()-sn*s.y(); v.z() = cs*t.z()-sn*s.z(); n.x() = sn*t.x()+cs*s.x(); n.y() = sn*t.y()+cs*s.y(); n.z() = sn*t.z()+cs*s.z(); setModelViewMatrix(); } void GLCamera::yaw(float angle) { float cs=cos(angle*3.14159265/180); float sn=sin(angle*3.14159265/180); Vector3d t(n); Vector3d s(u); n.x() = cs*t.x()-sn*s.x(); n.y() = cs*t.y()-sn*s.y(); n.z() = cs*t.z()-sn*s.z(); u.x() = sn*t.x()+cs*s.x(); u.y() = sn*t.y()+cs*s.y(); u.z() = sn*t.z()+cs*s.z(); setModelViewMatrix(); } float GLCamera::getDist() { float dist = pow(m_pos.x(),2)+pow(m_pos.y(),2)+pow(m_pos.z(),2); return pow(dist,0.5); }
沒(méi)什么好說(shuō)的,都是矩陣的一些計(jì)算。
這樣就可以將你的攝像機(jī)融入到OpenGL工程中了,比如說(shuō)放進(jìn)一個(gè)Qt的工程,用一個(gè)GLWifget類(lèi)來(lái)顯示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);
glLoadIdentity(); camera->setModelViewMatrix();
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 有點(diǎn)大,耐心等待):
openGL中camera類(lèi)的設(shè)計(jì)以及使用 - http://blog.csdn.net/hobbit1988/article/details/7956838
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。