首页 > 其他分享 >最长的一帧学习(待补)

最长的一帧学习(待补)

时间:2024-08-12 18:52:57浏览次数:5  
标签:GraphicsContext 一帧 待补 Viewer camera gc osgViewer 最长 osg

文章目录

  • 场景图形-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

相关文章

  • Leetcode热题100-128.最长连续序列
    Leetcode热题100-128.最长连续序列1.题目描述2.解题思路3.代码实现1.题目描述128.最长连续序列2.解题思路使用哈希集合的思想:初始化一个unordered_set并将nums中所有元素放入集合中;遍历数组,依次判断当前元素是否为连续序列的开始,若是则求出当前连续序列......
  • 最大流学习笔记(待补充)
    刚学了最大流的EK算法和Dinic算法,在此做一点总结。由于这次专题学习是偏向图论建模的,因此目前暂且不涉及算法本身。Dinic板子:namespaceNet{ intS,T; inthead[510],work[510],etot=1; structnode{intnxt,v,cap;}edge[160010]; inlinevoidadd(intx,inty,in......
  • leetcode 718. 最长重复子数组,leetcode 1143. 最长公共子序列
    leetcode718和leetcode1143两道十分相似的题,就不放题目了思路实际上区别就在于一个要求连续数组,另一个要求不连续的序列。二者的dp表达式和状态转移其实是不一致的,前者f[i][j]代表nums1以i结尾nums2以j结尾的最长子数组长度,后者代表nums1以i结尾nums2以j结尾的区间内存......
  • 最长上升子序列
    普通#include<iostream>usingnamespacestd;#include<algorithm>#include<cstring>constintN=5010;intn,f[N];inta[N];intmain(){ cin>>n; for(inti=1;i<=n;i++)cin>>a[i]; for(inti=1;i<=n;i++......
  • 一帧CAN报文的收发流程
    一帧CAN报文的收发流程概述介绍总线通讯的文章有很多了,各种平台,CSDN\知乎、电子发烧友等等。扪心自问,你真的懂了吗?还是和大多数人一样,浅尝辄止,仅仅了解了一些分层的概念,就停滞不前了?附赠自动驾驶最全的学习资料和量产经验:链接认识事物的逻辑可不是这样的。autosar实......
  • 代码随想录 day 47 回文子串 | 最长回文子序列
    回文子串回文子串解题思路dp数组的状态是判断以i结尾,j开始的字符串是否为回文,用bool类型存储,之后当i和j的字符串相等时,通过计算它们之间的距离和判断它们之间是否为回文串来进行递归。知识点回文,动态规划心得如果不看题解根本想不到怎么做最长回文子序列最长回文子序列......
  • L2-008 最长对称子串 C++
    对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定IsPAT&TAPsymmetric?,最长对称子串为sPAT&TAPs,于是你应该输出11。输入格式:输入在一行中给出长度不超过1000的非空字符串。输出格式:在一行中输出最长对称子串的长度。输入样例:IsPAT&TAPsymmetric?输出样......
  • 根号分治(待补充)
    最近有根号分治的题都没那么熟悉,想到了方向感也很差,故写一点题解。LCA查询看到题目中的条件\(\sumk\le10^5\)说明\(k\leS\)的个数很多,\(S\lek\)的个数很少。那么对于前者,考虑一开始最朴素的\(O(S^2)\)的暴力,枚举集合中的两个点,求\(LCA\)总时间复杂度\(O(\fra......
  • 最长上升子序列(LIS)
    最长上升子序列(LIS)前情提要子串:连续的子序列:非连续,但相对序的抛出示例15239524等8个数a[8]前1个数构成的LIS:最长是1。子序列为:1前2个数构成的LIS:最长是2。子序列为:15前3个数构成的LIS:最长是2。子序列为:15或12(但我们只考虑12)前4个数构成的LI......
  • 最长最短单词【原创】
    最长最短单词描述输入1行句子(不多于200个单词,每个单词长度不超过100),只包含字母、空格和逗号。单词由至少一个连续的字母构成,空格和逗号都是单词间的间隔。试输出第1个最长的单词和第1个最短单词。输入一行句子。输出......