开始:osgViewer/ViewerBase.cpp   389行,startThreading()函数,启动线程
 
void ViewerBase::startThreading()
{
if (_threadsRunning) return; OSG_INFO<<"Viewer::startThreading() - starting threading"<<std::endl; // release any context held by the main thread.
releaseContext(); Contexts contexts;
getContexts(contexts); OSG_INFO<<"Viewer::startThreading() - contexts.size()="<<contexts.size()<<std::endl; Cameras cameras;
getCameras(cameras); unsigned int numThreadsOnStartBarrier = ;
unsigned int numThreadsOnEndBarrier = ;
switch(_threadingModel)
{
case(SingleThreaded):
numThreadsOnStartBarrier = ;
numThreadsOnEndBarrier = ;
return;
case(CullDrawThreadPerContext):
numThreadsOnStartBarrier = contexts.size()+;
numThreadsOnEndBarrier = contexts.size()+;
break;
case(DrawThreadPerContext):
numThreadsOnStartBarrier = ;
numThreadsOnEndBarrier = ;
break;
case(CullThreadPerCameraDrawThreadPerContext):
numThreadsOnStartBarrier = cameras.size()+;
numThreadsOnEndBarrier = ;
break;
default:
OSG_NOTICE<<"Error: Threading model not selected"<<std::endl;
return;
} Scenes scenes;
getScenes(scenes);
for(Scenes::iterator scitr = scenes.begin();
scitr != scenes.end();
++scitr)
{
if ((*scitr)->getSceneData())
{
OSG_INFO<<"Making scene thread safe"<<std::endl; // make sure that existing scene graph objects are allocated with thread safe ref/unref
(*scitr)->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.
(*scitr)->getSceneData()->resizeGLObjectBuffers(osg::DisplaySettings::instance()->getMaxNumberOfGraphicsContexts());
}
} Contexts::iterator citr; unsigned int numViewerDoubleBufferedRenderingOperation = ; bool graphicsThreadsDoesCull = _threadingModel == CullDrawThreadPerContext || _threadingModel==SingleThreaded; for(Cameras::iterator camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer)
{
renderer->setGraphicsThreadDoesCull(graphicsThreadsDoesCull);
renderer->setDone(false);
renderer->reset();
++numViewerDoubleBufferedRenderingOperation;
}
} if (_threadingModel==CullDrawThreadPerContext)
{
_startRenderingBarrier = ;
_endRenderingDispatchBarrier = ;
_endDynamicDrawBlock = ;
}
else if (_threadingModel==DrawThreadPerContext ||
_threadingModel==CullThreadPerCameraDrawThreadPerContext)
{
_startRenderingBarrier = ;
_endRenderingDispatchBarrier = ;
_endDynamicDrawBlock = new osg::EndOfDynamicDrawBlock(numViewerDoubleBufferedRenderingOperation); #ifndef OSGUTIL_RENDERBACKEND_USE_REF_PTR
if (!osg::Referenced::getDeleteHandler()) osg::Referenced::setDeleteHandler(new osg::DeleteHandler());
else osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects();
#endif
} if (numThreadsOnStartBarrier>)
{
_startRenderingBarrier = new osg::BarrierOperation(numThreadsOnStartBarrier, osg::BarrierOperation::NO_OPERATION);
} if (numThreadsOnEndBarrier>)
{
_endRenderingDispatchBarrier = new osg::BarrierOperation(numThreadsOnEndBarrier, _endBarrierOperation);
} osg::ref_ptr<osg::BarrierOperation> swapReadyBarrier = contexts.empty() ? : new osg::BarrierOperation(contexts.size(), osg::BarrierOperation::NO_OPERATION); osg::ref_ptr<osg::SwapBuffersOperation> swapOp = new osg::SwapBuffersOperation(); for(citr = contexts.begin();
citr != contexts.end();
++citr)
{
osg::GraphicsContext* gc = (*citr); if (!gc->isRealized())
{
OSG_INFO<<"ViewerBase::startThreading() : Realizng window "<<gc<<std::endl;
gc->realize();
} gc->getState()->setDynamicObjectRenderingCompletedCallback(_endDynamicDrawBlock.get()); // create the a graphics thread for this context
gc->createGraphicsThread(); // add the startRenderingBarrier
if (_threadingModel==CullDrawThreadPerContext && _startRenderingBarrier.valid()) gc->getGraphicsThread()->add(_startRenderingBarrier.get()); // add the rendering operation itself.
gc->getGraphicsThread()->add(new osg::RunOperations()); if (_threadingModel==CullDrawThreadPerContext && _endBarrierPosition==BeforeSwapBuffers && _endRenderingDispatchBarrier.valid())
{
// add the endRenderingDispatchBarrier
gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get());
} if (swapReadyBarrier.valid()) gc->getGraphicsThread()->add(swapReadyBarrier.get()); // add the swap buffers
gc->getGraphicsThread()->add(swapOp.get()); if (_threadingModel==CullDrawThreadPerContext && _endBarrierPosition==AfterSwapBuffers && _endRenderingDispatchBarrier.valid())
{
// add the endRenderingDispatchBarrier
gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get());
} } if (_threadingModel==CullThreadPerCameraDrawThreadPerContext && numThreadsOnStartBarrier>)
{
Cameras::iterator camItr; for(camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
camera->createCameraThread(); osg::GraphicsContext* gc = camera->getGraphicsContext(); // add the startRenderingBarrier
if (_startRenderingBarrier.valid()) camera->getCameraThread()->add(_startRenderingBarrier.get()); Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer)
{
renderer->setGraphicsThreadDoesCull(false);
camera->getCameraThread()->add(renderer);
} if (_endRenderingDispatchBarrier.valid())
{
// add the endRenderingDispatchBarrier
gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get());
} } for(camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
if (camera->getCameraThread() && !camera->getCameraThread()->isRunning())
{
OSG_INFO<<" camera->getCameraThread()-> "<<camera->getCameraThread()<<std::endl;
camera->getCameraThread()->startThread();
}
}
} for(citr = contexts.begin();
citr != contexts.end();
++citr)
{
osg::GraphicsContext* gc = (*citr);
if (gc->getGraphicsThread() && !gc->getGraphicsThread()->isRunning())
{
OSG_INFO<<" gc->getGraphicsThread()->startThread() "<<gc->getGraphicsThread()<<std::endl;
gc->getGraphicsThread()->startThread();
// OpenThreads::Thread::YieldCurrentThread();
}
} _threadsRunning = true; OSG_INFO<<"Set up threading"<<std::endl;
}
 osgViewer/ViewerBase.cpp   685行,run()函数中判断执行frame()函数

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 ;
}

