OpenGL与Memory DC

来源:互联网 发布:js 动态加载js 编辑:程序博客网 时间:2024/06/07 02:41

  //转载自http://www.programfan.com/blog/article.asp?id=40490

 GDI绘图时,双缓冲是经常采用的技术。其中关键的一步,就是要先把图形绘制到内存设备环境中,然后拷贝到屏幕上。 

  OpenGL本身已经有双缓冲的功能了。但是因为开发需要,在采用OpenGL的绘图程序中,我还是需要首先在内存设备环境上绘制一个物体,然后保存为bmp图像。但是采用类似于GDI相似的方法,却发现图像是一片空白。

 

  代码如下:

 

HBITMAP  GetObjBitmap(LPRECTlpRect, BOOL bSave, CString filename)

   {

// 屏幕和内存设备描述表

   HDC  hScrDC,hMemDC;  

  

   // 为屏幕创建设备描述表

    hScrDC =CreateDC("DISPLAY", NULL, NULL, NULL);

   

   // 为屏幕设备描述表创建兼容的内存设备描述表

    hMemDC =CreateCompatibleDC(hScrDC);

 

// 图像宽度和高度

    intnBMPWidth,nBMPHeight;

   nBMPWidth = nBMPHeight = 128;

 

   // 图像格式参数

int iPixel = 32;

   LPBITMAPINFO lpbmih = new BITMAPINFO;

   lpbmih->bmiHeader.biSize =sizeof(BITMAPINFOHEADER);

   lpbmih->bmiHeader.biWidth =nBMPWidth;

   lpbmih->bmiHeader.biHeight =nBMPHeight;

   lpbmih->bmiHeader.biPlanes = 1;

   lpbmih->bmiHeader.biBitCount =iPixel;

   lpbmih->bmiHeader.biCompression =BI_RGB;

   lpbmih->bmiHeader.biSizeImage = 0;

   lpbmih->bmiHeader.biXPelsPerMeter =0;

   lpbmih->bmiHeader.biYPelsPerMeter =0;

   lpbmih->bmiHeader.biClrUsed = 0;

   lpbmih->bmiHeader.biClrImportant = 0;

 

    BYTE*pBits;

   // 创建一个与屏幕设备描述表兼容的位图

    hBitmap= CreateDIBSection(hMemDC,lpbmih,DIB_PAL_COLORS,(void**)&pBits,NULL,0);

 

   // 把新位图选到内存设备描述表中

   hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);

 

    CRectrc(0,0,nBMPWidth,nBMPHeight);   

   //利用OpenGL在内存dc绘制图形

   Display(hMemDC,rc,3);   

 

    hBitmap= (HBITMAP)SelectObject(hMemDC, hOldBitmap);

   

   //清除

   DeleteDC(hScrDC);

   DeleteDC(hMemDC);

}

 

  检查了好多遍,都没有发现问题,但生成的图像就是一片空白。

 

    Google了一下,有人跟我遇到一模一样的问题,也在论坛上提问。一开始越看越开心,也越激动,到后来越来越郁闷了,因为没有给出解决方案,不过还是给了一些线索的。我也发现了一些问题,顺藤摸瓜,顺利解决了。

 

(1)第一个问题就是Display函数。以前的OpenGL程序都是直接绘制到屏幕的dc,因此从程序启动开始我都是只设置了一次OpenGL环境。设置OpenGL环境的代码如下:

void CModel3Ds::InitOpenGL(CDC*pDC)

{

   PIXELFORMATDESCRIPTOR pfd=

   {

     sizeof(PIXELFORMATDESCRIPTOR),

     1,

     PFD_DRAW_TO_WINDOW |

    PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER

     PFD_TYPE_RGBA,

     24,

     0,0,0,0,0,0,

     0,0,0,0,0,0,0,

     32,

     0,0,

     PFD_MAIN_PLANE,

     0,

     0,0,0

   };

 

   intpixelFormat=ChoosePixelFormat(pDC->m_hDC,&pfd);

   SetPixelFormat(pDC->m_hDC,pixelFormat,&pfd);

   m_hRC=wglCreateContext(pDC->m_hDC);   

}

    既然现在要绘制到新的内存dc,因此要重新设置一次,代码修改如下:

 

             //利用OpenGL绘制图形

      InitOpenGL(CDC::FromHandlehMemDC);

      Display(hMemDC,rc,3);   

 

     我想这次应该搞定了,但图像还是一片空白。

 

