文章目录
- 一、osgViewer:: ViewerBase:: frame()
- 二、void View::setSceneData(osg::Node* node)中重要的操作
- 场景图形-SceneGraph;场景子树-Subgraph;节点-Node;摄像机-Camera;渲染器-Renderer;窗口-Window;视口-Viewport;场景-Scene;视图-View;视景器-Viewer;漫游器-Manipulator;访问器-Visitor;回调-Callback;事件-Event;更新-Update;筛选-Cull;绘制-Draw
- 原文链接:http://bbs.osgchina.org/forum.php?mod=viewthread&tid=509&highlight=OSG%D4%AD%B4%B4%BD%CC%B3%CC%A3%BA%D7%EE%B3%A4%B5%C4%D2%BB%D6%A1
一、osgViewer:: ViewerBase:: frame()
void ViewerBase::frame(double simulationTime)
{
if (_done) return;
// OSG_NOTICE<<std::endl<<"CompositeViewer::frame()"<<std::endl<<std::endl;
if (_firstFrame)
{
viewerInit();
if (!isRealized())
{
realize();
}
_firstFrame = false;
}
advance(simulationTime);
eventTraversal();
updateTraversal();
renderingTraversals();
}
该函数所执行的主要工作如下:
- 如果这是仿真系统启动后的第一帧,则执行viewerInit();此时如果还没有执行realize()函数,则执行它。
- 执行advance函数。
- 执行eventTraversal函数,顾名思义,这个函数将负责处理系统产生的各种事件,诸如鼠标的移动,点击,键盘的响应,窗口的关闭等等,以及摄像机与场景图形的事件回调(EventCallback)。
- 执行updateTraversal函数,这个函数负责遍历所有的更新回调(UpdateCallback);除此之外,它的另一个重要任务就是负责更新DatabasePager与ImagePager这两个重要的分页数据处理组件。
- 执行renderingTraversals函数,这里将使用较为复杂的线程处理方法,完成场景的筛选(cull)和绘制(draw)工作。
1.osgViewer:: View:: init()
void View::init()
{
OSG_INFO<<"View::init()"<<std::endl;
osg::ref_ptr<osgGA::GUIEventAdapter> initEvent = _eventQueue->createEvent();
initEvent->setEventType(osgGA::GUIEventAdapter::FRAME);
if (_cameraManipulator.valid())
{
_cameraManipulator->init(*initEvent, *this);
}
}
- View::osg::ref_ptrosgGA::EventQueue _eventQueue;储存该视景器的事件队列(osgGA::GUIEventAdapter)
- 其中createEvent是分配和返回一个新的GUIEventAdapter事件的指针。
- View::osg::ref_ptrosgGA::CameraManipulator _cameraManipulator为该视景器的操作器的实例,如果希望编写自定义的场景操作器/漫游器,那么覆写并使用osgGA:: MatrixManipulator:: init就可以灵活地初始化自定义漫游器的功能了,它的调用时机就在这里。
- 其中如果调用的是viewer.run(),没有设置操作器,osg会自动设置一个:
int Viewer::run()
{
if (!getCameraManipulator() && getCamera()->getAllowEventFocus())
{
setCameraManipulator(new osgGA::TrackballManipulator());
}
setReleaseContextAtEndOfFrameHint(false);
return ViewerBase::run();
}
int ViewerBase::run()
{
if (!isRealized())
{
realize();
}
unsigned int runTillFrameNumber = osg::UNINITIALIZED_FRAME_NUMBER;
osg::getEnvVar("OSG_RUN_FRAME_COUNT", runTillFrameNumber);
while(!done() && (runTillFrameNumber==osg::UNINITIALIZED_FRAME_NUMBER || 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<unsigned int>(1000000.0*(minFrameTime-frameTime)));
}
return 0;
}
2.osgViewer::Viewer::realize(),窗口和场景的“设置”工作
part1 GraphicsContext
void Viewer::realize(){
Contexts contexts;
getContexts(contexts);}
void Viewer::getContexts(Contexts& contexts, bool onlyValid)
{
typedef std::set<osg::GraphicsContext*> ContextSet;
ContextSet contextSet;
contexts.clear();
if (_camera.valid() &&
_camera->getGraphicsContext() &&
(_camera->getGraphicsContext()->valid() || !onlyValid))
{
contextSet.insert(_camera->getGraphicsContext());
contexts.push_back(_camera->getGraphicsContext());
}
for(unsigned int i=0; i<getNumSlaves(); ++i)
{
Slave& slave = getSlave(i);
osg::GraphicsContext* sgc = slave._camera.valid() ? slave._camera->getGraphicsContext() : 0;
if (sgc && (sgc->valid() || !onlyValid))
{
if (contextSet.count(sgc)==0)
{
contextSet.insert(sgc);
contexts.push_back(sgc);
}
}
}
}
- ViewBase::std::vectorosg::GraphicsContext* Contexts
- contexts是一个保存了osg::GraphicsContext指针的向量组
- Viewer::getContexts函数的作用是获取所有的图形上下文,并保存到这个向量组中来。
首先判断场景的主摄像机_camera是否包含了一个有效的GraphicsContext设备,然后再遍历所有的从摄像机_slaves(一个视景器可以包含一个主摄像级和多个从摄像机),将所有找到的GraphicsContext图形上下文设备记录下来。(其中osg::GraphicsContext*互斥不重复!)- 当程序还没有进入仿真循环,且对于osgViewer::Viewer还没有任何的操作之时,系统是不会存在任何图形上下文的;创建一个新的osg::Camera对象也不会为其自动分配图形上下文。但是,图形上下文GraphicsContext却是场景显示的唯一平台,系统有必要在开始渲染之前完成其创建工作。
假设用户已经在进入仿真循环之前,自行创建了新的Camera摄像机对象,为其分配了自定义的GraphicsContext设备,并将Camera对象传递给视景器,就像osgviewerMFC和osgcamera例子,以及我们在编写与GUI系统嵌合的仿真程序时常做的那样。此时,系统已经不必为图形上下文的创建作任何多余的工作,因为用户不需要更多的窗口来显示自己的场景了。所以就算主摄像机_camera还没有分配GraphicsContext,只要系统中已经存在图形上下文,即可以开始执行仿真程序了。
所以在osgcamera例子中,哪怕有已经有一个主相机(没有gc),但是addsalve了6个带有gc的子相机,也是能正常显示的。
part1.1 通过阅读osgViewer::View::setUpViewInWindow()了解osg最基础的操作
详见这里!!!
GraphicsContext的创建由平台相关的抽象接口类WindowingSystemInterface负责,对于Win32平台而言,这个类是由GraphicsWindowWin32.cpp的Win32WindowingSystem类具体实现的,它创建的显示窗口设备即osgViewer::GraphicsWindowWin32的实例。
当我们尝试使用createGraphicsContext来创建一个图形设备上下文时,系统返回的实际上是这个函数的值:
ref_ptr<GraphicsContext::WindowingSystemInterface> &wsref = windowingSystemInterfaceRef();
return wsref->createGraphicsContext(traits);
part2 DisplaySettings
void Viewer::realize()
{
。。。
// get the display settings that will be active for this viewer
osg::DisplaySettings* ds = _displaySettings.valid() ? _displaySettings.get() : osg::DisplaySettings::instance().get();
// 获取窗口系统API接口,即GraphicsContext::WindowingSystemInterface的实例
// static osg::ref_ptr<WindowingSystemInterfaces>& getWindowingSystemInterfaces()
osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
// pass on the display settings to the WindowSystemInterface.
if (wsi && wsi->getDisplaySettings()==0) wsi->setDisplaySettings(ds);
unsigned int maxTexturePoolSize = ds->getMaxTexturePoolSize();
unsigned int maxBufferObjectPoolSize = ds->getMaxBufferObjectPoolSize();
}
osg::DisplaySettings类使用单例模式来声明所有这些元素的窗口的唯一实例(一个程序中只能有一个DisplaySettings::instance()对象)。所以我们可以在程序中的任意获取显示设置实例:
DisplaySettings的作用仅仅是保存所有可能在系统显示中用到的数据,这个类本身并不会据此改变任何系统设置和渲染方式。
osg::DisplaySettings* ds = osg::DisplaySettings::instance();
- 主要记录窗口的一些显示设置,例如渲染窗口的OpenGL图像环境。
- DisplaySettings可以很方便地从系统环境变量或者命令行参数中获取用户对显示设备的设置,详细的调用方法可以参阅DisplaySettings::readEnvironmentalVariables和DisplaySettings::readCommandLine两个函数的内容。
- 如果希望在用户程序中更改DisplaySettings中的显示设置,请务必在执行视景器的realize函数之前,当然也就是仿真循环开始之前
- _displayType:显示器类型,默认为MONITOR(监视器),此外还支持POWERWALL(威力墙),REALITY_CENTER(虚拟实境中心)和HEAD_MOUNTED_DISPLAY(头盔显示器)。
- _stereoMode:立体显示模式,默认为ANAGLYPHIC(互补色),此外还支持QUAD_BUFFER(四方体缓冲),HORIZONTAL_SPLIT(水平分割),VERTICAL_SPLIT(垂直分割),LEFT_EYE(左眼用),RIGHT_EYE(右眼用),HORIZONTAL_INTERLACE(水平交错),VERTICAL_INTERLACE(垂直交错),CHECKERBOARD(棋盘式交错,用于DLP显示器)。
- _eyeSeparation:双眼的物理距离,默认为0.05。
- _screenWidth,_screenHeight:屏幕的实际宽度和高度,分别默认设置为0.325和0.26,目前它们影响的仅仅是视图采用透视投影时的宽高比。
- _screenDistance:人眼到屏幕的距离,默认为0.5。
- _splitStereoHorizontalEyeMapping:默认为LEFT_EYE_LEFT_VIEWPORT(左眼渲染左视口),也可设为LEFT_EYE_RIGHT_VIEWPORT(左眼渲染右视口)。
- _splitStereoHorizontalSeparation:左视口和右视口之间的距离(像素数),默认为0。
- _splitStereoVerticalEyeMapping:默认为LEFT_EYE_TOP_VIEWPORT(左眼渲染顶视口),也可设为LEFT_EYE_BOTTOM_VIEWPORT(左眼渲染底视口)。
- _splitStereoVerticalSeparation:顶视口和底视口之间的距离(像素数),默认为0。
- _splitStereoAutoAdjustAspectRatio:默认为true,用于屏幕分割之后对其宽高比进行补偿。
- _maxNumOfGraphicsContexts:用户程序中最多可用的GraphicsContext(图形设备上下文)数目,默认为32个。
- _numMultiSamples:多重采样的子像素样本数,默认为0。如果显示卡支持的话,打开多重采样可以大幅改善反走样(anti-aliasing)的效果。
- _minimumNumberStencilBits(模板缓存的最小位数)
- 更多具体使用参考这里的Chapter7
part3 遍历所得的所有GraphicsContext设备
void Viewer::realize()
{
。。。
for(Contexts::iterator citr = contexts.begin();
citr != contexts.end();
++citr)
{
osg::GraphicsContext* gc = *citr;
if (ds->getSyncSwapBuffers()) gc->setSwapCallback(new osg::SyncSwapBuffersCallback);
// set the pool sizes, 0 the default will result in no GL object pools.
gc->getState()->setMaxTexturePoolSize(maxTexturePoolSize);
gc->getState()->setMaxBufferObjectPoolSize(maxBufferObjectPoolSize);
gc->realize();
if (_realizeOperation.valid() && gc->valid())
{
gc->makeCurrent();
(*_realizeOperation)(gc);
gc->releaseContext();
}
}
}
_realizeOperation,通过ViewerBase::setRealizeOperation来设置的,其主要作用是在执行realize函数时,顺便完成用户指定的一些工作。您自己的工作内容可以通过继承osg::Operation类,并重载operator()操作符来添加。osgcatch这个妙趣横生的例子(一个傻娃娃接玩具的小游戏)中就使用了setRealizeOperation,主要的作用是为场景中的Drawable几何对象立即编译显示列表(Display List)。
part4 把鼠标焦点转到当前窗口上
当前位置:osgViewer/Viewer.cpp第463行,osgViewer::Viewer::realize()
下面我们再次遍历所有GraphicsContext设备,对于每个GraphicsContext指针gc,判断它是否为GraphicsWindow对象,并执行GraphicsWindow::grabFocusIfPointerInWindow函数。阅读GraphicsWindowWin32类(即GraphicsContext的具体实现者)的同名函数可以发现,这个函数不过是负责把鼠标焦点转到当前窗口上而已。
void Viewer::realize()
{
。。。
// attach contexts to _incrementalCompileOperation if attached.
if (_incrementalCompileOperation) _incrementalCompileOperation->assignContexts(contexts);
bool grabFocus = true;
if (grabFocus)
{
for(Contexts::iterator citr = contexts.begin();
citr != contexts.end();
++citr)
{
osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
if (gw)
{
gw->grabFocusIfPointerInWindow();
}
}
}
}
part5 启动OSG内部定时器并开始计时,设置线程,Compile Contexts
void Viewer::realize()
{
。。。
// initialize the global timer to be relative to the current time.
osg::Timer::instance()->setStartTick();
// pass on the start tick to all the associated event queues
setStartTick(osg::Timer::instance()->getStartTick());
// configure threading.
setUpThreading();
if (osg::DisplaySettings::instance()->getCompileContextsHint())
{
for(unsigned int i=0; i<= osg::GraphicsContext::getMaxContextID(); ++i)
{
osg::GraphicsContext* gc = osg::GraphicsContext::getOrCreateCompileContext(i);
if (gc)
{
gc->createGraphicsThread();
gc->getGraphicsThread()->startThread();
}
}
}
}
- Viewer::setStartTick函数的工作是找到当前视景器和所有GraphicsContext设备的事件队列_eventQueue,并设定它们的启动时刻为当前时间。
- ViewerBase::setUpThreading函数……设置线程:
- OSG的视景器包括四种线程模型,可以使用viewer.setThreadingModel进行设置,更多参考这里
- 启用编译上下文Compile Contexts,只需要在调用realize之前执行:
osg::DisplaySettings::instance()->setCompileContextsHint(true);
3.正式进入仿真循环当中:advance,eventTraversal,updateTraversal和renderingTraversals
4.osgViewer::Viewer::advance(),循环运行前的准备工作
http://bbs.osgchina.org/forum.php?mod=viewthread&tid=519&highlight=OSG%D4%AD%B4%B4%BD%CC%B3%CC%A3%BA%D7%EE%B3%A4%B5%C4%D2%BB%D6%A1
二、void View::setSceneData(osg::Node* node)中重要的操作
void View::setSceneData(osg::Node* node)
{
if (node==_scene->getSceneData()) return;
osg::ref_ptr<Scene> scene = Scene::getScene(node);
if (scene)
{
OSG_INFO<<"View::setSceneData() Sharing scene "<<scene.get()<<std::endl;
_scene = scene;
}
else
{
if (_scene->referenceCount()!=1)
{
// we are not the only reference to the Scene so we cannot reuse it.
_scene = new Scene;
OSG_INFO<<"View::setSceneData() Allocating new scene"<<_scene.get()<<std::endl;
}
else
{
OSG_INFO<<"View::setSceneData() Reusing existing scene"<<_scene.get()<<std::endl;
}
_scene->setSceneData(node);
}
if (getSceneData())
{
#if 0
#if defined(OSG_GLES2_AVAILABLE)
osgUtil::ShaderGenVisitor sgv;
getSceneData()->getOrCreateStateSet();
getSceneData()->accept(sgv);
#endif
#endif
// now make sure the scene graph is set up with the correct DataVariance to protect the dynamic elements of
// the scene graph from being run in parallel.
osgUtil::Optimizer::StaticObjectDetectionVisitor sodv;
getSceneData()->accept(sodv);
// make sure that existing scene graph objects are allocated with thread safe ref/unref
if (getViewerBase() &&
getViewerBase()->getThreadingModel()!=ViewerBase::SingleThreaded)
{
getSceneData()->setThreadSafeRefUnref(true);
}
// update the scene graph so that it has enough GL object buffer memory for the graphics contexts that will be using it.
// 用户程序中最多可用的GraphicsContext(图形设备上下文)数目,默认为32个
getSceneData()->resizeGLObjectBuffers(osg::DisplaySettings::instance()->getMaxNumberOfGraphicsContexts());
}
computeActiveCoordinateSystemNodePath();
assignSceneDataToCameras();
}
- Scene::getScene(node),获取当前视景器对应的osgViewer::Scene对象,也就是场景。一个场景包括了唯一的场景图形根节点,分页数据库(DatabasePager),以及分页图像库(ImagePager)。Viewer视景器对象通常只包括一个Scene场景,而CompositeViewer复合视景器则可能包括多个场景对象。
- View::assignSceneDataToCameras,这其中包括以下几项工作:
1、对于场景漫游器_cameraManipulator,执行其setNode函数和home函数,也就是设置漫游器对应于场景图形根节点,并回到其原点位置。不过在我们使用setCameraManipulator函数时也会自动执行同样的操作。
2、将场景图形赋予主摄像机_camera,同时设置它对应的渲染器(Renderer)的相关函数。
3、同样将场景图形赋予所有的从摄像机_slaves,并设置每个从摄像机的渲染器(Renderer)。
void View::assignSceneDataToCameras()
{
// OSG_NOTICE<<"View::assignSceneDataToCameras()"<<std::endl;
if (_scene.valid() && _scene->getDatabasePager() && getViewerBase())
{
_scene->getDatabasePager()->setIncrementalCompileOperation(getViewerBase()->getIncrementalCompileOperation());
}
osg::Node* sceneData = _scene.valid() ? _scene->getSceneData() : 0;
if (_cameraManipulator.valid())
{
_cameraManipulator->setNode(sceneData);
osg::ref_ptr<osgGA::GUIEventAdapter> dummyEvent = _eventQueue->createEvent();
_cameraManipulator->home(*dummyEvent, *this);
}
if (_camera.valid())
{
_camera->removeChildren(0,_camera->getNumChildren());
if (sceneData) _camera->addChild(sceneData);
Renderer* renderer = dynamic_cast<Renderer*>(_camera->getRenderer());
if (renderer) renderer->setCompileOnNextDraw(true);
}
for(unsigned i=0; i<getNumSlaves(); ++i)
{
Slave& slave = getSlave(i);
if (slave._camera.valid() && slave._useMastersSceneData)
{
slave._camera->removeChildren(0,slave._camera->getNumChildren());
if (sceneData) slave._camera->addChild(sceneData);
Renderer* renderer = dynamic_cast<Renderer*>(slave._camera->getRenderer());
if (renderer) renderer->setCompileOnNextDraw(true);
}
}
}
标签:GraphicsContext,一帧,待补,Viewer,camera,gc,osgViewer,最长,osg
From: https://blog.csdn.net/qq_42491346/article/details/141132458