osgViewer/ViewerBase.cpp   727行,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();
}

解读:

frame 函数的内容本身几乎一眼就可以看完。不过要注意的是,这个函数是 ViewerBase类的成员函数,而非 Viewer 类。因此,无论对于单视景器的 Viewer 类,还是多视景器的
CompositeViewer 类,frame 函数的内容都是相同的(因为它们都没有再重写这个函数的内容)。
该函数所执行的主要工作如下:1、如果这是仿真系统启动后的第一帧,则执行 viewerInit();此时如果还没有执行 realize()函数,则执行它。(Viewer::viewerInit 函数只做了一件事,就是调用 View::init()函数,而这个 init 函数的工作似乎也是一目了然的:无非就是完成视景器的初始化工作而已。)
-----------------------------------------------------------------------------------------------------------------------------------------------------
①如果执行 viewerInit()
先跳转到osgViewer/Viewer的   virtual void viewerInit() { init(); }  ,该函数调用
osgViewer/View.cpp 第 257 行,osgViewer::View::init() 
void View::init()
{
OSG_INFO<<"View::init()"<<std::endl;
  //View::init 函数中出现了两个重要的类成员变量:_eventQueue 和_cameraManipulator,并且还将一个 osgGA::GUIEventAdapter 的实例传入_cameraManipulator的初始化函数。
    osg::ref_ptr<osgGA::GUIEventAdapter> initEvent = _eventQueue->createEvent();
initEvent->setEventType(osgGA::GUIEventAdapter::FRAME); if (_cameraManipulator.valid())
{
_cameraManipulator->init(*initEvent, *this);
}
}
 

  从变量的名称可以猜测出_eventQueue 的功能,它用于储存该视景器的事件队列。OSG中代表事件的类是 osgGA::GUIEventAdapter,它可以用于表达各种类型的鼠标、键盘、触压笔和窗口事件。在用户程序中,我们往往通过继承 osgGA::GUIEventHandler 类,并重写 handle函数的方法,获取实时的鼠标 / 键盘输入,并进而实现相应的用户代码。
  _eventQueue 除了保存一个 GUIEventAdapter 的链表之外,还提供了一系列对链表及其元素的操作函数,这其中,createEvent 函数的作用是分配和返回一个新的 GUIEventAdapter事件的指针。
  随后,这个新事件的类型被指定为 FRAME 事件,即每帧都会触发的一个事件。
  那么,_cameraManipulator 呢?没错,它就是视景器中所用的场景漫游器的实例。通常我们都会使用 setCameraManipulator 来设置这个变量的内容,例如轨迹球漫游器(TrackballManipulator)可以使用鼠标拖动来观察场景,而驾驶漫游器(DriveManipulator)则使用类似于汽车驾驶的效果来实现场景的漫游。
  上面的代码将新创建的 FRAME 事件和 Viewer 对象本身传递给_cameraManipulator 的init 函数,不同的漫游器(如 TrackballManipulator、DriveManipulator)会重写各自的 init 函数,实现自己所需的初始化工作。如果读者希望自己编写一个场景的漫游器,那么覆写并使用 osgGA::MatrixManipulator::init 就可以灵活地初始化自定义漫游器的功能了,它的调用时机就在这里。

