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文件
- #ifndef CAMERA_H
- #define CAMERA_H
-
- #include <QtOpenGL>
- #include <math.h>
-
- class Point3
- {
- public:
- float x,y,z;
- void set(float dx,float dy,float dz)
- {
- x=dx; y=dy; z=dz;
- }
- void set(Point3& p)
- {
- x=p.x; y=p.y; z=p.z;
- }
- Point3(float xx,float yy,float zz)
- {
- x=xx; y=yy; z=zz;
- }
- Point3()
- {
- x=y=z=0;
- }
- void build4tuple(float v[])
- {
- v[0]=x; v[1]=y; v[2]=z; v[3]=1.0f;
- }
- };
-
- class Vector3
- {
- public:
- float x,y,z;
- void set(float dx,float dy,float dz)
- {
- x=dx; y=dy; z=dz;
- }
- void set(Vector3& v)
- {
- x=v.x; y=v.y; z=v.z;
- }
- void flip()
- {
- x=-x; y=-y; z=-z;
- }
- void setDiff(Point3& a,Point3& b)
- {
- x=a.x-b.x; y=a.y-b.y; z=a.z-b.z;
- }
- void normalize()
- {
- float base=pow(x,2)+pow(y,2)+pow(z,2);
- x=x/pow(base,0.5);
- y=y/pow(base,0.5);
- z=z/pow(base,0.5);
- }
- Vector3(float xx, float yy, float zz)
- {
- x=xx; y=yy; z=zz;
- }
- Vector3(Vector3 &v)
- {
- x=v.x; y=v.y; z=v.z;
- }
- Vector3()
- {
- x=0; y=0; z=0;
- }
-
- Vector3 cross(Vector3 b)
- {
- float x1,y1,z1;
- x1=y*b.z-z*b.y;
- y1=z*b.x-x*b.z;
- z1=x*b.y-y*b.x;
- Vector3 c(x1,y1,z1);
- return c;
- }
-
- float dot(Vector3 b)
- {
- float d=x*b.x+y*b.y+z*b.z;
- return d;
- }
- };
-
- class Camera
- {
- public:
-
- Camera();
- ~Camera();
-
-
- void setCamera( float eyeX, float eyeY, float eyeZ,
- float lookX, float lookY, float lookZ,
- float upX, float upY, float upZ);
- void roll(float angle);
- void pitch(float angle);
- void yaw(float angle);
- void slide(float du, float dv, float dn);
- float getDist();
- void setShape(float viewAngle,float aspect,float Near,float Far);
-
-
- private:
-
- Point3 eye,look,up;
- Vector3 u,v,n;
- float viewAngle, aspect, nearDist, farDist;
- void setModelViewMatrix();
-
- };
-
- #endif //__CAMERA_H__
其中inlude的QtOpenGL文件是QT中封装的opengl类,如果不是在QT下运行,则需要inlude "gl\glut.h" 。Point3和Vector3类的定义主要是考虑camera中坐标点和向量的使用而设计。
camera.cpp文件:
- #include "camera.h"
-
-
-
- Camera::Camera() {}
-
- Camera::~Camera() {}
-
-
- void Camera::setCamera( float eyeX, float eyeY, float eyeZ,
- float lookX, float lookY, float lookZ,
- float upX, float upY, float upZ)
- {
-
- eye.set(eyeX, eyeY, eyeZ);
- look.set(lookX, lookY, lookZ);
- up.set(upX, upY, upZ);
- Vector3 upvec(up.x-eye.x,up.y-eye.y,up.z-eye.z);
-
-
- n.set(eye.x-look.x, eye.y-look.y, eye.z-look.z);
- u.set(upvec.cross(n).x,upvec.cross(n).y,upvec.cross(n).z);
- v.set(n.cross(u).x,n.cross(u).y,n.cross(u).z);
-
- u.normalize();
- v.normalize();
- n.normalize();
- setModelViewMatrix();
- }
-
- float Camera::getDist()
- {
- float dist=pow(eye.x,2)+pow(eye.y,2)+pow(eye.z,2);
- return pow(dist,0.5);
- }
-
- void Camera::setModelViewMatrix()
- {
- float m[16];
- Vector3 eVec(eye.x, eye.y,eye.z);
- m[0]=u.x; m[4]=u.y; m[8]=u.z; m[12]=-eVec.dot(u);
- m[1]=v.x; m[5]=v.y; m[9]=v.z; m[13]=-eVec.dot(v);
- m[2]=n.x; m[6]=n.y; m[10]=n.z; m[14]=-eVec.dot(n);
- m[3]=0; m[7]=0; m[11]=0; m[15]=1.0;
- glMatrixMode(GL_MODELVIEW);
- glLoadMatrixf(m);
- }
-
- void Camera::roll(float angle)
-
- {
- float cs=cos(angle*3.14159265/180);
- float sn=sin(angle*3.14159265/180);
- Vector3 t(u);
- Vector3 s(v);
- u.set(cs*t.x-sn*s.x, cs*t.y-sn*s.y, cs*t.z-sn*s.z);
- v.set(sn*t.x+cs*s.x, sn*t.y+cs*s.y, sn*t.z+cs*s.z);
- setModelViewMatrix();
- }
-
- void Camera::yaw(float angle)
- {
- float cs=cos(angle*3.14159265/180);
- float sn=sin(angle*3.14159265/180);
- Vector3 t(n);
- Vector3 s(u);
- n.set(cs*t.x-sn*s.x, cs*t.y-sn*s.y, cs*t.z-sn*s.z);
- u.set(sn*t.x+cs*s.x, sn*t.y+cs*s.y, sn*t.z+cs*s.z);
- setModelViewMatrix();
- }
-
- void Camera::pitch(float angle)
-
- {
- float cs=cos(angle*3.14159265/180);
- float sn=sin(angle*3.14159265/180);
- Vector3 t(v);
- Vector3 s(n);
- v.set(cs*t.x-sn*s.x, cs*t.y-sn*s.y, cs*t.z-sn*s.z);
- n.set(sn*t.x+cs*s.x, sn*t.y+cs*s.y, sn*t.z+cs*s.z);
- setModelViewMatrix();
- }
-
- void Camera::slide(float du, float dv, float dn)
- {
- eye.x+=du*u.x+dv*v.x+dn*n.x;
- eye.y+=du*u.y+dv*v.y+dn*n.y;
- eye.z+=du*u.z+dv*v.z+dn*n.z;
- look.x+=du*u.x+dv*v.x+dn*n.x;
- look.y+=du*u.y+dv*v.y+dn*n.y;
- look.z+=du*u.z+dv*v.z+dn*n.z;
- setModelViewMatrix();
- }
-
- void Camera::setShape(float viewAngle, float aspect, float Near, float Far)
- {
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(viewAngle,aspect, Near, Far);
- }
使用:
假设物体坐标中心在世界坐标原点,那么可以把摄像头设定在物体以外稍远的地方让方向朝向物体即可。调用cam.setCamera和cam.setShape函数来对摄像机进行初始化。
如何用鼠标实现对类的使用呢?这里基于QT中鼠标的事件控制,按下鼠标左键沿着屏幕左右移动实现沿着u轴slide和绕着v轴yaw;上下移动实现沿着v轴slide和绕着u轴pitch。注意,每次移动鼠标都要使摄像头绕这物体的中心点(一般设定为look)点旋转,即eye和look点的距离保持不变。这需要把每次移动看作多次平移和旋转的结合,下面的代码给出每次微小平移和旋转的几何关系,此关系读者可以通过画图分析得到。
通过右键的左右实现roll,上下实现沿着n轴的slide也就是缩放。
首先给出QT中鼠标事件的代码:
- 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)
- {
- cam.roll(dx);
- cam.slide(0,0,-dy);
- }
- lastPos = event->pos();
- }
RotateX()和RotateY()函数实现环绕物体漫游的操作,代码如下:
- void GLWidget::RotateX(float angle)
- {
- float d=cam.getDist();
- int cnt=100;
- float theta=angle/cnt;
- float slide_d=-2*d*sin(theta*3.14159265/360);
- cam.yaw(theta/2);
- for(;cnt!=0;--cnt)
- {
- cam.slide(slide_d,0,0);
- cam.yaw(theta);
- }
- cam.yaw(-theta/2);
- }
-
- void GLWidget::RotateY(float angle)
- {
- float d=cam.getDist();
- int cnt=100;
- float theta=angle/cnt;
- float slide_d=2*d*sin(theta*3.14159265/360);
- cam.pitch(theta/2);
- for(;cnt!=0;--cnt)
- {
- cam.slide(0,slide_d,0);
- cam.pitch(theta);
- }
- cam.pitch(-theta/2);
- }
通过上面两个函数可以实现对物体任意角度的观察,其中d是eye和物体中心点的距离,这里假设物体的中心点就是坐标原点。
原文链接:http://blog.csdn.net/hobbit1988/article/details/7956838