openGL+MFC导致闪烁、不刷新等问题的解决方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wang15061955806/article/details/49870533
我们知道,在窗口中拖动一个图形的时候,由于边画边显示,会出现闪烁的现象。在GDI中解决这个问题较为复杂,通过在内存中生成一个内存DC,绘画时让画笔在内存DC中画,画完后一次用Bitblt将内存DC“贴”到显示器上,就可解决闪烁的问题。在OpenGL中,我们是通过双缓存来解决这个问题的。一般来说,双缓存在图形工作软件中是很普遍的。双缓存是两个缓存,一个前台缓存、一个后台缓存。绘图先在后台缓存中画,画完后,交换到前台缓存,这样就不会有闪烁现象了。
A:将像素格式定义成支持双缓存的    pixelDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_STEREO_DONTCARE;
B:在物体绘制完成后加入这样一行代码SwapBuffers(hDC),到此OpenGL绘图已经没有闪烁了,但MFC还会反复的重绘背景产生闪烁
C:处理消息循环OnEraseBkgnd,使其不要重绘背景
BOOL CoglView::OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
return TRUE;
//return CView::OnEraseBkgnd(pDC);
}
这样就彻底解决了闪烁问题!


MFC+OpenGL去除闪烁的方法网上很多。例如:http://hi.baidu.com/piaoshi111/blog/item/66dba5ff643caa275c60083b.html

上文相当详细的解释了MFC中使用OpenGL的基本的流程。并给出了实现无闪烁的步骤。

现在给出第一个问题:如果你在MFC生成向导中选择了“拆分窗口”,你会发现即使重载了OnEraseBkgnd,也无法解决闪烁问题!

笔者经过排查,发现问题出现在CMainFrame中MFC帮你生成的如下代码:

[cpp] view plain copy
  1. //在MainFrm.h中:  
  2.     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);  
  3.   
  4. //在MainFrm.cpp中:  
  5. BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,  
  6.     CCreateContext* pContext)  
  7. {  
  8.     return m_wndSplitter.Create(this,  
  9.         2, 2,               // TODO: 调整行数和列数  
  10.         CSize(10, 10),      // TODO: 调整最小窗格大小  
  11.         pContext);  
  12. }  
也就是说MFC为实现拆分窗口帮你重载了OnCreateClient函数。我们知道MainFrm继承自CFrameWnd。我们看一下CFrameWnd的OnCreateClient做了什么:
[cpp] view plain copy
  1. BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)  
  2. {  
  3.     // default create client will create a view if asked for it  
  4.     if (pContext != NULL && pContext->m_pNewViewClass != NULL)  
  5.     {  
  6.         if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)  
  7.             return FALSE;  
  8.     }  
  9.     return TRUE;  
  10. }  

这里就创建了我们初始化OpenGL的视图类CxxxView了。

但是,如果使用了拆分,MFC会自动添加一个类型为CSplitterWnd的对象m_wndSplitter,而你的CxxxView类则仅仅是CSplitterWnd内部的视图罢了。

在改变窗口大小时,CSplitterWnd首先响应ON_WM_SIZE,然后在其响应函数中让CxxxView进一步处理。

CSplitterWnd的窗口大小要略大于其内部的CxxxView。因此CSplitterWnd自身的刷新会影响CxxxView的刷新而而导致闪烁。

那么解决方法,要么不使用拆分,这个绝对没有问题。

如果必须要拆分窗口,那么可能需要派生自己的CSplitterWnd类,修改其行为.

下一个问题,你可能遇到窗口不刷新,只有改变窗口大小的情况才刷新

这是因为你的CxxxView只重载了OnDraw的原因。OnDraw是如何被调用的?

参见*\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\viewcore.cpp

[cpp] view plain copy
  1. /////////////////////////////////////////////////////////////////////////////  
  2. // CView drawing support  
  3. void CView::OnPaint()  
  4. {  
  5.     // standard paint routine  
  6.     CPaintDC dc(this);  
  7.     OnPrepareDC(&dc);  
  8.     OnDraw(&dc);  
  9. }  

响应ON_WM_PAINT消息的是CView,他调用了你重载的OnDraw。正因为这里的CPaintDC dc(this);导致了窗口不能持续刷新。

要知道更详细的原因,请参见http://peipengshuai.blog.163.com/blog/static/1901226620123169431072/

你只需要知道,CPaintDC会从刷新消息队列中移除ON_WM_PAINT即可。移除后,如果窗口没有发生改变,就不会自动刷新。

因此,你需要做的很简单。自己响应你的CxxxView类的ON_WM_PAINT,并不要在里面使用CPaintDC dc(this);(注释掉)即可。

再下一个问题:最大化窗口后,窗口某些区域(如工具栏)无法刷新,留下空白

这个问题尚未得到完美解决方案。参考http://topic.csdn.net/t/20020220/16/534304.html,一说是驱动问题。暂时无法验证。

如果有解决方案,希望读者留言告之。


阅读更多

没有更多推荐了,返回首页