查看: 2363|回复: 8

难题!MFC下将OSG内容渲染到位图,为什么会闪烁,并且模型放到时渲染不正确??

[复制链接]

该用户从未签到

发表于 2011-2-13 23:11:58 | 显示全部楼层 |阅读模式
高手们:
我现在要使用OSG绘制三维,使用Windows GDI绘制二维图形,将两者的内容叠加在一起,在窗口中显示。我查看了osgViewer::GraphicsWindowWin32这个类的源代码,发现他是将OpenGL直接与窗口的DC环境关联,这样就无法使用GDI再进行绘制二维图形。我在这个类中创建了一个与窗口兼容的MemoryDC,创建一个Bitmap与MemoryDC关联。略加改动代码,是的OSG的内容可以渲染到位图上,然后在适当的时机,将MemoryDC的内容通过函数“BitBlt”复制到窗口上。
   我的这种方法,在大部分情况下是争取的,不足是窗口闪烁严重。我使用了临界区保护MemoryDC,使得它不被重入。但是在放大模型(视点拉近时),渲染的不正确。两个图说明我的问题:图1是大部分正常情况

图1

图1

文字hello world是我用GDI绘制的文本。

图2

图2

显示不正常了,而且文本也闪烁,时而可见,时而不可见。
哪位朋友,能够给我提供些思路?
下不要追究我为什么要使用OSG和GDI结合了,我知道只使用OSG也可以绘制二维,但是我想换种方法。

该用户从未签到

发表于 2011-2-14 23:38:51 | 显示全部楼层
怎么没有人回答我的问题啊!
下一步我准备将位图保存到文件中,看看效果。

该用户从未签到

发表于 2011-2-15 08:08:01 | 显示全部楼层
抱歉我从未做过这样的事情,并且我认为渲染上下文和GDI绘图的是很难并存的——如果可以的话,应该早就有人做过类似的3DGUI才对,然而开源世界在这方面似乎却是一片空白——我个人如果遇到类似的工作,一定会设法采取别的方式来规避处理 :-)

该用户从未签到

发表于 2011-2-15 11:32:28 | 显示全部楼层
本帖最后由 yin_savage 于 2011-2-15 11:46 编辑

发现这个帖子和前面的帖子“关于在osg绘制窗口使用gdi函数的问题”,很类似的,我在那里回复了。
若是要输出位图,其实Array的那个osgPoster就是很NB的范例了。

该用户从未签到

发表于 2011-2-15 11:36:46 | 显示全部楼层
本帖最后由 yin_savage 于 2011-2-15 11:47 编辑

就你上面的截图来看,你无非是想做一个HUD对象,为什么要使用GDI呢?直接使用HUD不是更完美?

该用户从未签到

发表于 2011-2-15 12:00:51 | 显示全部楼层
截图的示例仅仅是示意。我会用GDI绘制更多的元素(位图、文本,多边形),做类似于热点的功能。虽然三维中可可以同样做到,我想GDI画线是不是锯齿没那么严重啊!另外,我会建两套数据,一套是OSG自己的场景数据,另一套数据是专门用于GDI的,他们数据不同,而且各自响应事件。
我想知道第二个截图,到底是缺少了什么东西?是没有渲染完成,还是OpenGL哪里的设置不正确?

该用户从未签到

发表于 2011-2-15 12:13:41 | 显示全部楼层
我看了4楼专家在““关于在osg绘制窗口使用gdi函数的问题””的回复,它的回复如下:
----------------------
OpenGL和GDI是可以部并存的,不过,OSG就未必能够了。
原因:
GDI绘图,通常(99%的时候)需要在WM_PAINT消息下绘图,若是使用MFC,那么就是在OnPaint函数里面绘图,这是没什么问题的,完全遵从微软的要求。GDI绘图不放在WM_PAINT消息下面,那么窗口客户区更新的时候,绘制的东西就消失了。据此理,所有需要往窗口客户区绘图的操作,原则上都需要放在WM_PAINT消息下面。
而OSG通常没这么做,大部分图形引擎都没有这么做。为了避免窗口客户区更新时能够维持窗口画面,就必须不断的绘制,比如做一个定时器,或者开一个线程,或者在程序空闲的时候,不间断地往窗口上绘图。这样就可以避免窗口客户区更新是画面被擦除。
但是,这样又导致了一个冲突:这种不间断地绘图,势必会覆盖在WM_PAINT消息下的绘图结果。

要解决你的问题,有两个办法:
1、若你的程序不是一个实时3D程序,比如CAD之类的程序,那么完全没有必要“不间断地绘图”,只需要将OSG的frame函数移到WM_PAINT消息里面去执行即可。在frame函数执行完了,再执行你的GDI代码,问题解决。
2、若你的程序的确是一个实时3D程序,那么,你在哪里调用的osg的frame函数?在调用完frame函数之后,立即开始你的GDI绘图(注意,此时GDI绘图所需要的HDC,使用GetDC函数获取,不得使用BeginPaint或者MFC的CPaintDC对象),也能达到一样的效果。此时,WM_PAINT消息下面的任何绘图操作都是看不到的,最好直接删除掉WM_PAINT消息的响应代码(若不删掉该消息的处理,那么一定要保证该响应代码里面有BeginPaint和EndPaint的成对调用,或者MFC的CPaintDC对象构造和析构)。
--------------------
我使用的就是你提出的方法中的“第2个方法”。我以前写过一个OpenGL与GDI兼容的程序。现在出现这个问题,主要是我刚刚接触OSG,不是很清楚它到底在哪些地方做了OpenGL的设置。
而且奇怪的是,如果不将视点拉近,效果还可以。
现在项目紧张,先换个方法绕过去吧!过后还要“刨根问底”一下。

该用户从未签到

发表于 2011-2-15 16:20:12 | 显示全部楼层
就我猜测,你是否在GDI的绘图代码里面做了一些类似于擦除的操作?注意了,你的GDI代码千万不能直接或者间接的调用到BeginPaint和EndPaint函数。否则会导致一些问题的(比如闪烁之类的)。而且,你的GDI代码一定要在frame执行完成之后才能开始,而且GDI绘图所需要的窗口DC,最好在frame调用完成之后才能获取,不能事先获取了放着,这些都是可能导致问题的。
不过上述都是我的猜测。GDI和OpenGL本身是不冲突的,也没有理由冲突,因为他们最终都是要把图像绘制到DC上面去,一回事。

至于你说到的,使用GDI是否会改善锯齿方面的问题,这个我个人认为,绝无改善的可能。相反,使用OpenGL倒还有改善的可能,比如你打开了反走样开关,效果还是蛮好的:
const bool bSupported = osg::isGLExtensionOrVersionSupported(0u, "WGL_ARB_multisample", fltOGLVersion);
if(bSupported)
{
    pTraits->samples = 4;
    pTraits->sampleBuffers = 1;
}
上面的变量pTraits,就是osg::GraphicsContext::Traits类型的指针,初始化osg的时候所需要自行new出来的对象。osg在场景反走样方面,还是做了许多包装的,挺好用的。

该用户从未签到

发表于 2011-2-16 12:10:14 | 显示全部楼层
很有意义的讨论,了解下~~
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表