【转】OSG动态调度DatabasePager,pagedLOD

使用动态调度的原因
当用户需要浏览的数据量很大,比如地形模拟、虚拟小区和城市等的时候,会对计算机系统产生极大的负担。
在内存中可能要存储海量数据,这些海量数据指的是数百GB甚至TB级别的数据(例如中国境内的山形地貌等),这些不可能全部载入内存中,就算未来的计算机能够将它们一次性读入,也已经损耗了太多的系统性能。
 
动态调度的原理
在显示当前视域中的场景元素(可见元素)的同时,预判断下一步可能载入的数据(预可见元素),以及那些短时间内不可能被看到的对象(不可见元素),从而作出正确的数据加载和卸载处理,确保内存中始终维持有限的数据额度,并且不会因此造成场景浏览时重要信息的丢失或者过于迟缓。
数据的动态调度可以使用多线程的工作方式,使数据的动态调度和场景的实时绘制同时进行。由于动态数据的加载/卸载可能影响到场景树的结构,因此这一工作需要在场景更新的阶段完成,以免影响到裁剪和绘制的过程。
 
动态调度的过程
(1) 删除过期的场景数据:过期数据指的是那些长时间没有处于用户视域内,并且有理由认为它们不会立即显现的场景元素。场景的更新遍历函数负责将检索到的过期对象收集并送入相应的过期对象列表;而列表中的数据通常可以在数据线程中统一予以删除。
(2) 获取新的数据加载请求:请求加载的可能是新的数据信息,也可能是已有的场景数据(曾经从“当前页面”中去除,更新又回到“当前页面”中);数据可能是本地的文件,也可能来自网络;从网络下载的数据往往还需要缓存在本地磁盘上。这些都需要在数据线程中一一加以判断。
(3) 编译加载的数据:有些数据如果提前进行编译可以有效地提升效率,例如为几何体数据创建显示列表,以及将纹理对象提前加载到纹理内存。虽然OSG同样可以在负责渲染的主进程中根据用户需要执行这些工作,但是那样有可能造成帧的延迟。例如,一个大型场景的显示和调度过程中,如果大量的地块数据同时被加载入内存,那么下一帧的数据编译任务将变得十分繁重,继而造成较为严重的帧延迟。此时如果由数据线程负责预编译的工作,则可以在一定程度上缓解这一压力。
(4) 将加载的数据合并至场景图形:直接由数据线程来完成这一工作显然是不合适的,因为系统主进程不知道当DatabaseThread线程视图操作场景中的结点时,OSG的渲染器在做些什么。最好的方法是将读入的数据先保存在一个列表中,并且由仿真循环负责获取和执行合并新节点的操作。
 
DatabasePager与PagedLOD
在OSG中,osgDB::DatabasePager类负责执行场景动态调度的工作。与osgDB::DatabasePager搭配使用的是PagedLOD和osg::ProxyNode。这里主要讨论PagedLOD。
PagedLOD既具有将大量数据或者模型使用细节层次(LOD)原则划分的特性,又有动态调度以保证渲染效率和内存管理的特性。
 
代码示例
 
#include <osg/ShapeDrawable>
#include <osg/Geode>
#include <osg/PagedLOD>
#include <osgViewer/Viewer>
osg::Geode* createBox(const osg::Vec3& center, float width)
{
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable(
        new osg::ShapeDrawable(new osg::Box(center, width)));
    return geode.release();
}
osg::Group* createPagedLOD(int row, int col)
{
    osg::ref_ptr<osg::Group> root = new osg::Group;
    char buffer[5] = "";
    for (int i = 0; i<row; i++)
    {
        for (int j = 0; j<col; j++)
        {
            std::string filename = "cow.osg.";
#ifdef _WIN32
            _itoa_s(i * 10, buffer, 5, 10);
            filename += buffer; filename += ",";
            _itoa_s(j * 10, buffer, 5, 10);
            filename += buffer; filename += ",0.trans";
#else
            gcvt(i * 10, 5, buffer);
            filename += buffer; filename += ",";
            gcvt(j * 10, 5, buffer);
            filename += buffer; filename += ",0.trans";
#endif
            osg::ref_ptr<osg::PagedLOD> lod = new osg::PagedLOD;
            lod->setCenter(osg::Vec3(i * 10, j * 10, 0.0));
            lod->addChild(createBox(osg::Vec3(i * 10, j * 10, 0.0), 1), 200.0, FLT_MAX);
            lod->setFileName(1, filename);
            lod->setRange(1, 0.0, 200.0);
            root->addChild(lod.get());
        }
    }
    return root.release();
}
int main(int argc, char** argv)
{
    osgViewer::Viewer viewer;
    viewer.setSceneData(createPagedLOD(30, 30));
    return viewer.run();
}

 

运行结果图:
远处看

近处看

 

posted @ 2018-11-29 22:02  天山寒龟  阅读(1328)  评论(0)    收藏  举报