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

来源:互联网 发布:和平网络电视频道地址 编辑:程序博客网 时间:2024/04/30 19:49

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

(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);//初始化OpenGLCRect 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;//初始化象素格式以及选取合适的格式来创建RCPIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // pfd结构的大小 1, // 版本号 PFD_DRAW_TO_WINDOW | // 支持在窗口中绘图 PFD_SUPPORT_OPENGL | // 支持 OpenGL PFD_DOUBLEBUFFER, // 双缓存模式 PFD_TYPE_RGBA, // RGBA 颜色模式 24, // 24 位颜色深度 ,color depth0, 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来创建RCm_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 SetupglClearDepth(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 = zoomfloat 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);}

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











0 0