最长的一帧学习 part3

2024-08-29 02:36
文章标签 学习 最长 一帧 part3

本文主要是介绍最长的一帧学习 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

这篇关于最长的一帧学习 part3的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1116596

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

poj3261(可重复k次的最长子串)

题意:可重复k次的最长子串 解题思路:求所有区间[x,x+k-1]中的最小值的最大值。求sa时间复杂度Nlog(N),求最值时间复杂度N*N,但实际复杂度很低。题目数据也比较水,不然估计过不了。 代码入下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识