MFC单文档结构,实现OpenGL的绘图,移动,旋转,缩放

MFC单文档结构,实现OpenGL的绘图,移动,旋转,缩放

MFC单文档结构,实现OpenGL的绘图,移动,旋转,缩放_第1张图片



基本知识:

Windows应用程序是使用设备描述表(Device Context,简写为"DC")进行图形的绘制输出,

OpenGL并不使用标准的设备描述表,它使用渲染描述表(Rendering Context,简写为"RC")完成图形图像的映射。

描述表的映射核心是像素格式的设置。

   当进行OpenGL的绘图操作时,实际上是在进行设备像素的操作。OpenGL将数据转化为像素操作写入帧缓存中,就需要知道Windows的像素格式, 或者说需要与其一致起来。在初始化OpenGL时,需要一个叫做PIXELFORMATDESCRIPTOR的结构来完成对像素属性的设置。


Win32提 供了4个函数管理像素格式


  ①ChoosePixelFormat():返回和&pfd指定的像素格式最匹配的DC支持的像素格式的索引值,如果函数调用不成功,返回零。


  ②SetPixelFormat():在DC中设置pixelformat指定的设备的像素格式,如果函数调用成功,返回TRUE。


  ③GetPixelFormat():返回当前DC的像素格式的索引,如果函数调用不成功,返回零。


  ④DescribePixelFormat():用n指定的DC的像素格式的数据设置&pfd,如果函数调用成功,返回DC的像素格式的最大索引,否则,返回零。



    OpenGL的渲染描述表保存着在窗口中用来渲染一个场景所需的信息。一个OpenGL应用程序必须有一个渲染描述表,并且在OpenGL进行绘制之前它应该是当前的。在渲染描述表存入信息后,OpenGL就可以在Windows系统中更新一个窗口的图形状态。

Win32提供了5个函数管理渲染描述表


  ①wglCreateContext():创建一个与给定的DC兼容的RC,调用成功返回RC的句柄,不成功返回NULL。


  ②wglMakeCurrent():使指定的RC成为当前的,调用成功返回GL_TRUE,否则返回GL_FALSE。


  ③wglGetCurrentCotext():获得当前的RC句柄。


  ④wglGetCurrentDC():获得和当前的RC联系的DC句柄。


  ⑤wglDeleteContext():删除RC。



/* Pixel format descriptor */
typedef struct tagPIXELFORMATDESCRIPTOR
{
    WORD  nSize;
    WORD  nVersion;
    DWORD dwFlags;
    BYTE  iPixelType;
    BYTE  cColorBits;
    BYTE  cRedBits;
    BYTE  cRedShift;
    BYTE  cGreenBits;
    BYTE  cGreenShift;
    BYTE  cBlueBits;
    BYTE  cBlueShift;
    BYTE  cAlphaBits;
    BYTE  cAlphaShift;
    BYTE  cAccumBits;
    BYTE  cAccumRedBits;
    BYTE  cAccumGreenBits;
    BYTE  cAccumBlueBits;
    BYTE  cAccumAlphaBits;
    BYTE  cDepthBits;
    BYTE  cStencilBits;
    BYTE  cAuxBuffers;
    BYTE  iLayerType;
    BYTE  bReserved;
    DWORD dwLayerMask;
    DWORD dwVisibleMask;
    DWORD dwDamageMask;
} PIXELFORMATDESCRIPTOR, *PPIXELFORMATDESCRIPTOR, FAR *LPPIXELFORMATDESCRIPTOR;



=============================================================


创建一个单文档结构的MFC程序


=============================================================

窗口创建之前我们须设置窗口风格

WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时裁剪子窗口所覆盖的区域)和

WS_CLIPSIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)

从而避免OpenGL绘制到其他窗口中去。

这些应该在窗口创建之前设置好,则代码位于PreCreateWindow()中。

BOOL CLDSView::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: 在此处通过修改
    //  CREATESTRUCT cs 来修改窗口类或样式
    cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
    return CView::PreCreateWindow(cs);
}


=============================================================


在窗口创建时,则需要将OpenGL的环境设置好

int CLDSView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CView::OnCreate(lpCreateStruct) == -1)
        return -1;

    // TODO:  在此添加您专用的创建代码
    InitializeOpenGL();

    return 0;
}


定义如下变量:

CClientDC* m_pDC;          // Device Context 设备描述表

HGLRC m_hRC;               // Render Context 着色描述表

Windows应用程序是使用设备描述表(Device Context,简写为"DC")进行图形的绘制输出,

OpenGL并不使用标准的设备描述表,它使用渲染描述表(Rendering Context,简写为"RC")



初始化OpenGL如下:

