Ogre的渲染主循环解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DdogYuan/article/details/85011943

渲染循环

一般由应用程序调用root->startRendering(void)开启渲染循环。

void Root::startRendering(void)
{
         ...
        //游戏渲染循环是一个无限循环,除非被frame listeners或者queueEndRendering()终止
        mQueuedEnd = false;

        while( !mQueuedEnd )
        {
           ...
           if (!renderOneFrame())
                break;
        }
}

从RenderTarget到场景

而Root::renderOneFrame()函数将调用Root::_updateAllRenderTargets(void)

class _OgreExport Root : public Singleton<Root>, public RootAlloc
{
    protected:
        RenderSystem* mActiveRenderer;
    ...
    bool Root::_updateAllRenderTargets(void)
    {
        // update all targets but don't swap buffers
        mActiveRenderer->_updateAllRenderTargets(false);
        ...
    }
    ...
}

Root::_updateAllRenderTargets(void)将调用Root当前激活的RenderSystem实例的__updateAllRenderTargets(false)方法,在这里,RenderSystem对所有的RenderTarget进行update,RenderTarget指的是渲染的目的地,一种是RenderTexture,即渲染到纹理,另一种是RenderWindow,即渲染到窗口。

 /** The render targets, ordered by priority. */
RenderTargetPriorityMap mPrioritisedRenderTargets;

void RenderSystem::_updateAllRenderTargets(bool swapBuffers)
    {
        // Update all in order of priority
        // This ensures render-to-texture targets get updated before render windows
        RenderTargetPriorityMap::iterator itarg, itargend;
        itargend = mPrioritisedRenderTargets.end();
        for( itarg = mPrioritisedRenderTargets.begin(); itarg != itargend; ++itarg )
        {
            if( itarg->second->isActive() && itarg->second->isAutoUpdated())
                itarg->second->update(swapBuffers);
        }
    }

在当前激活的RenderSystem实例中,是用RenderTargetPriorityMap来保存RenderTarget,这是因为有的RenderTarget需要在其他RenderTarget之前渲染。

而RenderTarget::update最终会调用到RenderTarget::_updateAutoUpdatedViewports(bool updateStatistics)

void RenderTarget::_updateAutoUpdatedViewports(bool updateStatistics)
    {
        // Go through viewports in Z-order
        // Tell each to refresh
        ViewportList::iterator it = mViewportList.begin();
        while (it != mViewportList.end())
        {
            Viewport* viewport = (*it).second;
            if(viewport->isAutoUpdated())
            {
                _updateViewport(viewport,updateStatistics);
            }
            ++it;
        }
    }

可以看到,RenderTarget会遍历mViewPortList,对每一个Viewport调用_updateViewport(…)函数,即一个RenderTarget对应着多个Viewport,ViewPort是窗口中被更新的地方,一个窗口可以有多个ViewPort,多个Viewport可以在一个窗口中。例如下图中只有一个窗口,但是却有三个Viewport。
在这里插入图片描述

RenderTarget::_updateViewport(viewport,updateStatistics)随后会调用到Viewport::update(void)

void Viewport::update(void)
    {
        if (mCamera)
        {
            if (mCamera->getViewport() != this)
                mCamera->_notifyViewport(this);

            // Tell Camera to render into me
            mCamera->_renderScene(this, mShowOverlays);
        }
    }

对于每一个Viewport,只有一个Camera引用。一个Viewport是对一个Camera所拍摄到的场景的反映。所以说一个Viewport只能绑定一个Camera。Camera::_renderScene(Viewport *vp, bool includeOverlays)函数的作用主要为调用其绑定的Scene的渲染函数:

SceneManager *mSceneMgr;

void Camera::_renderScene(Viewport *vp, bool includeOverlays)
    {
       ...
        //render scene
        mSceneMgr->_renderScene(this, vp, includeOverlays);
       ...
    }

一个Camera同时只可以拍摄一个场景,所以一个Camera只可以绑定一个场景。

从场景到渲染队列

Ogre中,场景定义了一个游戏世界的组织,比如说主角在哪,怪物的出生点在哪,目的地在哪,可以类比Unity中的scene。每一个场景对应一个SceneManager,其负责场景物体的创建和删除,以及进行场景查询和调用渲染队列的渲染。

