最长的一帧学习 part3


原文

八、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之后得到的渲染树内容进行排序和精简

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();

。。。
}
  • RenderStage::sort函数的执行是按照前序渲染台,当前渲染台,后序渲染台的顺序进行的,其中前序渲染台(RenderStage::_preRenderList)和后序渲染台(_postRenderList)是通过Camera::setRenderOrder实现的,它们保存了指定摄像机及其场景子树的渲染树结构。
  • 事实上是通过RenderBin::sortImplementation函数实现的,如果我们希望实现自定义的渲染树排序动作,通过RenderBin::setSortCallback函数为根节点渲染台(可以从摄像机的SceneView对象中取得)设置新的排序回调即可。
  • !!!需要注意的是:要排序的对象仅仅是渲染树中各个渲染元(RenderBin)中保存的状态节点(StateGraph)或者渲染叶(RenderLeaf),渲染元之间不需要进行排序(那样会打乱实际的绘制顺序)。
void RenderStage::sort()
{
    for(RenderStageList::iterator pre_itr = _preRenderList.begin();
        pre_itr != _preRenderList.end();
        ++pre_itr)
    {
        pre_itr->second->sort();
    }

    RenderBin::sort();

    for(RenderStageList::iterator post_itr = _postRenderList.begin();
        post_itr != _postRenderList.end();
        ++post_itr)
    {
        post_itr->second->sort();
    }
}

//
void RenderBin::sort()
{
    if (_sorted) return;

    for(RenderBinList::iterator itr = _bins.begin();
        itr!=_bins.end();
        ++itr)
    {
        itr->second->sort();
    }

    if (_sortCallback.valid())
    {
        _sortCallback->sortImplementation(this);
    }
    else sortImplementation();

    _sorted = true;
}

//
void RenderBin::sortImplementation()
{
    switch(_sortMode)
    {
        case(SORT_BY_STATE)://不排序
            sortByState();
            break;
        case(SORT_BY_STATE_THEN_FRONT_TO_BACK):
            sortByStateThenFrontToBack();
            break;
        case(SORT_FRONT_TO_BACK):
            sortFrontToBack();//采用深度值升序的原则执行所有元素的排序
            break;
        case(SORT_BACK_TO_FRONT):
            sortBackToFront();//采用深度值降序的原则执行所有元素的排序
            break;
        case(TRAVERSAL_ORDER):
            sortTraversalOrder();
            break;
    }
}

//
void RenderBin::sortBackToFront()
{
    copyLeavesFromStateGraphListToRenderLeafList();

    // now sort the list into ascending depth order.
    std::sort(_renderLeafList.begin(),_renderLeafList.end(),BackToFrontSortFunctor());

//    cout << "sort back to front"<<endl;
}

//
struct BackToFrontSortFunctor
{
    bool operator() (const RenderLeaf* lhs,const RenderLeaf* rhs) const
    {
    	// 深度值是在CullVisitor::apply(Geode&)函数中计算出来的
        return (rhs->_depth<lhs->_depth);
    }
};

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

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)
{
。。。
    // 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 );
}

//
template<class matrix_type, class value_type>
bool clampProjectionMatrix(matrix_type& projection, double& znear, double& zfar, value_type nearFarRatio)
{
    double epsilon = 1e-6;
    if (zfar<znear-epsilon)
    {
        if (zfar != -FLT_MAX || znear != FLT_MAX)
        {
            OSG_INFO<<"_clampProjectionMatrix not applied, invalid depth range, znear = "<<znear<<"  zfar = "<<zfar<<std::endl;
        }
        return false;
    }

    if (zfar<znear+epsilon)
    {
    	// 扩大他们的距离,略偏于他们的平均值
        // znear and zfar are too close together and could cause divide by zero problems
        // late on in the clamping code, so move the znear and zfar apart.
        double average = (znear+zfar)*0.5;
        znear = average-epsilon;
        zfar = average+epsilon;
        // OSG_INFO << "_clampProjectionMatrix widening znear and zfar to "<<znear<<" "<<zfar<<std::endl;
    }
    
	// 正交
    if (fabs(projection(0,3))<epsilon  && fabs(projection(1,3))<epsilon  && fabs(projection(2,3))<epsilon )
    {
        // OSG_INFO << "Orthographic matrix before clamping"<<projection<<std::endl;
	
		// 扩展近平面和远平面
        value_type delta_span = (zfar-znear)*0.02;
        if (delta_span<1.0) delta_span = 1.0;
        value_type desired_znear = znear - delta_span;
        value_type desired_zfar = zfar + delta_span;

        // assign the clamped values back to the computed values.
        znear = desired_znear;
        zfar = desired_zfar;

		// 并相应调整矩阵
        projection(2,2)=-2.0f/(desired_zfar-desired_znear);
        projection(3,2)=-(desired_zfar+desired_znear)/(desired_zfar-desired_znear);

        // OSG_INFO << "Orthographic matrix after clamping "<<projection<<std::endl;
    }
    // 透视
    else
    {

        // OSG_INFO << "Persepective matrix before clamping"<<projection<<std::endl;

        //std::cout << "_computed_znear"<<_computed_znear<<std::endl;
        //std::cout << "_computed_zfar"<<_computed_zfar<<std::endl;

        value_type zfarPushRatio = 1.02;
        value_type znearPullRatio = 0.98;

        //znearPullRatio = 0.99;

		// 略微增大远平面,略微缩小近平面
        value_type desired_znear = znear * znearPullRatio;
        value_type desired_zfar = zfar * zfarPushRatio;

        // near plane clamping.
        // 根据比例修正进平面,不会靠的太近
        double min_near_plane = zfar*nearFarRatio;
        if (desired_znear<min_near_plane) desired_znear=min_near_plane;

        // assign the clamped values back to the computed values.
        znear = desired_znear;
        zfar = desired_zfar;

        value_type trans_near_plane = (-desired_znear*projection(2,2)+projection(3,2))/(-desired_znear*projection(2,3)+projection(3,3));
        value_type trans_far_plane = (-desired_zfar*projection(2,2)+projection(3,2))/(-desired_zfar*projection(2,3)+projection(3,3));

        value_type ratio = fabs(2.0/(trans_near_plane-trans_far_plane));
        value_type center = -(trans_near_plane+trans_far_plane)/2.0;

        projection.postMult(osg::Matrix(1.0f,0.0f,0.0f,0.0f,
                                        0.0f,1.0f,0.0f,0.0f,
                                        0.0f,0.0f,ratio,0.0f,
                                        0.0f,0.0f,center*ratio,1.0f));

        // OSG_INFO << "Persepective matrix after clamping"<<projection<<std::endl;
    }
    return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值