BOOL CLDSView::InitializeOpenGL()
{
    m_pDC = new CClientDC (ThisWindow()); 
    if(m_pDC == NULL)        
    {        
        AfxMessageBox(_T("new CClientDC  ERROR!"));
        return FALSE;        
    }

    // 设置当前的绘图像素格式   
    if(!SetupPixelFormat())  // 在初始化OpenGL环境的时候,必须要设置好像素格式
    {        
        return FALSE;        
    }

    // 创建绘图描述表   
    m_hRC = ::wglCreateContext (m_pDC->GetSafeHdc ());   // DC转RC
    if(m_hRC == 0)
    {
        AfxMessageBox(_T("创建绘图描述表失败"));
        return FALSE;
    }
    
    // 使绘图描述表为当前调用线程的当前绘图描述表  
    if(::wglMakeCurrent (m_pDC->GetSafeHdc (), m_hRC)==FALSE)
    {
        AfxMessageBox(_T("调用线程的当前绘图描述表失败"));
        return FALSE;
    }

    glClearColor(0.28f,0.28f,0.28f,0.0f); // 设置清屏颜色
    glClearDepth(1.0f);        // 指定清除深度缓存时使用的深度值
    glEnable(GL_DEPTH_TEST);

    return TRUE;
}

一般,应用程序先调用函数wglCreateContext,然后调用函数wglMakeCurrent使渲染上下文和设备显示相联系,因此OpenGL 的绘图操作都可以在设备显示上完成。完成绘图以后,通过再一次调用函数wglMakeCurrent(参数设置为NULL)可以使渲染上下文释放DC,最后调用函数wglDeleteContext来删除渲染上下文。(http://blog.csdn.net/chuajiang/article/details/1767520)


在创建一个图形操作表之前,首先必须设置像素格式即产生一个RC的第一步是定义窗口的像素格式。

像素格式含有设备绘图界面的属性,这些属性包括绘图界面是用RGBA模式还是颜色表模式,像素缓存是用单缓存还是双缓存,以及颜色位数、深度缓存和模板缓存所用的位数,还有其它一些属性信息。像素格式决定窗口着所显示的图形在内存中是如何表示的。


在下面将在SetupPixelFormat()函数中对这些参数的设置。代码如下:


BOOL CLDSView::SetupPixelFormat()
{
    static PIXELFORMATDESCRIPTOR pfd =   
    {  
        sizeof(PIXELFORMATDESCRIPTOR),  // pfd结构的大小    
        1,                                                        // 版本号    
        PFD_DRAW_TO_WINDOW |             // 支持在窗口中绘图    
        PFD_SUPPORT_OPENGL |                    // 支持 OpenGL    
        PFD_DOUBLEBUFFER,                        // 双缓存模式    
        PFD_TYPE_RGBA,                                // RGBA 颜色模式    
        24,                                                    // 24 位颜色深度    
        0, 0, 0, 0, 0, 0,               // 忽略颜色位    
        0,                                     // 没有非透明度缓存    
        0,                                    // 忽略移位位    
        0,                                    // 无累计缓存    
        0, 0, 0, 0,                      // 忽略累计位    
        32,                                // 32 位深度缓存        
        0,                                    // 无模板缓存    
        0,                                    // 无辅助缓存    
        PFD_MAIN_PLANE,      // 主层    
        0,                                    // 保留    
        0, 0, 0                         // 忽略层,可见性和损毁掩模    
    };  

    // 为设备描述表得到最匹配的像素格式   
    int m_nPixelFormat = ::ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd);    
    if ( m_nPixelFormat == 0 )        
    {        
        return FALSE;        
    }
     // 设置最匹配的像素格式为当前的像素格式
    if ( ::SetPixelFormat(m_pDC->GetSafeHdc(), m_nPixelFormat, &pfd) == FALSE)        
    {        
        return FALSE;        
    }    
    return TRUE;
}


上述则将OpenGL初始化成功


=============================================================

视图设置

在初始化OpenGL环境后,我们需要对视图进行设置

窗口创建好后,我们需要将OpoenGL的视口与MFC的客户区进行覆盖,进行OpenGL的图形显示


而且当主程序窗口进行缩放后,会影响到OpenGL图形的显示


void CLDSView::OnSize( int cx, int cy )
{

    if ( 0 >= cx || 0 >= cy )
    {
        return;
    }

    glViewport(0, 0, cx, cy);        // 视口覆盖整个程序的客户区
    glMatrixMode(GL_PROJECTION);    // 随后的矩阵操作glOrtho应用于投影矩阵中
    glLoadIdentity();    // 重置当前指定的矩阵为单位矩阵
    glOrtho(-cx ,  cx, -cy, cy,-10000000,10000000);    // 正交投影

}

我们还可以用透视投影操作:

将 

glOrtho(-cx ,  cx, -cy, cy,-10000000,10000000);    // 正交投影

转换为:

GLfloat aspect_ratio = (GLdouble)cx/(GLdouble)cy; // 防止窗口缩放变形
gluPerspective(60.0f, aspect_ratio, 0.01f, 1500.0f);



=============================================================


将环境搭建好了,视口也设置好了,下面,我们就要进行图形绘制


