VS2010-MFC:用OpenGL在对话框中的PictureControl(图片控件)中绘制三维模型,可旋转、平移、缩放,可用于三维模型的预览

由于有这个需求,就是当在对话框设置一些数值的时候,可以在对话框上预览三维图像。

(1)生成一个基于对话框的程序,或者直接在单文档或者多文档上插入一个对话框,生成一个新的对话框类CGridingDlg,名字可以任取。

(2)配置好工程的OpenGL环境,不知道可以百度。

(3)在CGridingDlg的头文件中添加以下变量:

 

/*-----OpenGL绘图相关的变量-----------------------*/
	//旋转角度
	float xrof;
	float yrof;
	float zrof; 
	//Z轴移动变量
	BOOL  m_bZoomZ;
	float m_fZoomZ;
	GLfloat fNearPlane, fFarPlane;//透视投影的最近、最远的裁剪面距离
	float cameraPos[3];//相机位置
	float modelView[16];//模型矩阵
	float translateSpeed;//鼠标滑轮平移速度
	float rotateSpeed;//旋转速度
	float walkSpeed;
	float inertia;
	GLfloat m_xCurAngle;//X方向上的旋转角度
	GLfloat m_yCurAngle;//Y方向上的旋转角度
	GLfloat m_zCurAngle;//Z方向上的旋转角度
	CPoint m_MouseDownPoint;//鼠标点击的落点
	BOOL bMouseWheelStop;//鼠标中键滚动
	int buttonState;//鼠标状态
	HGLRC m_hRC;    //Rendering Context着色描述表  
	HDC m_pDC;//Device Context设备描述表
	BOOL SetupPixelFormat(HDC hDC);//设置像素格式
	BOOL InitializeOpenGL(HDC hDC);//初始化OpenGL
	CRect m_oldRect;
	void RenderScene(void);//绘图函数
	/*------------------------------------------------*/


以上为在后面需要使用到变量

 

 

(4)重载对话框类CGridingDlg的虚函数OnInitDialog(),添加WM_TIMER消息,

添加函数SetupPixelFormat(HDC hDC)---->用于设置OpenGL的像素格式

 

BOOL CGriding::SetupPixelFormat(HDC hDC)
{
	//PIXELFORMATDESCRIPTOR pixelDesc;
	//pixelDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
	//pixelDesc.nVersion = 1;
	//pixelDesc.dwFlags = PFD_DRAW_TO_WINDOW | 
	//	PFD_SUPPORT_OPENGL |
	//	PFD_DOUBLEBUFFER |
	//	PFD_TYPE_RGBA;
	//pixelDesc.iPixelType = PFD_TYPE_RGBA;
	//pixelDesc.cColorBits = 32;
	//pixelDesc.cRedBits = 0;
	//pixelDesc.cRedShift = 0;
	//pixelDesc.cGreenBits = 0;
	//pixelDesc.cGreenShift = 0;
	//pixelDesc.cBlueBits = 0;
	//pixelDesc.cBlueShift = 0;
	//pixelDesc.cAlphaBits = 0;
	//pixelDesc.cAlphaShift = 0;
	//pixelDesc.cAccumBits = 0;
	//pixelDesc.cAccumRedBits = 0;
	//pixelDesc.cAccumGreenBits = 0;
	//pixelDesc.cAccumBlueBits = 0;
	//pixelDesc.cAccumAlphaBits = 0;
	//pixelDesc.cDepthBits = 0;
	//pixelDesc.cStencilBits = 1;
	//pixelDesc.cAuxBuffers = 0;
	//pixelDesc.iLayerType = PFD_MAIN_PLANE;
	//pixelDesc.bReserved = 0;
	//pixelDesc.dwLayerMask = 0;
	//pixelDesc.dwVisibleMask = 0;
	//pixelDesc.dwDamageMask = 0;
	//int PixelFormat = ChoosePixelFormat(hDC,&pixelDesc);
	//if(PixelFormat==0) // Choose default
	//{
	//	PixelFormat = 1;
	//	if(DescribePixelFormat(hDC,PixelFormat,
	//		sizeof(PIXELFORMATDESCRIPTOR),&pixelDesc)==0)
	//	{
	//		return FALSE;
	//	}
	//}
	//if(SetPixelFormat(hDC,PixelFormat,&pixelDesc)==FALSE)
	//{ 
	//	return FALSE;
	//}
	//return TRUE;
	//初始化象素格式以及选取合适的格式来创建RC
	PIXELFORMATDESCRIPTOR pfd = { 
		sizeof(PIXELFORMATDESCRIPTOR), // pfd结构的大小 
		1, // 版本号 
		PFD_DRAW_TO_WINDOW | // 支持在窗口中绘图 
		PFD_SUPPORT_OPENGL | // 支持 OpenGL 
		PFD_DOUBLEBUFFER, // 双缓存模式 
		PFD_TYPE_RGBA, // RGBA 颜色模式 
		24, // 24 位颜色深度 ,color depth
		0, 0, 0, 0, 0, 0, // 忽略颜色位 
		0, // 没有非透明度缓存 
		0, // 忽略移位位 
		0, // 无累加缓存 
		0, 0, 0, 0, // 忽略累加位 
		32, // 32 位深度缓存 
		0, // 无模板缓存 
		0, // 无辅助缓存 
		PFD_MAIN_PLANE, // 主层 
		0, // 保留 
		0, 0, 0 // 忽略层,可见性和损毁掩模 
	}; 
	//在DC中选择合适的象素格式并返回索引号
	int pixelformat;
	pixelformat=::ChoosePixelFormat(m_pDC,&pfd);
	if (pixelformat==0)
	{
		AfxMessageBox("选择像素格式失败!");
		return FALSE;
	}
	//设置指定象素格式
	if (::SetPixelFormat(m_pDC,pixelformat,&pfd)==FALSE)
	{
		AfxMessageBox("设置像素格式失败!");
		return FALSE;
	}
	return TRUE;
}


