最长的一帧学习 part3

文章目录

  • 八、osgUtil:: SceneView::cull ()
    • part1 初始化必要的SceneView类成员变量
    • part2 立体显示的处理
    • part3 执行SceneView::cullStage函数,它也是场景视图筛选工作的核心函数
      • part3.1 首先统计场景中的遮挡节点(OccluderNode),并使用CollectOccludersVisitor访问器遍历场景中的所有节点
      • part3.2 将筛选所需的数据送入筛选访问器(CullVisitor)
      • ✳part3.3 首先将“全局状态节点”和“局部状态节点”追加到状态树中,CullVisitor::pushStateSet和popStateSet中构建状态树和渲染树
      • ✳part3.4 使用筛选访问器CullVisitor遍历场景中的节点,在遍历过程中带条件地裁减掉,从而提高场景绘制的效率,并构建状态树和渲染树
        • osgUtil::CullVisitor::apply(Transform&)
        • osgUtil::CullVisitor::apply(Geode&)
        • osgUtil::CullVisitor::apply(osg::Drawable& drawable)
          • inline void CullVisitor::addDrawableAndDepth(osg::Drawable* drawable,osg::RefMatrix* matrix,float depth)
        • osgUtil::CullVisitor::apply(Camera&)
      • part3.5 依次执行RenderStage::sort和StateGraph::prune函数,对cullVisitor之后得到的渲染树内容进行排序和精简
      • part3.6 计算出场景中动态对象(DYNAMIC)的数目并保存到SceneView的成员变量_dynamicObjectCount中
    • part4 执行CullVisitor::clampProjectionMatrix,重新设定场景视图的投影矩阵

原文

八、osgUtil:: SceneView::cull ()

part1 初始化必要的SceneView类成员变量

  • 包括该视图的渲染信息(_renderInfo),筛选访问器(_cullVisitor),状态树根节点(_stateGraph),渲染树根节点(_renderStage),view和State等,还有局部渲染状态_localStateSet的更新(SceneView::updateUniforms),更新的主要内容是一些内设的osg::Uniform着色器变量(osg_FrameNumber,osg_FrameTime,osg_DeltaFrameTime,osg_SimulationTime,osg_ViewMatrix,osg_ViewMatrixInverse)。我们可以在编写GLSL程序时调用这些变量获取OSG提供的一些场景和时间方面的信息。
void SceneView::cull()
{_dynamicObjectCount = 0;if (_camera->getNodeMask()==0) return;_renderInfo.setView(_camera->getView());// update the active uniformsupdateUniforms();if (!_renderInfo.getState()){OSG_INFO << "Warning: no valid osgUtil::SceneView::_state attached, creating a default state automatically."<< std::endl;// note the constructor for osg::State will set ContextID to 0 which will be fine to single context graphics// applications which is ok for most apps, but not multiple context/pipe applications._renderInfo.setState(new osg::State);}if (!_localStateSet){_localStateSet = new osg::StateSet;}if (!_cullVisitor){OSG_INFO << "Warning: no valid osgUtil::SceneView:: attached, creating a default CullVisitor automatically."<< std::endl;_cullVisitor = CullVisitor::create();}if (!_stateGraph){OSG_INFO << "Warning: no valid osgUtil::SceneView:: attached, creating a global default StateGraph automatically."<< std::endl;_stateGraph = new StateGraph;}if (!_renderStage){OSG_INFO << "Warning: no valid osgUtil::SceneView::_renderStage attached, creating a default RenderStage automatically."<< std::endl;_renderStage = new RenderStage;}。。。
}

part2 立体显示的处理

如果之前我们设置了立体显示的选项(参见第三日的相关内容),那么此时OSG会针对左/右眼(osg:: DisplaySettings:: LEFT_EYE/RIGHT_EYE)以其它各种设置做出适当的处理,相关的函数包括SceneView类成员computeLeftEyeProjection,computeLeftEyeView,computeRightEyeProjection,computeRightEyeView等,这里不再做深入的研究。

part3 执行SceneView::cullStage函数,它也是场景视图筛选工作的核心函数

void SceneView::cull()
{
。。。else{_cullVisitor->setTraversalMask(_cullMask);bool computeNearFar = cullStage(getProjectionMatrix(),getViewMatrix(),_cullVisitor.get(),_stateGraph.get(),_renderStage.get(),getViewport());。。。}
。。。 
}

part3.1 首先统计场景中的遮挡节点(OccluderNode),并使用CollectOccludersVisitor访问器遍历场景中的所有节点

