OpenGL與Memory DC

  //轉載自http://www.programfan.com/blog/article.asp?id=40490

  用GDI繪圖時,雙緩衝是經常採用的技術。其中關鍵的一步,就是要先把圖形繪製到內存設備環境中,然後拷貝到屏幕上。 

  OpenGL本身已經有雙緩衝的功能了。但是因爲開發需要,在採用OpenGL的繪圖程序中,我還是需要首先在內存設備環境上繪製一個物體,然後保存爲bmp圖像。但是採用類似於GDI相似的方法,卻發現圖像是一片空白。

 

  代碼如下:

 

HBITMAP  GetObjBitmap(LPRECT lpRect, BOOL bSave, CString filename)

    {

// 屏幕和內存設備描述表

    HDC  hScrDC, hMemDC;  

  

    // 爲屏幕創建設備描述表

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

   

    // 爲屏幕設備描述表創建兼容的內存設備描述表

    hMemDC = CreateCompatibleDC(hScrDC);

 

// 圖像寬度和高度

    int nBMPWidth,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);

 

    CRect rc(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

    };

 

    int pixelFormat=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 draw to a window or device surface.

PFD_DRAW_TO_BITMAP

The buffer can draw to a memory bitmap.

PFD_SUPPORT_GDI

The buffer supports GDI drawing. This flag and PFD_DOUBLEBUFFER are mutually exclusive in the current 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

    };

 

    int pixelFormat=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

    };

 

    int pixelFormat=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

    };

 

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

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

    m_hRC=wglCreateContext(pDC->m_hDC);   

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章