上面是两种设置OpenGL像素格式的方法,其中注释的代码也可以用于设置像素格式,任用一种。

 

在对话框上面添加一个Picture控件,用于显示图形,设置其ID为IDC_STATIC_PictureRender

 

添加InitializeOpenGL()函数用于在对话框中初始化OpenGL。

 

BOOL CGriding::InitializeOpenGL(HDC hDC)
{
	//首先把DC的象素格式调整为指定的格式,以便后面对DC的使用
	SetupPixelFormat(hDC); 
	//根据DC来创建RC
	m_hRC=::wglCreateContext(hDC);  
	if (m_hRC==NULL)
	{
		return FALSE;
	}
	//设置当前的RC,以后的画图操作都画在m_pDC指向的DC上
	if ((::wglMakeCurrent(hDC,m_hRC))==FALSE)
	{
		return FALSE;
	}
	return TRUE;

	//下面可以进行画图操作了	
	//初始化整个场景和OpenGL的状态变量
	// OpenGL场景初始化(光照、雾化等〕
//	GetClientRect(&m_oldRect);
	GetDlgItem(IDC_STATIC_PictureRender)->GetClientRect(&m_oldRect);//获取图片控件的大小
	//*--设置OpenGL初始状态模式---*/
	///设置清屏颜色为白色
	glClearColor(1.0f,1.0f,1.0f, 1.0f);	
	//清除颜色缓冲区
	glClear(GL_COLOR_BUFFER_BIT);
	glEnable( GL_COLOR_MATERIAL );//启动颜色材料模式,使得在光照下模型颜色有用-不加这一句,模型颜色全为黑白色
	//设置深度缓存的清除值 ,Depth Buffer Setup
	glClearDepth(1.0f);	
	glClear(GL_DEPTH_BUFFER_BIT);
	//做深度测试,实现三维场景的消隐
	glEnable(GL_DEPTH_TEST);
	//深度测试函数,GL_LEQUAL:深度小或相等当前深度的时候也渲染 
	glDepthFunc(GL_LEQUAL);
	//可以调用函数glHint()对图像质量和绘制速度之间的权衡作一些控制
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	

	/*表示把渲染的图像融合到目标区域。也就是说源的每一个像素的alpha
	都等于自己的alpha,目标的每一个像素的alpha等于1减去该位置源像素
	的alpha。 因此不论叠加多少次,亮度是不变的。*/

	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	//glEnable(GL_TEXTURE_RECTANGLE_ARB);

	glEnable(GL_NORMALIZE);//打开法线矢量自动归一化功能

	GLfloat      fAspect;
	
	if (m_oldRect.bottom)
		fAspect = (GLfloat)m_oldRect.right/m_oldRect.bottom;
	else    
		fAspect = 1.0f;
	fNearPlane = 0.1f;//透视投影近平面
	fFarPlane = 1000.0f;//透视投影远平面		
	cameraPos[2] =-(fNearPlane + fFarPlane)/ 40.0f;
	cameraPos[0]=-0;
	cameraPos[1]=-0;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	//设置透视投影矩阵
	gluPerspective(45, fAspect, fNearPlane, fFarPlane);
//	glOrtho(0,10000000,0,10000000,-2000,10000000);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	SwapBuffers(m_pDC); 
	return TRUE;
}