bool SceneView::cullStage(const osg::Matrixd& projection,const osg::Matrixd& modelview,osgUtil::CullVisitor* cullVisitor, osgUtil::StateGraph* rendergraph, osgUtil::RenderStage* renderStage, osg::Viewport *viewport)
{if (!_camera || !viewport) return false;osg::ref_ptr<RefMatrix> proj = new osg::RefMatrix(projection);osg::ref_ptr<RefMatrix> mv = new osg::RefMatrix(modelview);// collect any occluder in the view frustum.//Node::containsOccluderNodesif (_camera->containsOccluderNodes()){//std::cout << "Scene graph contains occluder nodes, searching for them"<<std::endl;if (!_collectOccludersVisitor) _collectOccludersVisitor = new osg::CollectOccludersVisitor;_collectOccludersVisitor->inheritCullSettings(*this);_collectOccludersVisitor->reset();_collectOccludersVisitor->setFrameStamp(_frameStamp.get());// use the frame number for the traversal number.if (_frameStamp.valid()){_collectOccludersVisitor->setTraversalNumber(_frameStamp->getFrameNumber());}_collectOccludersVisitor->pushViewport(viewport);_collectOccludersVisitor->pushProjectionMatrix(proj.get());_collectOccludersVisitor->pushModelViewMatrix(mv.get(),osg::Transform::ABSOLUTE_RF);// traverse the scene graph to search for occluder in there new positions// 其实就是accept_collectOccludersVisitor->traverse(*_camera);_collectOccludersVisitor->popModelViewMatrix();_collectOccludersVisitor->popProjectionMatrix();_collectOccludersVisitor->popViewport();// sort the occluder from largest occluder volume to smallest._collectOccludersVisitor->removeOccludedOccluders();OSG_DEBUG << "finished searching for occluder - found "<<_collectOccludersVisitor->getCollectedOccluderSet().size()<<std::endl;cullVisitor->getOccluderList().clear();std::copy(_collectOccludersVisitor->getCollectedOccluderSet().begin(),_collectOccludersVisitor->getCollectedOccluderSet().end(), std::back_insert_iterator<CullStack::OccluderList>(cullVisitor->getOccluderList()));}。。。
}

part3.2 将筛选所需的数据送入筛选访问器(CullVisitor)

包括筛选设置(CullSettings),状态树根节点(StateGraph),渲染树根节点(RenderStage),渲染信息(RenderInfo)。注意此时状态树和渲染树还没有生成,我们还要设置渲染树构建所需的各种信息.

bool SceneView::cullStage(const osg::Matrixd& projection,const osg::Matrixd& modelview,osgUtil::CullVisitor* cullVisitor, osgUtil::StateGraph* rendergraph, osgUtil::RenderStage* renderStage, osg::Viewport *viewport)
{
。。。cullVisitor->reset();cullVisitor->setFrameStamp(_frameStamp.get());// use the frame number for the traversal number.if (_frameStamp.valid()){cullVisitor->setTraversalNumber(_frameStamp->getFrameNumber());}cullVisitor->inheritCullSettings(*this);cullVisitor->setStateGraph(rendergraph);cullVisitor->setRenderStage(renderStage);cullVisitor->setRenderInfo( _renderInfo );renderStage->reset();// comment out reset of rendergraph since clean is more efficient.//  rendergraph->reset();// use clean of the rendergraph rather than reset, as it is able to// reuse the structure on the rendergraph in the next frame. This// achieves a certain amount of frame cohereancy of memory allocation.// 是StageGragh类rendergraph->clean();renderStage->setInitialViewMatrix(mv.get());renderStage->setViewport(viewport);renderStage->setClearColor(_camera->getClearColor());renderStage->setClearDepth(_camera->getClearDepth());renderStage->setClearAccum(_camera->getClearAccum());renderStage->setClearStencil(_camera->getClearStencil());renderStage->setClearMask(_camera->getClearMask());#if 1renderStage->setCamera(_camera.get());
#endif#if defined(OSG_GL_FIXED_FUNCTION_AVAILABLE)switch(_lightingMode){case(HEADLIGHT):if (_light.valid()) renderStage->addPositionedAttribute(NULL,_light.get());else OSG_WARN<<"Warning: no osg::Light attached to osgUtil::SceneView to provide head light.*/"<<std::endl;break;case(SKY_LIGHT):if (_light.valid()) renderStage->addPositionedAttribute(mv.get(),_light.get());else OSG_WARN<<"Warning: no osg::Light attached to osgUtil::SceneView to provide sky light.*/"<<std::endl;break;default:break;}#endif
。。。
}

✳part3.3 首先将“全局状态节点”和“局部状态节点”追加到状态树中,CullVisitor::pushStateSet和popStateSet中构建状态树和渲染树

bool SceneView::cullStage(const osg::Matrixd& projection,const osg::Matrixd& modelview,osgUtil::CullVisitor* cullVisitor, osgUtil::StateGraph* rendergraph, osgUtil::RenderStage* renderStage, osg::Viewport *viewport)
{
。。。if (_globalStateSet.valid()) cullVisitor->pushStateSet(_globalStateSet.get());if (_secondaryStateSet.valid()) cullVisitor->pushStateSet(_secondaryStateSet.get());if (_localStateSet.valid()) cullVisitor->pushStateSet(_localStateSet.get());cullVisitor->pushViewport(viewport);cullVisitor->pushProjectionMatrix(proj.get());cullVisitor->pushModelViewMatrix(mv.get(),osg::Transform::ABSOLUTE_RF);// traverse the scene graph to generate the rendergraph.// If the camera has a cullCallback execute the callback which has the// requirement that it must traverse the camera's children.{osg::Callback* callback = _camera->getCullCallback();if (callback) callback->run(_camera.get(), cullVisitor);// 相当于acceptelse cullVisitor->traverse(*_camera);}cullVisitor->popModelViewMatrix();cullVisitor->popProjectionMatrix();cullVisitor->popViewport();if (_localStateSet.valid()) cullVisitor->popStateSet();if (_secondaryStateSet.valid()) cullVisitor->popStateSet();if (_globalStateSet.valid()) cullVisitor->popStateSet();
。。。
}

