探索未知种族之osg类生物---起源 osg渲染三维
任何程序都是有生命的,是生命就需要呼吸。例如普通的windows程序,当运行完main()函数后,就需要进入消息循环,来监听用户的各种操作,以便做出及时的回应。这样的每次循环就像生命的每次呼吸,来维持生命体征。
osg的程序不仅仅需要消息循环来监听用户的鼠标、键盘等操作,同时也得具备了渲染循环。当然随着我们的对osg的深入了解会发现,osg的事件监听和渲染循环是串行的。但是当我们把osg与MFC(QT)等结合时,相应UI上的鼠标,键盘事件的同时也要兼顾可能发生在osg中的效果,所以一般的osg程序起码需要两个并行的线程(例如osg与qt结合使用,为了保持足够灵敏的相应速度就需要把QTUI和osg渲染看成两种生命,分为两个线程)来维持它的正常运行。我们今天就是要解读osg程序赖以生存的每次呼吸。
首先我们得找到osg是用什么呼吸的,就想地球上的一般生物都是用鼻子呼吸,我们又大概得知道鼻子长在生物的那个位置。这样我们才可以开始我们的研究。当然我们肯定得有osg的源码,就像我们研究生物的呼吸先得有这种生物的身体。有了身体我们还得持续的观察一个有生命的生物,所以我们最先得到osg的可运行的程序就是example中的各种程序。其中大部分的程序main()函数的最后部分都是调用一下viewer.run()。
所以我们可以有一个模糊的判断这一类的通过调用viewer类的run函数的程序,他的呼吸系统可能是通过run完成的。但是run()函数是一个单独的一行,按说他执行完毕以后程序就会结束了,所以我们有了新的判断osg的每一帧的调用的入口是在run()函数中的。这是osg程序存在的一种形式(或者叫独立运行模式)。Osg还有另一种存在形式,就是和各种UI混合使用,例如qt与osg结合使用,MFC与osg结合使用等等。我们可以从examples/osgviewerQt 的例子,可以根据上一个的思路,呼吸不是一次性的动作,是只要存活就会一直存在的。所以从osgviewerQt.cpp中根据以前的经验定位到timer (计时器),他每次timeout触发时调用的函数update()中一定包含了osg的每一帧的调用的入口。
根据上面两种osg的存活形式,可以进行进一步的确认,究竟哪里才是维持osg生命体征的位置。Viewer.run()函数(OSG Core/osgViewer/Viewer.cpp)最后会继续调用ViewerBase::run()函数(OSG Core/osgViewer/ViewerBase.cpp),
1
2
3
4
5
6
7
8
9
10
11
|
int Viewer::run()
{ if (!getCameraManipulator() && getCamera()->getAllowEventFocus())
{
setCameraManipulator( new osgGA::TrackballManipulator());
}
setReleaseContextAtEndOfFrameHint( false );
return ViewerBase::run();
} |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
int ViewerBase::run()
{ if (!isRealized())
{
realize();
}
const char * run_frame_count_str = getenv ( "OSG_RUN_FRAME_COUNT" );
unsigned int runTillFrameNumber = run_frame_count_str==0 ? osg::UNINITIALIZED_FRAME_NUMBER : atoi (run_frame_count_str);
while (!done() && (run_frame_count_str==0 || getViewerFrameStamp()->getFrameNumber()<runTillFrameNumber))
{
double minFrameTime = _runMaxFrameRate>0.0 ? 1.0/_runMaxFrameRate : 0.0;
osg::Timer_t startFrameTick = osg::Timer::instance()->tick();
if (_runFrameScheme==ON_DEMAND)
{
if (checkNeedToDoFrame())
{
frame();
}
else
{
// we don't need to render a frame but we don't want to spin the run loop so make sure the minimum
// loop time is 1/100th of second, if not otherwise set, so enabling the frame microSleep below to
// avoid consume excessive CPU resources.
if (minFrameTime==0.0) minFrameTime=0.01;
}
}
else
{
frame();
}
// work out if we need to force a sleep to hold back the frame rate
osg::Timer_t endFrameTick = osg::Timer::instance()->tick();
double frameTime = osg::Timer::instance()->delta_s(startFrameTick, endFrameTick);
if (frameTime < minFrameTime) OpenThreads::Thread::microSleep( static_cast (1000000.0*(minFrameTime-frameTime)));
}
return 0;
} |
我们在ViewerBase::run()中继续耐心的寻找就会发现有一个特殊的函数frame(),为什么特殊呢?因为frame的英文的意思就是’帧’,而我们学渲染都知道’帧’代表屏幕上一幅画,这和osg库的本质就联系在了一起。Osg就是一个库,一个在计算机屏幕上作画的库。所以ViewerBase::frame()就是我们要找的osg中会呼吸的地方。同样我们在examples/osgviewerQt中也会发现,timer每到设定事件就会调用update()函数,而qt的update()函数在内部就会调用paintEvent()函数,我们在osgviewerQt.cpp的paintEvent()函数中也会发现osg::CompositeViewer的update函数,而osg::CompositeViewer继承自ViewerBase,所以最后也会定位到ViewerBase::frame()。这样我们就可以确定osg这类生物的呼吸的入口是ViewerBase::frame()函数。终于我们打开了通往新世界的大门,下一步就是经历轮回,看看osg这类生物是怎么生存的。
欢迎大家来我的新家看一看 3wwang个人博客-记录走过的技术之路
上一篇: Android性能系列-渲染篇