在对话框类中的初始化对话框函数OnInitDialog(),初始化OpenGL;

 

 

BOOL CGriding::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	
	// TODO:  在此添加额外的初始化
	
	/*-------OpenGL绘图初始化相关---------------------*/
	CWnd *wnd=GetDlgItem(IDC_STATIC_PictureRender);//获取图片控件的窗口指针
	m_pDC=::GetDC(wnd->m_hWnd);//将绘图DC与图片窗口关联起来,如果这里是Wnd则绘图区域就设置为整个对//话框
	InitializeOpenGL(m_pDC);//初始化OpenGL函数
	     /// 
	SetTimer(1,1,0); //设置定时器



	return TRUE;  // return TRUE unless you set the focus to a control
	// 异常: OCX 属性页应返回 FALSE
}

 

 

然后为该对话框类添加一个函数RenderScene(),用于三维模型的绘制

 

void CGriding::RenderScene(void)
{
	///
	//基本准备
	glClearColor(1,1,1,1.0);//设置背景颜色为白色
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色缓冲和深度缓冲
	glEnable( GL_COLOR_MATERIAL );//启动颜色材料模式,使得在光照下模型颜色有用-不加这一句,模型颜色全为黑白色
	
	//开始显示图形
	//设置模型视景矩阵,包括了视点变换和模型变换矩阵
	//重置模型矩阵
	glMatrixMode(GL_MODELVIEW);//对模型视景矩阵堆栈应用随后的矩阵操作
	glLoadIdentity();//将当前的用户坐标系的原点移到了屏幕中心:类似于一个复位操作
	//keyboard();//响应键盘按键

	//ShowLegend();//绘制图例,在固定地方,没有改变模型视景矩阵

	/*GL_MODELVIEW矩阵在一个矩阵中包含view矩阵和model 矩阵
	 视点变换:gluLookAt()
	 模型变换:包括平移、旋转和缩放。
	 在程序中, 视点变换必须在模型变换之前完成, 但是投影变换和视口变换可以在绘图之前的任何时候指定。
	 函数glRotate( ) 旋转的是坐标系而不是物体,旋转操作遵循右手规则
	 如果矩阵模式是GL_MODELVIEW 或GL_PROJECTION 时,函数glRotate ( ) 调用后所有绘制的对象将被旋转。
	 glTranslate( ) 函数平移的是坐标系而不是物体。glTranslate( ) 函数可以作用于几何矩阵、投影矩阵和纹理坐标变换矩阵。
	 如果当前矩阵为几何矩阵, 函数的功能则是将物体坐标系的原点移到( x, y, z) 所指的位置,形成新的物体坐标系*/


	glTranslatef(cameraPos[0], cameraPos[1], cameraPos[2]);//平移,用到后面的modelView
	/*modelView的X(m0、m4、m8)、Y(m1、m5、m9)、Z(m2、m6、m10)存的是模型矩阵的X、Y、Z的方向
	,默认为(1,0,0)、(0,1,0)、(0,0,1)。本实例中modelView的X、Y、Z的方向保持不变,值也
	没有变化*/
	glGetFloatv(GL_MODELVIEW_MATRIX, modelView); 
	glRotatef(m_xCurAngle, 1.0, 0.0, 0.0);
	glRotatef(m_yCurAngle, 0.0, 1.0, 0.0);
	glRotatef(m_zCurAngle, 0.0, 0.0, 1.0);

	
	glPushMatrix();
	
	
	/*---------绘图函数代码区,只需添加绘图代码即可----------------*/
	//这里绘制了三个坐标轴线和一个立方体
	glBegin( GL_LINES );
	glColor3d(1.0, 0.0, 0.0);	// X轴 红色
	glVertex3d(0.0, 0.0, 0.0); 
	glVertex3d(1.0, 0.0, 0.0);
	glColor3d(0.0, 1.0, 0.0);	// Y轴 绿色
	glVertex3d(0.0, 0.0, 0.0);
	glVertex3d(0.0, 1.0, 0.0);
	glColor3d(0.0, 0.0, 1.0);	// Z轴 蓝色
	glVertex3d(0.0, 0.0, 0.0); 
	glVertex3d(0.0, 0.0, 1.0);
	glEnd();
	glColor3f(1.0, 0.0, 0.0);
	glutWireCube(1);

	/*--------------------------------------------------------------*/
	
	glPopMatrix();
	

	glDisable(GL_LIGHTING);
	glColor3f(1,0.5,0.5);
	glRasterPos2f(2.0,2.0);
	
	glEnable(GL_LIGHTING);
	glFinish();
	SwapBuffers(m_pDC);//交换缓冲区
}


