关闭

MFC对话框程序中使用opengl实现漫游

1302人阅读 评论(0) 收藏 举报
分类:

            忙活了一个下午和晚上,终于在之前的MFC对话框工程中实现了漫游的功能,虽然只是简单的照相机前进后退左右移动的功能,虽然很多代码是网上的,但是最终把功能加到这个MFC对话框程序中了,还是可喜可贺的。主要是对opengl编程太不熟悉了,一个个小问题卡住半天的时间。

         参照了如下文章:文章一文章二文章三

 首先是定义一个工具类,比较喜欢这种编程方式,这样可以很大程度上减少耦合,修改很容易,代码也很清晰。

         下面的主工具类Camera基本上是第一篇文章中的工具类,不过它的文章中的代码是基于QT的,所有有个Vector3类在MFC中找不到,所以我定义了一个和它功能相似的类。

Camera.h文件如下:

#pragma once
#include "D3Vector.h"
#include <math3d.h>
#include  <gl/GLU.h>

class Camera
{
public:
	Camera(void);
	~Camera(void);

	/* 获得摄像机状态方法 */
	D3Vector getPosition()   {	return m_Position;		}
	D3Vector getView()	    {	return m_View;			}
	D3Vector 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 yawCamera(float speed);

	/* 前后移动摄像机 */
	void moveCamera(float speed);

	/* 放置摄像机 */
	void setLook();

	/* 得到摄像机指针 */
	static Camera* GetCamera(void) { return m_pCamera;}


private:
	/* 摄像机属性 */
	static Camera  *m_pCamera;      /* 当前全局摄像机指针 */
	D3Vector        m_Position;      /* 位置 */
	D3Vector        m_View;          /* 朝向 */
	D3Vector        m_UpVector;      /* 向上向量 */
	float          m_Speed;         /* 速度 */
};

下面是Camera.cpp的代码:

#include "StdAfx.h"
#include "Camera.h"


Camera* Camera::m_pCamera = NULL;
Camera::Camera(void)
{
	/* 初始化向量值 */
	D3Vector zero = D3Vector(10.0, 10.0, 10.0);
	D3Vector view = D3Vector(0.0, 0.0, -5.0);
	D3Vector up   = D3Vector(0.0, 1.0, 0.0);

	/* 初始化摄像机 */

	//观察位置 Eye
	m_Position	= zero;

	//被观察点
	m_View		= view;

	//倒立还是正立
	m_UpVector	= up;

	//前进速度
	m_Speed     = 0.5;

	//相机指针
	m_pCamera = this;
}

Camera::~Camera(void)
{
}

/* 设置摄像机的位置, 观察点和向上向量 */
void Camera::setCamera(float positionX, float positionY, float positionZ,
			   float viewX,     float viewY,     float viewZ,
			   float upVectorX, float upVectorY, float upVectorZ){
				   /* 构造向量 */
				   D3Vector Position	= D3Vector(positionX, positionY, positionZ);
				   D3Vector View		= D3Vector(viewX, viewY, viewZ);
				   D3Vector UpVector	= D3Vector(upVectorX, upVectorY, upVectorZ);

				   /* 设置摄像机 */
				   m_Position = Position;
				   m_View     = View;
				   m_UpVector = UpVector;
}

/* 旋转摄像机方向 */
void Camera::rotateView(float angle, float x, float y, float z){
	D3Vector newView;

	/* 计算方向向量 */
	D3Vector 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::yawCamera(float speed){
	D3Vector yaw;
	D3Vector cross = m_View - m_Position;
	yaw = cross.crossProduct(m_UpVector);

	///归一化向量
	D3Vector::normalize(yaw);

	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){
	/* 计算方向向量 */
	D3Vector vector = m_View - m_Position;
	D3Vector::normalize(vector);         /*< 单位化 */

	/* 更新摄像机 */
	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);
}


上面的代码基本上都是文章一的作者写的,因为他的文章中的Vector3在MFC中找不到,所以我替换成了自己定义的D3Vector类,它的头文件和源文件如下:

#pragma once


/************************************************************************/
/* 使用行向量表示坐标                                                                     */
/************************************************************************/
class D3Vector
{
public:
	D3Vector(void);
	D3Vector(float x,float y,float z=1.0f);
	~D3Vector(void);

public:

	//重载加法和减法运算
	D3Vector operator+(D3Vector vec);

	D3Vector operator-(D3Vector vec);

	/************************************************************************/
	/* 将向量归一化                                                                     */
	/************************************************************************/
	static void normalize(D3Vector & vec);

	/************************************************************************/
	/* 计算两个向量的叉积                                                                     */
	/************************************************************************/
	D3Vector crossProduct(D3Vector vec);

public:
	//该三维向量的三个坐标值,默认情况下z坐标为1
	float _x;
	float _y;
	float _z;
};