void CLDSView::OnDraw(CDC* /*pDC*/)
{
    CLDSDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    // TODO: 在此处为本机数据添加绘制代码
    RenderScene();    // 绘制图形
}


    
void CLDSView::RenderScene()
{
    //清除颜色缓冲区和深度缓冲区
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    if (!::wglMakeCurrent(m_pDC->GetSafeHdc(),  m_hRC)) 
    {
        return;
    }
    glMatrixMode(GL_MODELVIEW);    // 图形绘制在模型视图中绘制
    glLoadIdentity();

    // 后面描述
    glTranslated(m_dXPos, m_dYPos, 0.0);    // XY方向上的移动
    glRotated(m_dXAngle, 1.0, 0.0, 0.0);    // 旋转
    glRotated(m_dYAngle, 0.0, 1.0, 0.0);
    glScalef(m_dScale, m_dScale, m_dScale); // 缩放 


    GLfloat dAsixLen = 600.0f;

    glBegin(GL_LINES);     // 绘制三根轴
        glColor3f(1.0f, 0.0f, 0.0f);    // X
        glVertex3f(0.0f, 0.0f, 0.0f);
        glVertex3f(dAsixLen, 0.0f, 0.0f);    

        glColor3f(0.0f, 1.0f, 0.0f);    // Y
        glVertex3f(0.0f, 0.0f, 0.0f);
        glVertex3f(0.0f, dAsixLen, 0.0f);

        glColor3f(0.0f, 0.0f, 1.0f);    // Z
        glVertex3f(0.0f, 0.0f, 0.0f);
        glVertex3f(0.0f, 0.0f, dAsixLen);
    glEnd(); 
     SwapBuffers( m_pDC->GetSafeHdc() );
}




=============================================================

按住左键后移动旋转图形

定义一个成员变量用于保存状态点

CPoint m_ptCurrent;


void CLDSView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    SetCapture();
    m_ptCurrent = point;


    __super::OnLButtonDown(nFlags, point);
}


void CLDSView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    ReleaseCapture();
    mouseOpt = OTHER;


    __super::OnLButtonUp(nFlags, point);
}



void CLDSView::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值

   m_dXAngle += (point.y - m_ptCurrent.y)/6; // 注意 X Y
   m_dYAngle += (point.x - m_ptCurrent.x)/6;

   m_ptCurrent = point;

    RedrawWindow()
;
    __super::OnMouseMove(nFlags, point);
}


RedrawWindow();后,程序窗口进行刷新,刷新时,绘制图形,

则就调用了 

glRotated(m_dXAngle, 1.0, 0.0, 0.0);
glRotated(m_dYAngle, 0.0, 1.0, 0.0);

进行旋转


=============================================================

按住中键后移动移动图形


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

    __super::OnMButtonDown(nFlags, point);
}


void CLDSView::OnMButtonUp(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    ReleaseCapture();
    __super::OnMButtonUp(nFlags, point);
}


void CLDSView::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    //if(this == GetCapture())
    //    return;

    m_dXPos += (point.x - m_ptCurrent.x)*2;  // 因为glOrtho截取的是两倍的屏幕大小
    m_dYPos += (m_ptCurrent.y - point.y )*2;

    m_ptCurrent = point;

    RedrawWindow();

    __super::OnMouseMove(nFlags, point);
}


还可以通过键盘的方向键进行图形的移动



void CLDSView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    switch(nChar)
    {
    case VK_UP:
        m_dYPos += 1;
        break;

    case VK_DOWN:
        m_dYPos -= 1;
        break;

    case VK_LEFT:
        m_dXPos -= 1;
        break;

    case VK_RIGHT:
        m_dXPos += 1;
        break;
    }
    RedrawWindow();

    __super::OnKeyDown(nChar, nRepCnt, nFlags);
}


RedrawWindow();后,程序窗口进行刷新,刷新时,绘制图形,

则就调用了 

glTranslated(m_dXPos, m_dYPos, -10.0);    // 移动

进行移动



=============================================================

鼠标中键滚动进行图形缩放

BOOL CLDSView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

if (m_dScale > 0.06  && m_dScale <8 )

{

m_dScale += (zDelta /120) * 0.02;

}

else

{

m_dScale -= (zDelta /120) * 0.02;

}


CString strDebugOut;

strDebugOut.Format(_T("%.3f\n"), m_dScale);

OutputDebugString(strDebugOut);


mouseOpt = SCALE;


RedrawWindow();


return __super::OnMouseWheel(nFlags, zDelta, pt);

}



=============================================================

可以绘制,并进行一些简单的操作后,程序退出后呢?

我们需要做一些清理工作

void CLDSView::OnDestroy()
{
    CView::OnDestroy();

    // TODO: 在此处添加消息处理程序代码
    if(::wglMakeCurrent (0,0) == FALSE)    // 使渲染上下文释放DC
    {
        AfxMessageBox(_T("wglMakeCurrent Error!"));
    }
    if(::wglDeleteContext (m_hRC)==FALSE)    // 删除渲染上下文

    {
        AfxMessageBox(_T("不能删除RC-- wglDeleteContext Error"));
    }
    if(m_pDC)
    {
        delete m_pDC;
    }
    m_pDC = NULL;

}


=============================================================



源代码下载:

http://download.csdn.net/detail/yulinxx/7911397


你可能感兴趣的:(OpenGL)