最长的一帧学习 part3

news2024/9/20 21:22:35

文章目录

  • 八、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 uniforms
    updateUniforms();

    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::containsOccluderNodes
    if (_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 1
    renderStage->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);
       // 相当于accept
       else 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 settings
    CullSettings saved_cull_settings(*this);
    
#if 1
    // set cull settings from this Camera
    setCullSettings(camera);

    // inherit the settings from above
    inheritCullSettings(saved_cull_settings, camera.getInheritanceMask());

    // activate all active cull settings from this Camera
    inheritCullSettings(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 frame
        projection = 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 candidates
    DistanceMatrixDrawableMap  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/values
        rtts->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 matrix
        rtts->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 text
        osgUtil::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 candidates
    previousNearPlaneCandidateMap.swap(_nearPlaneCandidateMap);
    previousFarPlaneCandidateMap.swap(_farPlaneCandidateMap);


    if (camera.getViewport()) popViewport();

    // restore the previous traversal mask settings
    if (mustSetCullMask) setTraversalMask(savedTraversalMask);

    // restore the previous cull settings
    setCullSettings(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;
            }
            else
                return 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.coloradmin.cn/o/2082398.html

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

相关文章

【python】关于判断空值None、判断len()=0,和 if not 的区别

如果我们问一个问题&#xff1a;如果一个东西是不存在的&#xff0c;那么它的长度是多少&#xff1f; 我一开始会不假思索地回答&#xff1a;都不存在了&#xff0c;长度肯定是0啦。 但是细想一下&#xff0c;它都不存在了&#xff0c;还怎么会有长度呢&#xff1f; 这就有点像…

【Java】数据类型与变量(二)

目录 3.变量 3.1什么是变量&#xff08;变量的概念&#xff09; 3.2语法格式 ​编辑​编辑3.3整型变量 3.3.1整型变量如何定义 ​编辑 3.3.2长整型变量 3.3.3短整型变量 3.3.4字节型变量 3.4浮点型变量 3.4.1双精度浮点型 3.4.2单精度浮点型 3.4.3单精度浮点型与双…

【大模型开源教程】Git开源教程「动手学大模型应用开发」,学微调也太简单了

LLM 正逐步成为信息世界的新革命力量&#xff0c;其通过强大的自然语言理解、自然语言生成能力&#xff0c;为开发者提供了新的、更强大的应用开发选择。随着国内外井喷式的 LLM API 服务开放&#xff0c;如何基于 LLM API 快速、便捷地开发具备更强能力、集成 LLM 的应用&…

AI搜索“懒人神器”,如何向谷歌和百度发起挑战?

“属于传统搜索的时代&#xff0c;已经彻底落幕了。” 2024年2月&#xff0c;全球IT分析与评估机构Gartner在一份报告中如此判断。同时该机构预测&#xff0c;到2026年&#xff0c;传统搜索引擎的搜索量将减少25&#xff05;。 与此同时&#xff0c;从最近公布的财报来看&…

源代码加密是什么?给源代码加密如何做?

一、源代码加密是什么&#xff1f; 源代码加密是一种通过特定的加密算法和工具对软件源代码文件进行保护的技术&#xff0c;使得只有授权用户才能解密和访问代码。加密技术可以防止未经授权的访问、阅读、修改或复制源代码。源代码加密&#xff0c;则是对源代码文件进行加密处…

Kubernetes高级调度2

一.Taint和Toleration 1.为什么使用taint &#xff08;1&#xff09;不让pod被部署到被设置为污点的节点上。 &#xff08;2&#xff09;不让master节点部署业务&#xff0c;只部署系统组件 &#xff08;3&#xff09;除了指点pod外&#xff0c;不想让节点部署其他pod 2.为…

解决ESP32 VScode环境报错:Something went wrong while trying to build the project。

当我在vscode开发esp32时&#xff0c;点击构建项目 报了个错。 说明&#xff1a;项目目录包含了中文字符&#xff0c;我给换成全英文路径&#xff0c;再次尝试 成功 改完发现还有问题&#xff0c;来到这个目录&#xff0c;里面的中文也要改成英文。&#xff08;不止&#xff0…

Qt是不是经常写个QWidget输入参数?

发现Qt自带的一个输入控件QInputDialog类 QInputDialog类提供了一个简单方便的对话框&#xff0c;用于从用户获取单个值。 输入值可以是字符串、数字或列表中的项。必须设置一个标签来告诉用户他们应该输入什么。 提供了五个静态方便函数:getText()、getMultiLineText()、getI…

干货:2024年四大免费PDF编辑器精选指南!

在日常生活工作中&#xff0c;我们几乎每天都会接触到PDF文档。当我们需要对这些文档进行编辑时&#xff0c;一些出色的免费PDF编辑器十分重要。接下来&#xff0c;我将分享自己使用过的几款优秀的免费PDF编辑器&#xff01; 福昕PDF编辑器&#xff1a;简洁高效的编辑体验 链…

【Python基础】学习Python常见的几种运行报错,看完之后,你就不会再犯错了!!!(附带解析)

学习Python时&#xff0c;遇到运行时错误是很常见的&#xff0c;这些错误通常是因为代码中存在语法错误、逻辑错误或是对Python的某些特性理解不够深入所导致的。 如果你也是刚入门的小伙伴呢&#xff0c;小编为你们准备了入门Python学习籽料和Python入门实践&#xff0c;点击…

期权末日轮的前提——高杠杆特性是怎样的?

今天带你了解期权末日轮的前提——高杠杆特性是怎样的&#xff1f;期权末日轮是临近期权到期行权&#xff0c;期权合约往往出现大的波动涨跌幅。 如何在实际交易过程中艺术的运用期权产品的高杠杆&#xff0c;始终是一个永无止境的话题&#xff0c;结合一些实战上的经验就是&a…

miniQMT怎么获取历史/最新行情?miniQMT原生python环境如何获取历史/最新行情?

原生Python 调用方法 python from xtquant import xtdata xtdata.get_market_data_ex(field_list[],# 字段stock_list[],# 合约代码列表period1d,# 数据周期——1m、5m、1d、tickstart_time,# 数据起始时间%Y%m%d或%Y%m%d%H%M%Send_time,# 数据结束时间%Y%m%d或%Y%m%d%H%M%Sc…

QT中UI实现小功能的步骤

1、新建一个目录 要勾选Generate form&#xff0c;生成一个.ui文件 把Shadow build的勾选取消&#xff0c;避免产生两个文件夹&#xff01; 2、具体步骤 1.在UI界面中拖拽需要的组件对象 2.修改组件对象的名称 3.保存并运行&#xff0c;根据运行结果适当调整界面布局 4.业…

真正懂耳机的人选了什么?开放式耳机推荐

真正懂耳机的人&#xff0c;在选择耳机时&#xff0c;往往会更注重音质的自然还原、佩戴的舒适度、以及耳机的设计是否符合个人审美和生活习惯。开放式耳机以其独特的开放式设计&#xff0c;能够带来更为自然、宽广的声场体验&#xff0c;同时减少对耳朵的压迫感&#xff0c;成…

宝藏!《联盟作文班配套讲义》(九尾篇)

本文内容&#xff0c;全部选自自动化考研联盟企业店的&#xff1a;独家制作&#xff0c;《联盟作文班配套讲义》(九尾篇)。 Part1&#xff1a;资料封面&目录 Part2&#xff1a;各章节内容展示 第一章 考研英语作文开篇总述 第二章、 小作文(英语一英语二) 第三章、 英语&…

docker拉取镜像并安装mysql

一. linux安装docker 1. 先删除本机旧的或者残留的docker sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine2. 安装依赖 sudo yum update -y && sudo yum i…

相机标定板生成

推荐一个好用的网站&#xff1a; https://calib.io/pages/camera-calibration-pattern-generator 可以生成多种样式的棋盘格&#xff0c; 大小&#xff0c; 行列数也都可以设置&#xff0c; 非常方便。 另外推荐结合相机标定程序使用&#xff0c; 效果更佳&#xff01;&#x…

分布式中间件

1.Nacos 服务注册和服务发现原理图&#xff1a; 1.服务提供方将集群信息注册到Nacos&#xff0c;并定期心跳包提供健康信息&#xff0c;宕机即剔除 2.服务消费方定期拉取订阅信息&#xff0c;获取服务实例列表 3.服务集群的负载均衡是在消费者一方进行选择 负载均衡&#xf…

使用第三方API打造智能对话:Amazon Lex聊天机器人轻松实现

这是本系列文章的第四篇&#xff0c;旨在通过动手实践&#xff0c;帮助大家学习亚马逊云科技的生成式AI相关技能。通过这些文章&#xff0c;大家将掌握如何利用亚马逊云科技的各类服务来应用AI技术。 那么让我们开始今天的内容吧&#xff01; 引言 什么是Amazon Lex&#xf…

第十三章- 游戏结束

在这个例子中我们开始研究一个带有记分的真正可玩的游戏。我们给MyWidget一个新的名字GameBoard并添加一些槽。 我们把定义放在gamebrd.h并把实现放在gamebrd.cpp。 CannonField现在有了一个游戏结束状态。 在LCDRange中的布局问题已经修好了。 lcdrange.h包含LCDRange类定…