然后在该类的定时器响应函数OnTime()将RenderScene()函数添加进去,每隔多少毫秒就调用RenderScene(),时间间隔设置的越少就可以形成连续绘制的效果

 

 

void CGriding::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	RenderScene();


	CDialogEx::OnTimer(nIDEvent);
	
}

最后别忘了在该类的析构函数中释放绘图句柄,防止出现内存泄露

 

 

CGriding::~CGriding()
{
	wglMakeCurrent(NULL, NULL) ;               
	wglDeleteContext(m_hRC);                                  //删除绘图描述表
	::ReleaseDC (m_hWnd, m_pDC) ;               //释放设备描述表
}


这样,我们就可以在对话框中看见我们所画的图形了,如果想自己随便画什么模型,那么就需要自己再在RenderScene()函数中封装一个函数,将该函数放置于RenderScene()函数的绘图代码区就好了。

 

绘制的效果如下

 

但是上面中的模型并不能进行交互操作,所以须在CGridingDlg该类中添加鼠标左键,鼠标中键以及鼠标滑轮的消息响应函数,但先需要在该类的构造函数中初始化控制变量。

 

//旋转角度
	xrof=-90.0f;
	yrof=0.0f;
	zrof=40.0f;
	//Z轴移动
	m_fZoomZ=0;
	m_bZoomZ=FALSE;
	translateSpeed = 0.0001f;//平移速度
	//	translateSpeed = 1.08f;//鼠标放大缩小时的缩放大小因子
//	translateSpeed = 0.9f;//鼠标放大缩小时的缩放大小因子
	rotateSpeed = 0.04f;//旋转速度
	walkSpeed = 0.08f;
	inertia = 0.1f;
	m_xCurAngle = -90.0f;
	m_yCurAngle = 0.0f;
	m_zCurAngle = 0.0f;
	bMouseWheelStop = TRUE;
	buttonState = -1; //鼠标按键的状态

 

 

然后在其各种鼠标响应消息中添加以下代码,鼠标左键用于选择,中键用于移动,滑轮用于缩放

 

void CGriding::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	m_MouseDownPoint=CPoint(0,0);
	/*ReleaseCapture():该函数从当前线程中的窗口释放鼠标捕获,并恢复通
	常的鼠标输入处理。捕获鼠标的窗口接收所有的鼠标输入(无论光标的位置
	在哪里),除非点击鼠标键时,光标热点在另一个线程的窗口中。*/
	ReleaseCapture();
	buttonState = -1;
	///
	CDialogEx::OnLButtonUp(nFlags, point);
}