源文件如下:

#include "StdAfx.h"
#include "D3Vector.h"

D3Vector::D3Vector(void)
{
}

D3Vector::~D3Vector(void)
{
}

D3Vector::D3Vector(float x,float y,float z){
	_x=x;
	_y=y;
	_z=z;
}



D3Vector D3Vector::operator+(D3Vector vec){
	D3Vector result;
	result._x=_x+vec._x;
	result._y=_y+vec._y;
	result._z=_z+vec._z;
	return result;
}

D3Vector D3Vector::operator-(D3Vector vec){
	D3Vector result;
	result._x=_x-vec._x;
	result._y=_y-vec._y;
	result._z=_z-vec._z;
	return result;
}

/************************************************************************/
/* 将向量归一化                                                                     */
/************************************************************************/
void D3Vector::normalize(D3Vector & vec){
	float  tmp=vec._x*vec._x+vec._y*vec._y+vec._z*vec._z;
	vec._x=vec._x/sqrt(tmp);
	vec._y=vec._y/sqrt(tmp);
	vec._z=vec._z/sqrt(tmp);
}

/************************************************************************/
/* 计算两个向量的叉积                                                                     */
/************************************************************************/
D3Vector D3Vector::crossProduct(D3Vector vec){
	D3Vector result;
	result._x=_y*vec._z-_z*vec._y;
	result._y=_z*vec._x-_x*vec._z;
	result._z=_x*vec._y-_y*vec._x;
	return result;
}


             上面的准备工作做好后就可以使用这个工具类实现漫游的功能了,在opengl中实现漫游功能的主要是通过gluLookAt这个函数实现的,就是上面的setLook函数中的内容。

        漫游主要是让程序响应外设的消息,这里只使用键盘控制,所以接下来要做的事情其实是消息的处理。这篇文章的源码是在上一篇文章中的源码的基础上更改的,所以对话框类时CDialog4。这里需要再添加一个WM_CHAR消息和重写PreTranslateMessage,至于为什么第三篇文章中讲的比较详细。

        修改完毕后CDialog4中重写的函数或消息响应函数有这么几个:

public:
	afx_msg void OnSize(UINT nType, int cx, int cy);
	virtual BOOL OnInitDialog();
	afx_msg void OnTimer(UINT_PTR nIDEvent);
	virtual BOOL PreTranslateMessage(MSG* pMsg);
	afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
并且添加一个成员变量:

Camera m_Camera;
PreTranslateMessage函数的函数体如下:

BOOL CDialog4::PreTranslateMessage(MSG* pMsg)
{
	// TODO: 在此添加专用代码和/或调用基类
	if (pMsg->message==WM_KEYDOWN||pMsg->message==WM_CHAR)
	{
		pMsg->hwnd=m_hWnd;
		return FALSE;
	}

	return CDialog::PreTranslateMessage(pMsg);
}


下面的就是实际的键盘事件响应了,按不同键时照相机会向不同的方向平移,还有一个旋转操作,但是对旋转操作还没有理解透彻。
void CDialog4::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	switch(nChar){
		case  'w':
			m_Camera.moveCamera(m_Camera.getSpeed());//向前运动
			break;
		case  's':
			m_Camera.moveCamera(-m_Camera.getSpeed());//向后运动
			break;
		case  'a':
			m_Camera.yawCamera(m_Camera.getSpeed());//向左运动
			break;
		case 'd':
			m_Camera.yawCamera(-m_Camera.getSpeed());//向右运动
			break;
		case 'y':
			m_Camera.rotateView(0.1,0,1,0);
			break;
		default:
			break;

	}
	CDialog::OnChar(nChar, nRepCnt, nFlags);
}

               还有最重要的一步,就是SetLook函数的调用,最开始调用这个函数的地方不对导致一直看不到图像,这个函数的调用地点放在COpenGL的Render函数内,并且要放在glMatrixMode(GL_MODELVIEW)下面:

void COpenGL::Render(){
	glEnable(GL_DEPTH_TEST);
	glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
	glColor3f(1.0, 0.0, 0.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	Camera::GetCamera()->setLook();

	drawCube();
	glColor3f(1.0f,0.0,0.0);
	glutSolidCube(1);
	glTranslatef(3,0,0);
	glColor3f(0,1,0);
	glutSolidSphere(1,20,16);
	glTranslatef(-6,0,2);
	glColor3f(0,0,1);
	glutWireTorus(0.5,1,20,10);
	SwapBuffers(hDC);    
}

        程序运行后的效果如下:并且可以进行漫游操作了。




0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:348370次
    • 积分:6296
    • 等级:
    • 排名:第4076名
    • 原创:261篇
    • 转载:43篇
    • 译文:13篇
    • 评论:44条
    友情链接
    博客专栏
    文章分类
    最新评论