创建新的渲染树节点(渲染元)有三个条件:

  • 一是渲染状态没有采用覆盖渲染细节(OVERRIDE_RENDERBIN_DETAILS)的方式(由setRenderBinMode函数设置),
  • 二是使用setRenderBinDetails设置了渲染细节,
  • 三是渲染细节的字符串名称不为空(事实上也不能随意写,只能为“RenderBin”或“DepthSortedBin”)。
    如果不满足这些条件的话,渲染树的当前位置(CullVisitor::_currentRenderBin)就不会发生变化

此外,渲染树的构建过程中只生成空的渲染元(RenderBin)节点,向其中纳入状态节点和渲染叶的任务将在后面的工作中完成

 /** Push state set on the current state group.* If the state exists in a child state group of the current* state group then move the current state group to that child.* Otherwise, create a new state group for the state set, add* it to the current state group then move the current state* group pointer to the new state group.*/inline void CullVisitor::pushStateSet(const osg::StateSet* ss){// 状态树的构建// 判断传入的渲染状态ss是否已经存在于某个状态节点中,并将状态树的当前位置(CullVisitor::_currentStateGraph)转到那个节点// 或者新建一个包含了ss的状态节点(StateGraph::find_or_insert的工作)_currentStateGraph = _currentStateGraph->find_or_insert(ss);// 判断是否需要创建新的渲染元/跳转到某渲染元bool useRenderBinDetails = (ss->useRenderBinDetails() && !ss->getBinName().empty()) &&(_numberOfEncloseOverrideRenderBinDetails==0 || (ss->getRenderBinMode()&osg::StateSet::PROTECTED_RENDERBIN_DETAILS)!=0);if (useRenderBinDetails){//使用堆栈记录上一次在渲染树中的位置_renderBinStack.push_back(_currentRenderBin);//创建新的渲染元/跳转到某渲染元_currentRenderBin = ss->getNestRenderBins() ?_currentRenderBin->find_or_insert(ss->getBinNumber(),ss->getBinName()) :_currentRenderBin->getStage()->find_or_insert(ss->getBinNumber(),ss->getBinName());}if ((ss->getRenderBinMode()&osg::StateSet::OVERRIDE_RENDERBIN_DETAILS)!=0){++_numberOfEncloseOverrideRenderBinDetails;}}

popStateSet的任务正好与pushStateSet相反。

/** Pop the top state set and hence associated state group.* Move the current state group to the parent of the popped* state group.*/inline void popStateSet(){const osg::StateSet* ss = _currentStateGraph->getStateSet();if ((ss->getRenderBinMode()&osg::StateSet::OVERRIDE_RENDERBIN_DETAILS)!=0){--_numberOfEncloseOverrideRenderBinDetails;}bool useRenderBinDetails = (ss->useRenderBinDetails() && !ss->getBinName().empty()) &&(_numberOfEncloseOverrideRenderBinDetails==0 || (ss->getRenderBinMode()&osg::StateSet::PROTECTED_RENDERBIN_DETAILS)!=0);if (useRenderBinDetails){if (_renderBinStack.empty()){_currentRenderBin = _currentRenderBin->getStage();}else{// 从堆栈中取出上一次渲染树中所处的渲染元节点,并跳转到这一位置_currentRenderBin = _renderBinStack.back();_renderBinStack.pop_back();}}// 状态树从当前位置跳转到其父节点_currentStateGraph = _currentStateGraph->_parent;}

如果我们在遍历场景节点树时,使用pushStateSet将某个节点的渲染状态置入,然后再将它的子节点的渲染状态置入,如此反复……结束这个子树的遍历时,则依次使用popStateSet弹出_currentRenderBin和_currentStateGraph,直到返回初始位置为止。如此即可在遍历节点子树的过程中构建起渲染后台的状态树和渲染树;并且,假如在筛选(CULL)过程中我们判断某个节点(及其子树)应当被剔除掉时,只要跳过pushStateSet和popStateSet的步骤,直接返回,就不会在渲染时留下节点的任何蛛丝马迹。
没错,这就是CullVisitor的工作了:

✳part3.4 使用筛选访问器CullVisitor遍历场景中的节点,在遍历过程中带条件地裁减掉,从而提高场景绘制的效率,并构建状态树和渲染树

cullVisitor->traverse(*_camera);类似于:
在这里插入图片描述
访问器的工作原理:
在这里插入图片描述
重要函数:

inline void handle_cull_callbacks_and_traverse(osg::Node& node){osg::Callback* callback = node.getCullCallback();if (callback) callback->run(&node,this);else traverse(node);}
osgUtil::CullVisitor::apply(Transform&)
void CullVisitor::apply(Transform& node)
{//可以使用Node::setCullingActive设置某个节点始终不会被剔除if (isCulled(node)) return;// push the culling mode.基本上每个都有//记录当前节点视锥体筛选计算的结果(即,视锥体的哪几个面与节点的包围球有交集),并将这个结果压入堆栈,以便为下一次的计算提供方便。// 其实重点是便于当设置了camera->setComputeNearFarMode(COMPUTE_NEAR_FAR_USING_PRIMITIVES 或COMPUTE_NEAR_USING_PRIMITIVES)时,// 需要记录数据以便更新NF,参考CullVisitor中updateCalculatedNearFar和popProjectionMatrix源码。pushCurrentMask();// push the node's state.// 如果ss存在,尝试将这个StateSet对象置入状态树和渲染树中,添加到对应的状态节点/渲染元中,或者为其新建一个相关的节点StateSet* node_state = node.getStateSet();if (node_state) pushStateSet(node_state);// 计算并储存Transform节点的位置姿态矩阵。
//CullVisitor将尝试为矩阵变换节点提供一个存储矩阵(使用CullStack::createOrReuseMatrix),
//并使用CullStack::pushModelViewMatrix将计算得到的世界矩阵(Transform::computeLocalToWorldMatrix)压入堆栈,
//供后面的场景绘制和相应的用户回调使用。RefMatrix* matrix = createOrReuseMatrix(*getModelViewMatrix());node.computeLocalToWorldMatrix(*matrix,this);pushModelViewMatrix(matrix, node.getReferenceFrame());//处理用户自定义的节点筛选回调(Node::setCullCallback),并使用traverse将访问器对象传递给所有的子节点。handle_cull_callbacks_and_traverse(node);// 实际只是弹出堆栈中的临时数据,计算结果仍然留下,同下popModelViewMatrix();// pop the node's state off the render graph stack.if (node_state) popStateSet();// pop the culling mode.popCurrentMask();//筛选结果掩码
}
osgUtil::CullVisitor::apply(Geode&)
void CullVisitor::apply(Geode& node)
{if (isCulled(node)) return;// push the culling mode.pushCurrentMask();// push the node's state.//执行pushStateSet函数,根据Geode的渲染状态构建状态树和渲染树StateSet* node_state = node.getStateSet();if (node_state) pushStateSet(node_state);//处理筛选回调并传递到子节点,遍历Geode节点储存的所有几何体对象(Drawable),执行几何体的筛选。handle_cull_callbacks_and_traverse(node);// pop the node's state off the geostate stack.if (node_state) popStateSet();// pop the culling mode.popCurrentMask();
}
osgUtil::CullVisitor::apply(osg::Drawable& drawable)
void CullVisitor::apply(osg::Drawable& drawable)
{RefMatrix& matrix = *getModelViewMatrix();const BoundingBox &bb =drawable.getBoundingBox();
//使用用户自定义的几何体筛选回调(Drawable::setCullCallback)来进行处理if( drawable.getCullCallback() ){osg::DrawableCullCallback* dcb = drawable.getCullCallback()->asDrawableCullCallback();if (dcb){if( dcb->cull( this, &drawable, &_renderInfo ) == true ) return;}else{drawable.getCullCallback()->run(&drawable,this);}}//OSG则使用isCulled函数和CullVisitor::updateCalculatedNearFar函数(计算几何体的边界是否超出远/近平面)来执行几何体对象的筛选工作。if (drawable.isCullingActive() && isCulled(bb)) return;if (_computeNearFar && bb.valid()){if (!updateCalculatedNearFar(matrix,drawable,false)) return;}// need to track how push/pops there are, so we can unravel the stack correctly.unsigned int numPopStateSetRequired = 0;// push the geoset's state on the geostate stack.StateSet* stateset = drawable.getStateSet();if (stateset){++numPopStateSetRequired;pushStateSet(stateset);}CullingSet& cs = getCurrentCullingSet();if (!cs.getStateFrustumList().empty()){osg::CullingSet::StateFrustumList& sfl = cs.getStateFrustumList();for(osg::CullingSet::StateFrustumList::iterator itr = sfl.begin();itr != sfl.end();++itr){if (itr->second.contains(bb)){++numPopStateSetRequired;pushStateSet(itr->first.get());}}}float depth = bb.valid() ? distance(bb.center(),matrix) : 0.0f;if (osg::isNaN(depth)){OSG_NOTICE<<"CullVisitor::apply(Geode&) detected NaN,"<<std::endl<<"    depth="<<depth<<", center=("<<bb.center()<<"),"<<std::endl<<"    matrix="<<matrix<<std::endl;OSG_DEBUG << "    NodePath:" << std::endl;for (NodePath::const_iterator i = getNodePath().begin(); i != getNodePath().end(); ++i){OSG_DEBUG << "        \"" << (*i)->getName() << "\"" << std::endl;}}else{// 将几何体对象及其深度值置入状态树和渲染树。这一步在渲染后台树状结构的构建上有着举足轻重的作用。详见下文addDrawableAndDepth(&drawable,&matrix,depth);}for(unsigned int i=0;i< numPopStateSetRequired; ++i){//执行多次popStateSet函数,将_currentStateGraph和_currentRenderBin指针跳回到原先的位置,保证在遍历场景其余节点时状态树和渲染树的位置正确popStateSet();}
}
inline void CullVisitor::addDrawableAndDepth(osg::Drawable* drawable,osg::RefMatrix* matrix,float depth)

当前叶节点附带的所有Drawable几何体对象将被追加到第二十一日中所提及的当前状态节点(_currentStateGraph,使用StateGraph::addLeaf)和当前渲染元(_currentRenderBin,使用RenderBin::addStateGraph)当中,从而真正为状态树和渲染树添加了实质性的可绘制内容。

inline void CullVisitor::addDrawableAndDepth(osg::Drawable* drawable,osg::RefMatrix* matrix,float depth)
{if (_currentStateGraph->leaves_empty()){// this is first leaf to be added to StateGraph// and therefore should not already know to current render bin,// so need to add it._currentRenderBin->addStateGraph(_currentStateGraph);}//_currentStateGraph->addLeaf(new RenderLeaf(drawable,matrix,depth));_currentStateGraph->addLeaf(createOrReuseRenderLeaf(drawable,_projectionStack.back().get(),matrix,depth));
}
osgUtil::CullVisitor::apply(Camera&)

当场景树中出现一个摄像机节点时,它以下的场景子树将按照这个摄像机的筛选、视口、观察矩阵和投影矩阵设置进行显示。我们也可以使用此摄像机指向另一个图形设备(窗口),Camera节点的特性使得HUD文字,鹰眼图等效果都可以在OSG的场景中轻松实现。

删除不影响理解的代码:

void CullVisitor::apply(osg::Camera& camera)
{// push the node's state.StateSet* node_state = camera.getStateSet();if (node_state) pushStateSet(node_state);//加载当前Camera的筛选设置(setCullSettings),并保存之前的设置// Save current cull settingsCullSettings saved_cull_settings(*this);#if 1// set cull settings from this CamerasetCullSettings(camera);// inherit the settings from aboveinheritCullSettings(saved_cull_settings, camera.getInheritanceMask());// activate all active cull settings from this CamerainheritCullSettings(camera);//加载当前Camera的遍历掩码(setTraversalMask),这里的遍历掩码往往是用户使用CullSettings::setCullMask函数设置的。
//节点掩码(setNodeMask)与遍历掩码“与”操作之后为0的节点将不会在当前摄像机中显示。// set the cull mask.unsigned int savedTraversalMask = getTraversalMask();bool mustSetCullMask = (camera.getInheritanceMask() & osg::CullSettings::CULL_MASK) == 0;if (mustSetCullMask) setTraversalMask(camera.getCullMask());//得到当摄像机的视口,投影矩阵和模型视点矩阵,并依次压入堆栈(pushViewport,pushProjectionMatrix与pushModelViewMatrix函数)RefMatrix& originalModelView = *getModelViewMatrix();osg::RefMatrix* projection = 0;osg::RefMatrix* modelview = 0;if (camera.getReferenceFrame()==osg::Transform::RELATIVE_RF){if (camera.getTransformOrder()==osg::Camera::POST_MULTIPLY){projection = createOrReuseMatrix(*getProjectionMatrix()*camera.getProjectionMatrix());modelview = createOrReuseMatrix(*getModelViewMatrix()*camera.getViewMatrix());}else // pre multiply{projection = createOrReuseMatrix(camera.getProjectionMatrix()*(*getProjectionMatrix()));modelview = createOrReuseMatrix(camera.getViewMatrix()*(*getModelViewMatrix()));}}else{// an absolute reference frameprojection = createOrReuseMatrix(camera.getProjectionMatrix());modelview = createOrReuseMatrix(camera.getViewMatrix());}if (camera.getViewport()) pushViewport(camera.getViewport());// record previous near and far values.value_type previous_znear = _computed_znear;value_type previous_zfar = _computed_zfar;// take a copy of the current near plane candidatesDistanceMatrixDrawableMap  previousNearPlaneCandidateMap;previousNearPlaneCandidateMap.swap(_nearPlaneCandidateMap);DistanceMatrixDrawableMap  previousFarPlaneCandidateMap;previousFarPlaneCandidateMap.swap(_farPlaneCandidateMap);_computed_znear = FLT_MAX;_computed_zfar = -FLT_MAX;pushProjectionMatrix(projection);pushModelViewMatrix(modelview, camera.getReferenceFrame());。。。。}
  • 接下来是摄像机节特有的。当我们使用Camera::setRenderOrder设置了摄像机渲染的顺序时,这里将针对采用PRE_RENDER和POST_RENDER方式的摄像机新建一个“渲染台”(RenderStage),并使用摄像机的相关参数来初始化这个渲染台。
  • 此时Camera节点的子树将全部追加到新建的“渲染台”当中(并根据渲染细节的设置生成渲染树),最后使用addPreRenderStage或者addPostRenderStage函数将新建渲染台追加到当前RenderStage对象的相应列表当中。在渲染过程中,各个摄像机将按照渲染台的顺序执行渲染工作。
  • 对于设置为NESTED_RENDER的摄像机(默认设置),不存在前序渲染/后序渲染这一说法,因此直接执行前文所述的handle_cull_callbacks_and_traverse函数,继续向子节点遍历。
void CullVisitor::apply(osg::Camera& camera)
{
。。。if (camera.getRenderOrder()==osg::Camera::NESTED_RENDER){handle_cull_callbacks_and_traverse(camera);}else{// set up lighting.// currently ignore lights in the scene graph itself..// will do later.osgUtil::RenderStage* previous_stage = getCurrentRenderBin()->getStage();// use render to texture stage.// create the render to texture stage.osg::ref_ptr<osgUtil::RenderStageCache> rsCache = dynamic_cast<osgUtil::RenderStageCache*>(camera.getRenderingCache());if (!rsCache){rsCache = new osgUtil::RenderStageCache;camera.setRenderingCache(rsCache.get());}osg::ref_ptr<osgUtil::RenderStage> rtts = rsCache->getRenderStage(this);if (!rtts){OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*(camera.getDataChangeMutex()));rtts = _rootRenderStage.valid() ? osg::cloneType(_rootRenderStage.get()) : new osgUtil::RenderStage;rsCache->setRenderStage(this,rtts.get());rtts->setCamera(&camera);if ( camera.getInheritanceMask() & DRAW_BUFFER ){// inherit draw buffer from above.rtts->setDrawBuffer(previous_stage->getDrawBuffer(),previous_stage->getDrawBufferApplyMask());}else{rtts->setDrawBuffer(camera.getDrawBuffer());}if ( camera.getInheritanceMask() & READ_BUFFER ){// inherit read buffer from above.rtts->setReadBuffer(previous_stage->getReadBuffer(), previous_stage->getReadBufferApplyMask());}else{rtts->setReadBuffer(camera.getReadBuffer());}}else{// reusing render to texture stage, so need to reset it to empty it from previous frames contents.rtts->reset();}// cache the StateGraph and replace with a clone of the existing parental chain.osg::ref_ptr<StateGraph> previous_rootStateGraph = _rootStateGraph;StateGraph* previous_currentStateGraph = _currentStateGraph;// replicate the StageGraph parental chain so that state graph and render leaves are kept local to the Camera's RenderStage.{typedef std::vector< osg::ref_ptr<StateGraph> > StageGraphStack;StageGraphStack stateGraphParentalChain;StateGraph* sg = _currentStateGraph;while(sg){stateGraphParentalChain.push_back(sg);sg = sg->_parent;}_rootStateGraph = rtts->getStateGraph();if (_rootStateGraph){_rootStateGraph->clean();}else{_rootStateGraph = new StateGraph;// assign the state graph to the RenderStage to ensure it remains in memory for the draw traversal.rtts->setStateGraph(_rootStateGraph.get());}_currentStateGraph = _rootStateGraph.get();StageGraphStack::reverse_iterator ritr = stateGraphParentalChain.rbegin();if (ritr!=stateGraphParentalChain.rend()){const osg::StateSet* ss = (*ritr++)->getStateSet();_rootStateGraph->setStateSet(ss);while(ritr != stateGraphParentalChain.rend()){_currentStateGraph = _currentStateGraph->find_or_insert((*ritr++)->getStateSet());}}}// set up clear masks/valuesrtts->setClearDepth(camera.getClearDepth());rtts->setClearAccum(camera.getClearAccum());rtts->setClearStencil(camera.getClearStencil());rtts->setClearMask((camera.getInheritanceMask() & CLEAR_MASK) ? previous_stage->getClearMask() : camera.getClearMask());rtts->setClearColor((camera.getInheritanceMask() & CLEAR_COLOR) ? previous_stage->getClearColor() : camera.getClearColor());// set the color mask.osg::ColorMask* colorMask = camera.getColorMask()!=0 ? camera.getColorMask() : previous_stage->getColorMask();rtts->setColorMask(colorMask);// set up the viewport.osg::Viewport* viewport = camera.getViewport()!=0 ? camera.getViewport() : previous_stage->getViewport();rtts->setViewport( viewport );// set initial view matrixrtts->setInitialViewMatrix(modelview);// set up to charge the same PositionalStateContainer is the parent previous stage.osg::Matrix inheritedMVtolocalMV;inheritedMVtolocalMV.invert(originalModelView);inheritedMVtolocalMV.postMult(*getModelViewMatrix());rtts->setInheritedPositionalStateContainerMatrix(inheritedMVtolocalMV);rtts->setInheritedPositionalStateContainer(previous_stage->getPositionalStateContainer());// record the render bin, to be restored after creation// of the render to textosgUtil::RenderBin* previousRenderBin = getCurrentRenderBin();// set the current renderbin to be the newly created stage.setCurrentRenderBin(rtts.get());// traverse the subgraph{handle_cull_callbacks_and_traverse(camera);}// restore the previous renderbin.setCurrentRenderBin(previousRenderBin);if (rtts->getStateGraphList().size()==0 && rtts->getRenderBinList().size()==0){// getting to this point means that all the subgraph has been// culled by small feature culling or is beyond LOD ranges.}// restore cache of the StateGraph_rootStateGraph->prune();_rootStateGraph = previous_rootStateGraph;_currentStateGraph = previous_currentStateGraph;// and the render to texture stage to the current stages// dependency list.switch(camera.getRenderOrder()){case osg::Camera::PRE_RENDER:getCurrentRenderBin()->getStage()->addPreRenderStage(rtts.get(),camera.getRenderOrderNum());break;default:getCurrentRenderBin()->getStage()->addPostRenderStage(rtts.get(),camera.getRenderOrderNum());break;}}//从堆栈中依次弹出模型视点矩阵,投影矩阵和摄像机视口的临时计算量,以及恢复遍历掩码和筛选设置的原先值。从而回到上级摄像机的控制当中,继续场景图形的遍历工作。// restore the previous model view matrix.popModelViewMatrix();// restore the previous model view matrix.popProjectionMatrix();// restore the original near and far values_computed_znear = previous_znear;_computed_zfar = previous_zfar;// swap back the near plane candidatespreviousNearPlaneCandidateMap.swap(_nearPlaneCandidateMap);previousFarPlaneCandidateMap.swap(_farPlaneCandidateMap);if (camera.getViewport()) popViewport();// restore the previous traversal mask settingsif (mustSetCullMask) setTraversalMask(savedTraversalMask);// restore the previous cull settingssetCullSettings(saved_cull_settings);// pop the node's state off the render graph stack.if (node_state) popStateSet();
}

part3.5 依次执行RenderStage::sort和StateGraph::prune函数,对cullVisitor之后得到的渲染树内容进行排序和精简

构建过程中可能有些空节点需要剔除

part3.6 计算出场景中动态对象(DYNAMIC)的数目并保存到SceneView的成员变量_dynamicObjectCount中

bool SceneView::cullStage(const osg::Matrixd& projection,const osg::Matrixd& modelview,osgUtil::CullVisitor* cullVisitor, osgUtil::StateGraph* rendergraph, osgUtil::RenderStage* renderStage, osg::Viewport *viewport)
{
。。。renderStage->sort();// prune out any empty StateGraph children.// note, this would be not required if the rendergraph had been// reset at the start of each frame (see top of this method) but// a clean has been used instead to try to minimize the amount of// allocation and deleting of the StateGraph nodes.rendergraph->prune();// set the number of dynamic objects in the scene._dynamicObjectCount += renderStage->computeNumberOfDynamicRenderLeaves();bool computeNearFar = (cullVisitor->getComputeNearFarMode()!=osgUtil::CullVisitor::DO_NOT_COMPUTE_NEAR_FAR) && getSceneData()!=0;return computeNearFar;
}

part4 执行CullVisitor::clampProjectionMatrix,重新设定场景视图的投影矩阵

根据远/近平面的取值,重新设定场景视图的投影矩阵。由于远/近平面是由筛选访问器计算出来的,有的时候我们可能不希望按照它的计算值来进行视图的处理,此时可以使用setClampProjectionMatrixCallback设置SceneView的投影矩阵计算回调,自己编写相关的处理函数。

void SceneView::cull()
{
。。。else{_cullVisitor->setTraversalMask(_cullMask);bool computeNearFar = cullStage(getProjectionMatrix(),getViewMatrix(),_cullVisitor.get(),_stateGraph.get(),_renderStage.get(),getViewport());if (computeNearFar){CullVisitor::value_type zNear = _cullVisitor->getCalculatedNearPlane();CullVisitor::value_type zFar = _cullVisitor->getCalculatedFarPlane();_cullVisitor->clampProjectionMatrix(getProjectionMatrix(),zNear,zFar);}}
。。。
}
/** Clamp the projection double matrix to computed near and far values, use callback if it exists,* otherwise use default CullVisitor implementation.*/inline bool clampProjectionMatrix(osg::Matrixd& projection, value_type& znear, value_type& zfar) const{double zn = znear;double zf = zfar;bool result = false;if (_clampProjectionMatrixCallback.valid()) result = _clampProjectionMatrixCallback->clampProjectionMatrixImplementation(projection, zn, zf);else result = clampProjectionMatrixImplementation(projection, zn, zf);if (result){znear = zn;zfar = zf;return true;}elsereturn false;}//
bool CullVisitor::clampProjectionMatrixImplementation(osg::Matrixf& projection, double& znear, double& zfar) const
{return osg::clampProjectionMatrix( projection, znear, zfar, _nearFarRatio );
}//

https://github.com/openscenegraph/OpenSceneGraph/blob/OpenSceneGraph-3.6/include/osgUtil/CullVisitor

http://bbs.osgchina.org/forum.php?mod=viewthread&tid=613&highlight=OSG%D4%AD%B4%B4%BD%CC%B3%CC%A3%BA%D7%EE%B3%A4%B5%C4%D2%BB%D6%A1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/52725.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

缺失ffmpeg.dll要用什么修复方法?快速恢复丢失的ffmpeg.dll文件

多媒体软件用户常常会遭遇一个提示&#xff1a;系统无法找到ffmpeg.dll文件。这类情况经常在启动视频编辑软件、流媒体播放应用或其他音视频处理工具时出现&#xff0c;导致相关程序无法正确加载和执行。ffmpeg.dll是一种关键的动态链接库文件&#xff0c;负责处理复杂的视频和…

ssrf+redis未授权访问漏洞复现

ssrfredis未授权访问漏洞复现 一&#xff0c;pikachu靶场练习 docker拉取环境&#xff1a; docker run -d -p 8765:80 8023/pikachu-expect:latest国内很多加速源都用不成&#xff0c;配置代理拉取即可&#xff0c;配置方式如下&#xff1a; 1&#xff0c;新建目录 mkdir -…

数据结构与算法的代码实现(C++版)

数据结构与算法的代码实现&#xff08;C版&#xff09; 1. 线性表的顺序表示和实现1.1 线性表的初始化1.2 线性表的销毁1.3 线性表的清空1.4 线性表的长度1.5 判断线性表是否为空1.6 线性表的线性表取值1.7 线性表的顺序查找1.8 线性表的插入1.9 线性表的删除总结 2. 线性表的链…

JavaScript ES6+ 新特性

JavaScript ES6 新特性 引言 随着前端技术的不断发展&#xff0c;JavaScript 语言也在不断演进。自 ES6&#xff08;ES2015&#xff09;发布以来&#xff0c;JavaScript 引入了许多新的特性和语法&#xff0c;极大地提升了开发者的编程体验和代码的可维护性。本篇文章将详细探…

真话有危险,测评需谨慎!一个家最大的内耗:谁都在抱怨,没人肯改变——早读(逆天打工人爬取热门微信文章解读)

现在都这么完了吗&#xff1f; 引言Python 代码第一篇 洞见 一个家最大的内耗&#xff1a;谁都在抱怨&#xff0c;没人肯改变第二篇 故事风云录结尾 引言 慢慢调整时间 一是现在有点忙 做那个传播声音的研究实验实在是有点没有头绪 没有头绪的事情你就不知道怎么安排时间 也就…

数学建模比赛(国赛)水奖攻略

之前很多同学私聊问我&#xff0c;学校要求参加数模比赛&#xff0c;但是不擅长建模编程&#xff0c;但又不想浪费这个时间该怎么办呢&#xff0c;今天就来给大家讲一下大家都非常感兴趣的内容——数学建模水奖攻略。分享一下博主直接参加比赛时候的经验。 一、选题技巧 有一句…

HarmonyOs如何获取rawfile目录下的所有文件列表

最近在做一个功能&#xff0c;需要使用获取rawfile下目录的所有文件 参考连接为&#xff1a; zRawfile-模块-C API-Localization Kit&#xff08;本地化开发服务&#xff09;-应用框架 - 华为HarmonyOS开发者 (huawei.com) 需要使用到native实现&#xff0c;实现步骤&#…

2008-2020年 中国健康与养老追踪调查CHARLS数据合集

中国健康与养老追踪调查&#xff08;China Health and Retirement Longitudinal Study, CHARLS&#xff09;是一项由北京大学国家发展研究院主持的大型跨学科调查项目。该项目始于2011年&#xff0c;每两到三年对样本进行一次追踪调查&#xff0c;旨在收集代表中国45岁及以上中…

面试经典算法150题系列-反转字符串中的单词

反转字符串中的单词 给你一个字符串 s &#xff0c;请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。 注意&#xff1a;输入字符串 s中可能…

关于告警,要想做好,从这些方面着手

各类监控系统都会产生告警事件&#xff0c;于是&#xff0c;就产生了 FlashDuty、PagerDuty、Opsgenie 这类产品&#xff0c;做告警事件的收敛降噪、排班认领升级等。如果你想增强自己公司的告警事件处理能力&#xff0c;参考&#xff08;chao xi&#xff09;这些产品的功能就可…

使用统计方法在AMD GPU上使用JAX Profiler可靠地比较大型生成AI模型中的算法性能

Using statistical methods to reliably compare algorithm performance in large generative AI models with JAX Profiler on AMD GPUs — ROCm Blogs 摘要 本文提供了一份详细的指南&#xff0c;介绍如何在JAX实现的生成AI模型中测量和比较各种算法的性能。利用JAX Profiler…

35岁程序员的4条出路!请提早布局!

小编准备入门了Python入门学习籽料80个Python爬虫实战入门实例 点击 领取&#xff08;无偿获得&#xff09; 20多岁&#xff0c;初入职场&#xff0c;满腔热血&#xff0c;对未来充满憧憬&#xff1b; 30多岁&#xff0c;家庭事业双重压力&#xff0c;开始迷茫&#xff0c;对…

阿里云发送短信功能(Java)

&#xff08;1&#xff09;注册用户&#xff0c;并且开通短信套餐 &#xff08;2&#xff09; 点击快速学习&#xff0c;然后绑定测试的手机号码。 选用专用测试签名&#xff08;自定义的话阿里可能会验证什么什么的比较麻烦&#xff09; 然后在选取调用API &#xff08;3&…

3秒AI写真出图,Stable Diffusion2024升级版+使用教程来了!(无需安装,解压即用)

要说今年摄影圈最大的新秀 那妥妥的就Stable Diffusion 比如下面的写真照片 你敢信这是SD绘画生成的&#xff1f; 就在刚刚它又全面升级了 新版无需安装&#xff0c;直接解压就能用 比之前推送的更加智能、快速和简单 另外还特意为大家准备了 Stable Diffusion 人工智能…

故障诊断 | 基于小波时频图与Swin Transformer的轴承故障诊断方法(PyTorch)

文章目录 文章概述程序设计参考资料文章概述 基于小波时频图与Swin Transformer的轴承故障诊断方法 针对用传统的故障诊断方法难以对非线性非平稳的柴油机故障信号进行准确高效诊断的问题, 提出基于小波时频图与Swin Transformer的故障诊断方法。该方法可以有效结合小波时频分…

Git实战精粹

一、快速入门 1. 什么是Git Git是一个分布式的版本控制软件。 软件&#xff0c;类似于QQ、office、dota等安装到电脑上才能使用的工具版本控制&#xff0c;类似于毕业论文、写文案、视频剪辑等&#xff0c;需要反复修改和保留原历史数据分布式 文件夹拷贝本地版本控制集中式…

如何在Java中使用protobuf

写在前面 本文看下在Java中如何使用protofbuf。 1&#xff1a;介绍 1.1&#xff1a;什么是protobuf 是一种数据格式&#xff0c;同json&#xff0c;xml&#xff0c;等。但是一种二进制数据格式。 1.2&#xff1a;强在哪里&#xff1f;为啥要用&#xff1f; 小&#xff0c…

JS中this的指向问题、JS的执行机制、offset、client、scroll

JS中this的指向问题 1. 在全局环境下 在全局环境中&#xff08;在浏览器中是 window 对象&#xff0c;在Node.js中是 global 对象&#xff09;&#xff0c;this 指向全局对象。 console.log(this window); // 在浏览器中为 true console.log(this.document ! undefined); //…

如何练高音技巧

如何练高音技巧 高音的练习技巧有&#xff1a;练“a”(啊)音&#xff0c;让口腔打开&#xff0c;声带放松&#xff0c;反复几次&#xff1b;再练“u”(呜)音&#xff0c;这个音可以有按摩声带的功能&#xff0c;也使声带进一步放松&#xff1b;发“i”(衣)音&#xff0c;逐步加…

k8s 四种Service类型(ClusterIP、NodePort、LoadBalancer、ExternalName)详解

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、k8s概述 2、Service在Kubernetes中的…