其中,场景查询的作用为决定哪些对象被送入到RenderSystem中进行渲染,通过减少需要渲染的物体来获得更好的渲染性能表现。

上节说到_renderScene函数,该函数会更新场景的变换信息(Transform)以及进行场景查询的操作:

SceneManager::_renderScene(Camera* camera, Viewport* vp, bool includeOverlays)
{
    ...
    _updateSceneGraph(camera);
    ...
    setViewport(vp);
    ...
    prepareRenderQueue();
    ...
    _findVisibleObjects(camera, &(camVisObjIt->second),
                mIlluminationStage == IRS_RENDER_TO_TEXTURE? true : false);
    ...
    
}

其中_renderScene中会调用__updateSceneGraph函数更新整个场景的变换信息:

void SceneManager::_updateSceneGraph(Camera* cam)
{
    ...
    // Cascade down the graph updating transforms & world bounds
    // ...
    getRootSceneNode()->_update(true, false);
    ...
}

接下来是setViewport函数,它会调用当前的RenderSystem的setViewport操作,设置Viewport中所包含的RenderTarget为当前所要渲染的目标,而Viewport中的区域为当前所要渲染的目标区域。

void SceneManager::setViewport(Viewport* vp)
{
    mCurrentViewport = vp;
    // Set viewport in render system
    mDestRenderSystem->_setViewport(vp);
    // Set the active material scheme for this viewport
    MaterialManager::getSingleton().setActiveScheme(vp->getMaterialScheme());
}

接下来一个重要的概念RenderQueue。可以简单把它想成是一个容器,里面的元素就是Renderable,每个Renderable可以看成是每次绘制时需要渲染的物体,可以是一个模型,也可以是模型的一部分。在RenderQueue中,它会按材质Material来分组这些Renderable,还会对Renderable进行一定规则的排序。之所以这么做,是因为使用相同材质的Material会使用同样的Shader,通过减少Shader切换的频率可以达到优化渲染效率的作用。

在每一次调用SceneManager::_renderScene时,都会调用SceneManager::prepareRenderQueue来清理RenderQueue,然后再调用SceneManager::__findVisibleObjects来把当前摄像机所能看见的物体都加入到RenderQueue中,该动作是为了减少送入渲染队列中的物体数量:

void SceneManager::_findVisibleObjects(
    Camera* cam, VisibleObjectsBoundsInfo* visibleBounds, bool onlyShadowCasters)
{
    // Tell nodes to find, cascade down all nodes
    getRootSceneNode()->_findVisibleObjects(cam, getRenderQueue(), visibleBounds, true, 
        mDisplayNodes, onlyShadowCasters);
}

渲染开始

在进行完场景查询之后,我们已经将需要渲染的物体送入了渲染队列中,回忆一下使用图形API渲染物体时候的流程:

  1. 设置顶点数据,顶点属性,贴图数据:这一步可以封装成读取模型以及其对应的贴图操作
  2. 清理颜色缓冲、深度缓冲等
  3. 设置shader并传入其所需的参数
  4. 绘制顶点并交换窗口缓冲

在SceneManager::_renderScene函数中,当执行完场景查询之后,便进入了实际渲染流程:

    ...
    // Clear the viewport if required
    if (mCurrentViewport->getClearEveryFrame())
    {
        mDestRenderSystem->clearFrameBuffer(
            mCurrentViewport->getClearBuffers(), 
            mCurrentViewport->getBackgroundColour(),
            mCurrentViewport->getDepthClear() );
    }        
    ...
    // Set rasterisation mode
    mDestRenderSystem->_setPolygonMode(camera->getPolygonMode());

    // Set initial camera state
    mDestRenderSystem->_setProjectionMatrix(mCameraInProgress->getProjectionMatrixRS());
    
    mCachedViewMatrix = mCameraInProgress->getViewMatrix(true);
    ...
    ...
    setViewMatrix(mCachedViewMatrix);

    // Render scene content
    {
        ...
        _renderVisibleObjects();
    }

可以看到清理缓冲以及设置好Projection Matrix、ViewMatrix等操作。

参考

http://gad.qq.com/article/detail/14402

猜你喜欢

转载自blog.csdn.net/DdogYuan/article/details/85011943