(2) 第二个问题在于InitOpenGL函数。以前对PIXELFORMATDESCRIPTOR这个结构了解不多,打开MSDN查阅了就发现有点猫腻在里面。

 

   这个结构体的第三个成员是标志位,有一个PFD_DRAW_TO_WINDOW支持绘图到窗口,原来还有一个PFD_DRAW_TO_BITMAP支持绘图到内存环境,PFD_SUPPORT_GDI支持GDI绘制。MSDN解释如下:

 

PFD_DRAW_TO_WINDOW

The buffer can drawto a window or device surface.

PFD_DRAW_TO_BITMAP

The buffer can drawto a memory bitmap.

PFD_SUPPORT_GDI

The buffer supports GDI drawing.This flag and PFD_DOUBLEBUFFER are mutually exclusive in thecurrent generic implementation.

 

 

于是修改代码如下,成功解决问题。

 

void CModel3Ds::InitOpenGL(CDC*pDC)

{

   PIXELFORMATDESCRIPTOR pfd=

   {

     sizeof(PIXELFORMATDESCRIPTOR),

     1,

     PFD_DRAW_TO_WINDOW |PFD_DRAW_TO_BITMAP

     PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER|PFD_SUPPORT_GDI

     PFD_TYPE_RGBA,

     24,

     0,0,0,0,0,0,

     0,0,0,0,0,0,0,

     32,

     0,0,

     PFD_MAIN_PLANE,

     0,

     0,0,0

   };

 

   intpixelFormat=ChoosePixelFormat(pDC->m_hDC,&pfd);

   SetPixelFormat(pDC->m_hDC,pixelFormat,&pfd);

   m_hRC=wglCreateContext(pDC->m_hDC);   

}

 

(3)但是还有一个问题。PFD_SUPPORT_GDIPFD_DOUBLEBUFFER是不能共存的,后者是双缓冲的重要标志。因此最好的解决方案就是2套初始化方案,如下:

 

// 屏幕dc相关的OpenGL环境设置

void CModel3Ds::InitOpenGLWithScreenDC(CDC*pDC)

{

   PIXELFORMATDESCRIPTOR pfd=

   {

     sizeof(PIXELFORMATDESCRIPTOR),

     1,

     PFD_DRAW_TO_WINDOW |

    PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER

     PFD_TYPE_RGBA,

     24,

     0,0,0,0,0,0,

     0,0,0,0,0,0,0,

     32,

     0,0,

     PFD_MAIN_PLANE,

     0,

     0,0,0

   };

 

   intpixelFormat=ChoosePixelFormat(pDC->m_hDC,&pfd);

   SetPixelFormat(pDC->m_hDC,pixelFormat,&pfd);

   m_hRC=wglCreateContext(pDC->m_hDC);   

}

 

// 内存dc相关的OpenGL环境设置

void CModel3Ds::InitOpenGLWithMemoryDC(CDC*pDC)

{

   PIXELFORMATDESCRIPTOR pfd=

   {

     sizeof(PIXELFORMATDESCRIPTOR),

     1,

     PFD_DRAW_TO_WINDOW |PFD_DRAW_TO_BITMAP

     PFD_SUPPORT_OPENGL| PFD_SUPPORT_GDI

     PFD_TYPE_RGBA,

     24,

     0,0,0,0,0,0,

     0,0,0,0,0,0,0,

     32,

     0,0,

     PFD_MAIN_PLANE,

     0,

     0,0,0

   };

 

   intpixelFormat=ChoosePixelFormat(pDC->m_hDC,&pfd);

   SetPixelFormat(pDC->m_hDC,pixelFormat,&pfd);

   m_hRC=wglCreateContext(pDC->m_hDC);   

}

0 0