本文固定链接: http://liusir.name/qt-learning-faqs.html | 民警小刘
一个不错的OpenGL程序当然免不了对整个场景的漫游。在我程序中便是用W、A、S、D来靠近,远离,向左,向右来移动场景,Q、E、Z、C则是旋转场景。同时,补充一条,我用鼠标滚轮实现了物体的放大和缩小,效果上和按W、S键是相同的,但本质上是有差别的,呵呵~
我要贴出的这个Camera类是从《OpenGL游戏编程》里提取出来的,并且已经在Qt环境下运行成功(本来代码是在VS2005)。
Camera.h内容:
#ifndef __CAMERA_H__ #define __CAMERA_H__ #include "Vector.h" /* 包含向量类头文件 */ /* 摄像机类 */ class Camera { public: /* 构造函数和析构函数 */ Camera(); ~Camera(); /* 获得摄像机状态方法 */ Vector3 getPosition() { return m_Position; } Vector3 getView() { return m_View; } Vector3 getUpVector() { return m_UpVector; } float getSpeed() { return m_Speed; } /* 设置速度 */ void setSpeed(float speed) { m_Speed = speed; } /* 设置摄像机的位置, 观察点和向上向量 */ void setCamera(float positionX, float positionY, float positionZ, float viewX, float viewY, float viewZ, float upVectorX, float upVectorY, float upVectorZ); /* 旋转摄像机方向 */ void rotateView(float angle, float X, float Y, float Z); /* 根据鼠标设置摄像机观察方向 */ void setViewByMouse(); /* 左右摄像机移动 */ void yawCamera(float speed); /* 前后移动摄像机 */ void moveCamera(float speed); /* 放置摄像机 */ void setLook(); /* 得到摄像机指针 */ static Camera* GetCamera(void) { return m_pCamera;} private: /* 摄像机属性 */ static Camera *m_pCamera; /* 当前全局摄像机指针 */ Vector3 m_Position; /* 位置 */ Vector3 m_View; /* 朝向 */ Vector3 m_UpVector; /* 向上向量 */ float m_Speed; /* 速度 */ }; #endif //__CAMERA_H__
Camera.cpp内容:
#include "Stdafx.h" #include "Camera.h" /* 包含摄像机头文件 */ #include "Vector.h" /* 包含向量类 */ #include "math.h"
Camera* Camera::m_pCamera = NULL;
/* 构造函数 */ Camera::Camera() { /* 初始化向量值 */ Vector3 zero = Vector3(0.0, 0.0, 0.0); Vector3 view = Vector3(0.0, 1.0, 0.5); Vector3 up = Vector3(0.0, 0.0, 1.0);
/* 初始化摄像机 */
//观察位置 Eye m_Position = zero;
//被观察点 m_View = view;
//倒立还是正立 m_UpVector = up;
//前进速度 m_Speed = 0.05f;
//相机指针 m_pCamera = this;
}
Camera::~Camera() { }
/* 设置摄像机的位置,朝向和向上向量 */ void Camera::setCamera( float positionX, float positionY, float positionZ, float viewX, float viewY, float viewZ, float upVectorX, float upVectorY, float upVectorZ) { /* 构造向量 */ Vector3 Position = Vector3(positionX, positionY, positionZ); Vector3 View = Vector3(viewX, viewY, viewZ); Vector3 UpVector = Vector3(upVectorX, upVectorY, upVectorZ);
/* 设置摄像机 */ m_Position = Position; m_View = View; m_UpVector = UpVector; }
/* 旋转摄像机方向 */ void Camera::rotateView(float angle, float x, float y, float z) { Vector3 newView;
/* 计算方向向量 */ Vector3 view = m_View - m_Position;
/* 计算 sin 和cos值 */ float cosTheta = (float)cos(angle); float sinTheta = (float)sin(angle);
/* 计算旋转向量的x值 */ newView.x = (cosTheta + (1 - cosTheta) * x * x) * view.x; newView.x += ((1 - cosTheta) * x * y - z * sinTheta) * view.y; newView.x += ((1 - cosTheta) * x * z + y * sinTheta) * view.z;
/* 计算旋转向量的y值 */ newView.y = ((1 - cosTheta) * x * y + z * sinTheta) * view.x; newView.y += (cosTheta + (1 - cosTheta) * y * y) * view.y; newView.y += ((1 - cosTheta) * y * z - x * sinTheta) * view.z;
/* 计算旋转向量的z值 */ newView.z = ((1 - cosTheta) * x * z - y * sinTheta) * view.x; newView.z += ((1 - cosTheta) * y * z + x * sinTheta) * view.y; newView.z += (cosTheta + (1 - cosTheta) * z * z) * view.z;
/* 更新摄像机的方向 */ m_View = m_Position + newView; }
/* 用鼠标旋转摄像机 */ void Camera::setViewByMouse() {/*此函数已放弃。如要使用,在Update处调用即可*/
/*< 保存当前鼠标位置 */ POINT mousePos; int middleX = GetSystemMetrics(SM_CXSCREEN) >> 1; /*< 得到屏幕宽度的一半 */ int middleY = GetSystemMetrics(SM_CYSCREEN) >> 1; /*< 得到屏幕高度的一半 */
float angleY = 0.0f;/*< 摄像机左右旋转角度 */ float angleZ = 0.0f;/*< 摄像机上下旋转角度 */ static float currentRotX = 0.0f; /* 得到当前鼠标位置 */ GetCursorPos(&mousePos); ShowCursor(TRUE); /* 如果鼠标没有移动,则不用更新 */ if( (mousePos.x == middleX) && (mousePos.y == middleY) ) return; /* 设置鼠标位置在屏幕中心 */ SetCursorPos(middleX, middleY); /* 得到鼠标移动方向 */ angleY = (float)( (middleX - mousePos.x) ) / 1000.0f; angleZ = (float)( (middleY - mousePos.y) ) / 1000.0f; static float lastRotX = 0.0f; /* 用于保存旋转角度 */ lastRotX = currentRotX; /* 跟踪摄像机上下旋转角度 */ currentRotX += angleZ; /* 如果上下旋转弧度大于1.0,我们截取到1.0并旋转 */ if(currentRotX > 1.0f) { currentRotX = 1.0f;
/* 根据保存的角度旋转方向 */ if(lastRotX != 1.0f) { /* 通过叉积找到与旋转方向垂直的向量 */ Vector3 vAxis = m_View - m_Position; vAxis = vAxis.crossProduct(m_UpVector); vAxis = vAxis.normalize();
///旋转 rotateView( 1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z); } } /* 如果旋转弧度小于-1.0,则也截取到-1.0并旋转 */ else if(currentRotX < -1.0f) { currentRotX = -1.0f;
if(lastRotX != -1.0f) {
/* 通过叉积找到与旋转方向垂直的向量 */ Vector3 vAxis = m_View - m_Position; vAxis = vAxis.crossProduct(m_UpVector); vAxis = vAxis.normalize();
///旋转 rotateView( -1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z); } } /* 否则就旋转angleZ度 */ else { /* 找到与旋转方向垂直向量 */ Vector3 vAxis = m_View - m_Position; vAxis = vAxis.crossProduct(m_UpVector); vAxis = vAxis.normalize();
///旋转 rotateView(angleZ, vAxis.x, vAxis.y, vAxis.z); }
/* 总是左右旋转摄像机 */ rotateView(angleY, 0, 1, 0); }
/* 左右移动摄像机 */ void Camera::yawCamera(float speed) { Vector3 yaw; Vector3 cross = m_View - m_Position; cross = cross.crossProduct(m_UpVector);
///归一化向量 yaw = cross.normalize();
m_Position.x += yaw.x * speed; m_Position.z += yaw.z * speed;
m_View.x += yaw.x * speed; m_View.z += yaw.z * speed;
}
/* 前后移动摄像机 */ void Camera::moveCamera(float speed) { /* 计算方向向量 */ Vector3 vector = m_View - m_Position; vector = vector.normalize(); /*< 单位化 */
/* 更新摄像机 */ m_Position.x += vector.x * speed; /*< 根据速度更新位置 */ m_Position.y += vector.y * speed; m_Position.z += vector.z * speed;
m_View.x += vector.x * speed; /*< 根据速度更新方向 */ m_View.y += vector.y * speed; m_View.z += vector.z * speed;
}
/* 设置视点 */ void Camera::setLook() {
/* 设置视口 */ gluLookAt(m_Position.x, m_Position.y, m_Position.z, m_View.x, m_View.y, m_View.z, m_UpVector.x, m_UpVector.y, m_UpVector.z); }
使用方法:
/* 设置全局相机 */ m_Camera.setLook();
/* 初始化相机 */ m_Camera.setCamera(0.0f, 0.0f, -3.0f, //Eye 0.0f, 0.0f, -7.0f, //Center 0.0f, 1.0f, 0.0f); //Up
case Qt::Key_W://镜头靠近 m_Camera.moveCamera(m_Camera.getSpeed()); break; case Qt::Key_S://镜头远离 m_Camera.moveCamera(-m_Camera.getSpeed());
上面记得要先初始化相机,然后给其“摆放”好,然后利用键盘事件改变其视点就好了,不过对其的运用前提要理解好glLookAt函数,可以参考我之前的日志“gluPerspective与glLookAt”