-----------------------------------------------------------------------------------------------------------------------------------------------------

②如果执行realize()函数

osgViewer/Viewer.cpp 第 496 行,void Viewer::realize()

Viewer::realize 函数是我们十分熟悉的另一个函数,从 OSG 问世以来,我们就习惯于在进入仿真循环之前调用它(现在的 OSG 会自动调用这个函数,如果我们忘记的话),以完成窗口和场景的“设置”工作。那么,什么叫做“设置”,这句简单的场景设置又包含了多少内容呢?艰辛的旅程就此开始吧。

void Viewer::realize()
{
//OSG_INFO<<"Viewer::realize()"<<std::endl;
   //setCameraWithFocus(0),其内容无非是设置类变量_cameraWithFocus 指向的内容为 NULL。
   //这部分程序不在osgViewer/Viewer.cpp中,在osgViewer/CompositeViewer.cpp对应的realize()函数中
/*setCameraWithFocus(0); if (_views.empty())
{
OSG_NOTICE<<"CompositeViewer::realize() - No views to realize."<<std::endl;
_done = true;
return;
}
*/ /**
  * 变量 contexts 是一个保存了 osg::GraphicsContext 指针的向量组,而 Viewer::getContexts函数的作用是获取所有的图形上下文,并保存到这个向量组中来。
  * 对于需要将 OSG 嵌合到各式各样的 GUI 系统(如 MFC,Qt,wxWidgets 等)的朋友来说,osg::GraphicsContext 类是经常要打交道的对象之一。一种常用的嵌入方式也许是这样实现的:
  * osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
  * osg::ref_ptr<osg::Referenced> windata = new osgViewer::GraphicsWindowWin32::WindowData(hWnd);
  * traits->x = 0;
  * traits->y = 0;
  * ……
  * traits->inheritedWindowData = windata;
  * osg::GraphicsContext* gc = osg::GraphicsContext::createGraphicsContext(traits.get());
  * Camera* camera = viewer.getCamera();
  * camera->setGraphicsContext(gc);
  * ……
  * viewer.setCamera(camera);
  * 这个过程虽然比较繁杂,但是顺序还是十分清楚的:首先设置嵌入窗口的特性(Traits),例如 X、Y 位置,宽度和高度,以及父窗口的句柄(inheritedWindowData);然后根据特性的设置创建一个新的图形设备上下文(GraphicsContext),将其赋予场景所用的摄像机。而我们在 getContexts 函数中所要获取的,也许就包括这样一个用户建立的 GraphicsContext 设备。   */
Contexts contexts;
getContexts(contexts);
  /*
  有一个显而易见的事实是:当程序还没有进入仿真循环,且对于 osgViewer::Viewer 还没有任何的操作之时,
  系统是不会存在任何图形上下文的;创建一个新的 osg::Camera 对象也不会为其自动分配图形上下文。
  但是,图形上下文 GraphicsContext 却是场景显示的唯一平台,系统有必要在开始渲染之前完成其创建工作。
  
  假设用户已经在进入仿真循环之前,自行创建了新的 Camera 摄像机对象,为其分配了自定义的 GraphicsContext 设备,
  并将 Camera 对象传递给视景器,就像 osgviewerMFC 和osgcamera 例子,以及我们在编写与 GUI 系统嵌合的仿真程序时常做的那样。
  此时,系统已经不必为图形上下文的创建作任何多余的工作,因为用户不需要更多的窗口来显示自己的场景了。
  所以就算主摄像机_camera 还没有分配 GraphicsContext,只要系统中已经存在图形上下文,即可以开始执行仿真程序了。   但是,如果 getContexts 没有得到任何图形上下文的话,就说明仿真系统还没有合适的显示平台,
  此时就需要尝试创建一个缺省的 GraphicsContext 设备,并再次执行 getContexts,
  如果还是没能得到任何图形上下文的话,那么就不得不退出程序了。
  */ if (contexts.empty())
{
OSG_INFO<<"Viewer::realize() - No valid contexts found, setting up view across all screens."<<std::endl; // no windows are already set up so set up a default view
     // 创建缺省 GraphicsContext 设备的方法有以下几种:
std::string value;
if (osg::getEnvVar("OSG_CONFIG_FILE", value))
{
       /*
       1、读取 OSG_CONFIG_FILE 环境变量的内容:如果用户在这个环境变量中定义了一个
         文件路径的话,那么系统会尝试用 osgDB::readObjectFile 函数读入这个文件,使用 cfg 插件
         进行解析;如果成功的话,则调用 osgViewer::Viewer::take 函数,使用配置信息设置当前的
         视景器。这些工作在 osgViewer::Viewer::readConfiguration 函数中实现。
        */
readConfiguration(value);
}
else
{
int screenNum = -;
osg::getEnvVar("OSG_SCREEN", screenNum); int x = -, y = -, width = -, height = -;
osg::getEnvVar("OSG_WINDOW", x, y, width, height); if (osg::getEnvVar("OSG_BORDERLESS_WINDOW", x, y, width, height))
{
          /*
          2、读取 OSG_WINDOW 环境变量的内容:如果用户以“x y w h”的格式在其中定义了
            窗口的左上角坐标(x,y)和尺寸(w,h)的话(注意要以空格为分隔符),系统会尝试使
            用 osgViewer::View::setUpViewInWindow 函数来创建设备
          */
osg::ref_ptr<osgViewer::SingleWindow> sw = new osgViewer::SingleWindow(x, y, width, height, screenNum);
sw->setWindowDecoration(false);
apply(sw.get());
}
else if (width> && height>)
{
if (screenNum>=) setUpViewInWindow(x, y, width, height, screenNum);
else setUpViewInWindow(x,y,width,height);
}
else if (screenNum>=)
{
          /*
          3、读取 OSG_SCREEN 环境变量的内容:如果用户在其中定义了所用屏幕的数量的话,
           系统会尝试用 osgViewer::View::setUpViewOnSingleScreen 函数,为每一个显示屏创建一个全
           屏幕的图形窗口;如果同时还设置了 OSG_WINDOW,那么这两个环境变量都可以起到作
           用,此时将调用 setUpViewInWindow 函数。
          */
setUpViewOnSingleScreen(screenNum);
}
else
{
          /*
          4、如果上述环境变量都没有设置的话(事实上这也是最常见的情况),那么系统将调用
          osgViewer::View::setUpViewAcrossAllScreens 函数,尝试创建一个全屏显示的图形设备。
          */
setUpViewAcrossAllScreens();
}
} getContexts(contexts);
} if (contexts.empty())
{
OSG_NOTICE<<"Viewer::realize() - failed to set up any windows"<<std::endl;
_done = true;
return;
} // get the display settings that will be active for this viewer
osg::DisplaySettings* ds = _displaySettings.valid() ? _displaySettings.get() : osg::DisplaySettings::instance().get();
osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface(); // pass on the display settings to the WindowSystemInterface.
if (wsi && wsi->getDisplaySettings()==) wsi->setDisplaySettings(ds); unsigned int maxTexturePoolSize = ds->getMaxTexturePoolSize();
unsigned int maxBufferObjectPoolSize = ds->getMaxBufferObjectPoolSize(); 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();
}
} // 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();
}
}
} // 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=; i<= osg::GraphicsContext::getMaxContextID(); ++i)
{
osg::GraphicsContext* gc = osg::GraphicsContext::getOrCreateCompileContext(i); if (gc)
{
gc->createGraphicsThread();
gc->getGraphicsThread()->startThread();
}
}
}
#if 0
osgGA::GUIEventAdapter* eventState = getEventQueue()->getCurrentEventState();
if (getCamera()->getViewport())
{
osg::Viewport* viewport = getCamera()->getViewport();
eventState->setInputRange( viewport->x(), viewport->y(), viewport->x() + viewport->width(), viewport->y() + viewport->height());
}
else
{
eventState->setInputRange(-1.0, -1.0, 1.0, 1.0);
}
#endif
}