void CGriding::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	m_MouseDownPoint=point;
	/*SetCapture():该函数在属于当前线程的指定窗口里设置鼠标捕获。一旦窗口捕获了鼠标,
	所有鼠标输入都针对该窗口,无论光标是否在窗口的边界内*/
	SetCapture();
	buttonState = GLUT_LEFT_BUTTON;

	CDialogEx::OnLButtonDown(nFlags, point);
}


void CGriding::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	/*GetCapture():该函数取得捕获了鼠标的窗口(如果存在)的句柄。
	在同一时刻,只有一个窗口能捕获鼠标;此时,该窗口接收鼠标的输入
	,无论光标是否在其范围内。*/
	if (GetCapture()==this)
 	{
		float dx, dy;
		dx = point.x - m_MouseDownPoint.x;
		dy = point.y - m_MouseDownPoint.y;
		if (buttonState == GLUT_LEFT_BUTTON)//如果是左键按下,就旋转物体
		{
			m_xCurAngle += dy/10;
			m_yCurAngle += dx/10;
		}
		if (buttonState == GLUT_MIDDLE_BUTTON ||buttonState == GLUT_RIGHT_BUTTON)//如果是鼠标中键或者是右键按下,就平移物体
		{
			float v[3];
			v[0] = dx*translateSpeed;
			v[1] = -dy*translateSpeed;
			v[2] = 0.0f;

			float r[3];
			r[0] = v[0]*modelView[0] + v[1]*modelView[1] + v[2]*modelView[2];
			r[1] = v[0]*modelView[4] + v[1]*modelView[5] + v[2]*modelView[6];
			r[2] = v[0]*modelView[8] + v[1]*modelView[9] + v[2]*modelView[10];
			
			cameraPos[0] += r[0];
			cameraPos[1] += r[1];
			cameraPos[2] += r[2];
		}
		/*if (buttonState== GLUT_WHEEL_UP) 
		//if(nFlags & MK_MBUTTON)
		{
			// left+middle = zoom
			float v[3];
			v[0] = 0.0;
			v[1] = 0.0;
			v[2] = dy*translateSpeed;
			
			float r[3];
			r[0] = v[0]*modelView[0] + v[1]*modelView[1] + v[2]*modelView[2];
			r[1] = v[0]*modelView[4] + v[1]*modelView[5] + v[2]*modelView[6];
			r[2] = v[0]*modelView[8] + v[1]*modelView[9] + v[2]*modelView[10];
			
			cameraPos[0] += r[0];
			cameraPos[1] += r[1];
			cameraPos[2] += r[2];
		} */
		
		m_MouseDownPoint=point;
		InvalidateRect(NULL,FALSE);
	}
	CDialogEx::OnMouseMove(nFlags, point);
}


BOOL CGriding::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	//鼠标中键滑轮滚动进行放大缩小操作

	float v[3];
	v[0] = 0.0;
	v[1] = 0.0;
	v[2] = zDelta*translateSpeed;

	float r[3];
	r[0] = v[0]*modelView[0] + v[1]*modelView[1] + v[2]*modelView[2];
	r[1] = v[0]*modelView[4] + v[1]*modelView[5] + v[2]*modelView[6];
	r[2] = v[0]*modelView[8] + v[1]*modelView[9] + v[2]*modelView[10];

	cameraPos[0] += r[0];
	cameraPos[1] += r[1];
	cameraPos[2] += r[2];

	InvalidateRect(NULL,FALSE);
	return CDialogEx::OnMouseWheel(nFlags, zDelta, pt);
}


void CGriding::OnMButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	m_MouseDownPoint=point;
	SetCapture();
	buttonState = GLUT_MIDDLE_BUTTON;
	CDialogEx::OnMButtonDown(nFlags, point);
}


void CGriding::OnMButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	m_MouseDownPoint=CPoint(0,0);
	ReleaseCapture();
	buttonState = -1;
	CDialogEx::OnMButtonUp(nFlags, point);
}


这样我们就可以旋转、放大、缩放物体了

 


 

如果您觉得这篇博文有用,请访问我的个人站:http://www.stubbornhuang.com,更多博文干货等着您。


 

 


 

 

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HW140701

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值