Viewer::getContexts函数的作用是获取所有的图形上下文,并保存到这个向量组中来。

当前位置:osgViewer/Viewer.cpp 第 1307 行,osgViewer::Viewer::getContexts()

/*
如果希望观察自己的程序中所有的图形设备上下文,
不妨使用这个函数来收集一下。简单的情形下,我们的程序中只有一个主摄像机,
也就只有一个 GraphicsContext 设备,它表达了一个全屏幕的图形窗口;
而 osgcamera 这个例子程序可以创建六个从摄像机,因此可以得到六个图形上下文设备,且各个图形窗口的 X 坐标各不相同,这也正是这个例子想要表达的。
*/
void Viewer::getContexts(Contexts& contexts, bool onlyValid)
{
typedef std::set<osg::GraphicsContext*> ContextSet;
ContextSet contextSet; contexts.clear(); /*首先判断场景的主摄像机_camera 是否包含了一个有效的GraphicsContext 设备,
然后再遍历所有的从摄像机_slaves(一个视景器可以包含一个主摄像机和多个从摄像机),
将所有找到的 GraphicsContext 图形上下文设备记录下来。 随后,将这些 GraphicsContext 的指针追加到传入参数(contexts 向量组)中,
并使用std::sort 执行了一步排序的工作(没看出来,因为context本身是一个 std::vector,没有插入数据自动实现排序的功能),所谓的排序是按照这样的原则来进行的:
1、屏幕数量较少的 GraphicsContext 设备排列靠前;
2、窗口 X 坐标较小的设备排列靠前;
3、窗口 Y 坐标较小的设备排列靠前。
*/
if (_camera.valid() &&
_camera->getGraphicsContext() &&
(_camera->getGraphicsContext()->valid() || !onlyValid))
{
contextSet.insert(_camera->getGraphicsContext());
contexts.push_back(_camera->getGraphicsContext());
} for(unsigned int i=; i<getNumSlaves(); ++i)
{
Slave& slave = getSlave(i);
osg::GraphicsContext* sgc = slave._camera.valid() ? slave._camera->getGraphicsContext() : ;
if (sgc && (sgc->valid() || !onlyValid))
{
if (contextSet.count(sgc)==)
{
contextSet.insert(sgc);
contexts.push_back(sgc);
}
}
}
}
2、执行 advance 函数。
...
3、执行 eventTraversal 函数,顾名思义,这个函数将负责处理系统产生的各种事件,诸如鼠标的移动,点击,键盘的响应,窗口的关闭等等,以及摄像机与场景图形的事件回调
(EventCallback)。
...
4、执行 updateTraversal 函数,这个函数负责遍历所有的更新回调(UpdateCallback);除此之外,它的另一个重要任务就是负责更新 DatabasePager 与 ImagePager 这两个重要的分页数据处理组件。
...
5、执行 renderingTraversals 函数,这里将使用较为复杂的线程处理方法,完成场景的筛选(cull)和绘制(draw)工作。
...
 
文字参考:王锐老师《最长的一帧》
代码参考:OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield    (osg3.4)

《最长的一帧》 osg3.4 osgViewer::View::init() osgViewer::Viewer::getContexts()的更多相关文章

  1. Cesium原理篇:5最长的一帧之影像

    如果把地球比做一个人,地形就相当于这个人的骨骼,而影像就相当于这个人的外表了.之前的几个系列,我们全面的介绍了Cesium的地形内容,详见: Cesium原理篇:1最长的一帧之渲染调度 Cesium原 ...

  2. Cesium原理篇:3最长的一帧之地形(2:高度图)

           这一篇,接着上一篇,内容集中在高度图方式构建地球网格的细节方面.        此时,Globe对每一个切片(GlobeSurfaceTile)创建对应的TileTerrain类,用来维 ...

  3. Cesium原理篇:7最长的一帧之Entity(上)

    之前的最长的一帧系列,我们主要集中在地形和影像服务方面.简单说,之前我们都集中在地球是怎么造出来的,从这一系列开始,我们的目光从GLOBE上解放出来,看看球面上的地物是如何渲染的.本篇也是先开一个头, ...

  4. osgViewer::View::setUpViewOnSingleScreen()

    void ViewerBase::frame(double simulationTime) { if (_done) return; // OSG_NOTICE<<std::endl< ...

  5. osg osgViewer::View::setUpViewInWindow()

    void ViewerBase::frame(double simulationTime) { if (_done) return; // OSG_NOTICE<<std::endl< ...

  6. Cesium原理篇:1最长的一帧之渲染调度

    原计划开始着手地形系列,但发现如果想要从逻辑上彻底了解地形相关的细节,那还是需要了解Cesium的数据调度过程,这样才能更好的理解,因此,打算先整体介绍一下Cesium的渲染过程,然后在过渡到其中的两 ...

  7. Cesium原理篇:7最长的一帧之Entity(下)

    上一篇,我们介绍了当我们添加一个Entity时,通过Graphics封装其对应参数,通过EntityCollection.Add方法,将EntityCollection的Entity传递到DataSo ...

  8. Cesium原理篇:3最长的一帧之地形(1)

    前面我们从宏观上分析了Cesium的整体调度以及网格方面的内容,通过前两篇,读者应该可以比较清楚的明白一个Tile是怎么来的吧(如果还不明白全是我的错).接下来,在前两篇的基础上,我们着重讨论一下地形 ...

  9. Cesium原理篇:3最长的一帧之地形(3:STK)

    有了之前高度图的基础,再介绍STK的地形相对轻松一些.STK的地形是TIN三角网的,基于特征值,坦白说,相比STK而言,高度图属于淘汰技术,但高度图对数据的要求相对简单,而且支持实时构建网格,STK具 ...

随机推荐

  1. Java反射中Class.forName和classloader的区别

    Java中Class.forName和classloader都可以用来对类进行加载. Class.forName除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块 ...

  2. 进程、requests模块的简单使用

    一.进程 1.进程间数据不共享 import multiprocessing data_list = [] def task(arg): data_list.append(arg) print(dat ...

  3. RollingRegression(滚动回归分析)之Python实现

    # -*- coding: utf-8 -*-"""Created on Sat Aug 18 11:08:38 2018 @author: acadsoc"& ...

  4. 阿里云上遇到: virtual memory exhausted: Cannot allocate memory

    # dd if=/dev/zero of=/swap bs=1024 count=1M Format the swap file: # mkswap /swap Enable the swap fil ...

  5. Git---报错:git Please move or remove them before you can merge 解决方案

    场景: 当前在本地仓库lucky,因修改了123.txt的文件内容,需要将lucky分支push到远程Git库,在push前有其他的同事已删除了远程Git库中的123.txt文件.因此这时就产生了远程 ...

  6. Laravel Repository Pattern

    Laravel Repository Pattern   The Repository Pattern can be very helpful to you in order to keep your ...

  7. P3939 数颜色 线段树动态开点

    P3939 数颜色 线段树动态开点 luogu P3939 水.直接对每种颜色开个权值线段树即可,注意动态开点. #include <cstdio> #include <algori ...

  8. getchar 和EOF

    本文章基于:http://www.cnblogs.com/QLinux/articles/2465329.html,稍作了修改. 大师级经典的著作,要字斟句酌的去读,去理解.以前在看K&R的T ...

  9. jQuery多选和单选下拉框插件select.js

    一.插件描述 可通过参数设置多选或者单选,多选返回数组结果,单选返回字符串,如图: 下载地址:https://pan.baidu.com/s/1JjVoK89_ueVVpfSlMDJwUQ   提取码 ...

  10. Best free and public DNS servers of 2019

    1. OpenDNSPrimary, secondary DNS servers: 208.67.222.222 and 208.67.220.220 2. CloudflarePrimary, se ...