Ogre2 学习之基本组成部分与执行逻辑

194 篇文章 42 订阅

Ogre2 的基本组成部分与执行逻辑

大致上,可以将Ogre当中的类型分为资源模块和逻辑模块。资源模块主要用于资源的管理,加载,获取和使用。而逻辑模块则实现了最终的渲染效果,并可以根据用户的想法做特殊的定制操作。

Ogre的资源模块

Ogre的编程大量采用了管理单例的结构,从基于GraphicsSystem的范例当中可以看到,使用图形引擎需要加载的几种资源:

  1. Mesh—Skeleton
  2. HLMSDatablock
  3. Compositor

前面两者最终绑定到Item上并且连接到场景图当中,而Compositor则负责控制整个渲染引擎的渲染逻辑。通过加载上述资源,然后执行Ogre内置的渲染逻辑,即可实现各种各样的效果。

Ogre的逻辑模块

从GraphicsSystem开始单步调试代码可以大致看到Ogre有以下的运行逻辑:

  1. 进入main函数之后,Graphics的Update函数会被调用。
  2. 接着进入Root::renderOneFrame,首先会调用sceneManager::updateSceneGraph,场景图当中的对象会被一一调用。
  3. 然后调用Root::_updateAllRenderTargets,进入CompositorWorkspace::_update,而Workspace描述了渲染的逻辑,是一个树形结构,他会负责对所有的子节点进行调用
  4. 进入CompositorNode::_update调用其中的CompositorPass
  5. 最后一步调用CompositorPass::execute(虚函数),来执行需要的操作。
  6. 要注意的是execute是纯虚函数,不同的Pass会用不同的实现。比如PassScene实现场景的渲染,PassCompute则执行计算着色器。
  7. 完成所有Pass定义的操作后,调用mCompositorManager2::_swapAllFinalTargets,该函数最终会调用GPU API实现的SwapBuffer功能,即设置渲染的结果输出到屏幕上,以及提交所有命令缓存中的设置到GPU。

RenderTarget

渲染的结果被呈现到RenderTarget都子类当中,包括RenderWindow和RenderTexture,他们在不同平台下有不同的实现方式。通过调用Root::createRenderWindow来创建具体的渲染窗口,RenderTarget当中可以包含一系列的RenderView,每一个RenderView负责一定区域中的渲染。 因此,可以在PassScene的execute函数中看到如下的语句:

if( !mDefinition->mReuseCullData )
{
     mTarget->_updateViewportCullPhase01(mViewport, mCullCamera, usedLodCamera,
                                         mDefinition->mFirstRQ, mDefinition->mLastRQ );
}

executeResourceTransitions();

mTarget->setFsaaResolveDirty();
mTarget->_updateViewportRenderPhase02(mViewport, mCamera, usedLodCamera,
                                      mDefinition->mFirstRQ, mDefinition->mLastRQ, true );

其中最终要的是:

  1. mTarget->_updateViewportCullPhase01
  2. mTarget::_updateViewportRenderPhase02

进一步,RenderView会调用Camera实现渲染,因此在RenderView中可以看到:

void Viewport::_updateCullPhase01(Camera* camera, const Camera *lodCamera,
                                  uint8 firstRq, uint8 lastRq )
{
    // Automatic AR cameras are useful for cameras that draw into multiple viewports
    const Real aspectRatio = (Real) mActWidth / (Real) std::max( 1, mActHeight );
    if( camera->getAutoAspectRatio() && camera->getAspectRatio() != aspectRatio )
    {
        camera->setAspectRatio( aspectRatio );
    }
    // Tell Camera to render into me
    camera->_notifyViewport(this);

    camera->_cullScenePhase01( lodCamera, this, firstRq, lastRq );
}

void Viewport::_updateRenderPhase02(Camera* camera, const Camera *lodCamera,
                                    uint8 firstRq, uint8 lastRq )
{
    camera->_renderScenePhase02( lodCamera, this, firstRq, lastRq, mShowOverlays );
}

而Camera则会调用SceneManager当中类似名称的函数:

void Camera::_cullScenePhase01( const Camera *lodCamera, Viewport *vp, uint8 firstRq, uint8 lastRq )
{
    OgreProfileBeginGPUEvent("Camera: " + getName());

    //update the pixel display ratio
    if (mProjType == Ogre::PT_PERSPECTIVE)
    {
        mPixelDisplayRatio = (2 * Ogre::Math::Tan(mFOVy * 0.5f)) / vp->getActualHeight();
    }
    else
    {
        mPixelDisplayRatio = (mTop - mBottom) / vp->getActualHeight();
    }

    //notify prerender scene
    ListenerList listenersCopy = mListeners;
    for (ListenerList::iterator i = listenersCopy.begin(); i != listenersCopy.end(); ++i)
    {
        (*i)->cameraPreRenderScene(this);
    }

    //render scene
    mSceneMgr->_cullPhase01( this, lodCamera, vp, firstRq, lastRq );
}
//-----------------------------------------------------------------------
void Camera::_renderScenePhase02(const Camera *lodCamera, Viewport *vp,
                                 uint8 firstRq, uint8 lastRq, bool includeOverlays)
{
    //render scene
    mSceneMgr->_renderPhase02( this, lodCamera, vp, firstRq, lastRq, includeOverlays );

    // Listener list may have changed
    ListenerList listenersCopy = mListeners;

    //notify postrender scene
    for (ListenerList::iterator i = listenersCopy.begin(); i != listenersCopy.end(); ++i)
    {
        (*i)->cameraPostRenderScene(this);
    }
    OgreProfileEndGPUEvent("Camera: " + getName());
}

前者完成了场景图的视锥切割并且将可见物体添加到渲染队列中,后者将MovableObject和一系列参数放入RenderQueue当中等待渲染。

Ogre2底层API

RenderQueue

进入渲染队列的执行语句后,最重要的就是将Renderable转化到CommandBuffer,并且调用execute命令。

CommandBuffer本质上是一个字符数组,利用reinterpret将不同的命令都按照同样长的内存长度存储下来,需要用的时候再按照不同的类型,解释具体的内存区域中的数据。从CbType枚举类型中,可以看到他支持的渲染操作的种类,包括

  1. CB_INVALID,
  2. CB_SET_VAO,
  3. CB_SET_INDIRECT_BUFFER,
  4. CB_DRAW_CALL_INDEXED_EMULATED_NO_BASE_INSTANCE,
  5. CB_DRAW_CALL_INDEXED_EMULATED,
  6. CB_DRAW_CALL_INDEXED,
  7. CB_DRAW_CALL_STRIP_EMULATED_NO_BASE_INSTANCE,
  8. CB_DRAW_CALL_STRIP_EMULATED,
  9. CB_DRAW_CALL_STRIP,
  10. CB_SET_CONSTANT_BUFFER_VS,
  11. CB_SET_CONSTANT_BUFFER_PS,
  12. CB_SET_CONSTANT_BUFFER_GS,
  13. CB_SET_CONSTANT_BUFFER_HS,
  14. CB_SET_CONSTANT_BUFFER_DS,
  15. CB_SET_CONSTANT_BUFFER_CS,
  16. CB_SET_CONSTANT_BUFFER_INVALID,
  17. CB_SET_TEXTURE_BUFFER_VS,
  18. CB_SET_TEXTURE_BUFFER_PS,
  19. CB_SET_TEXTURE_BUFFER_GS,
  20. CB_SET_TEXTURE_BUFFER_HS,
  21. CB_SET_TEXTURE_BUFFER_DS,
  22. CB_SET_TEXTURE_BUFFER_CS,
  23. CB_SET_TEXTURE_BUFFER_INVALID,
  24. CB_SET_PSO,
  25. CB_SET_TEXTURE,
  26. CB_TEXTURE_DISABLE_FROM,
  27. CB_START_V1_LEGACY_RENDERING,
  28. CB_SET_V1_RENDER_OP,
  29. CB_DRAW_V1_INDEXED_NO_BASE_INSTANCE,
  30. CB_DRAW_V1_INDEXED,
  31. CB_DRAW_V1_STRIP_NO_BASE_INSTANCE,
  32. CB_DRAW_V1_STRIP,
  33. CB_LOW_LEVEL_MATERIAL,
  34. MAX_COMMAND_BUFFER

这些渲染操作会具体的调用RenderSystem中的命令,而RenderSystem针对不同的API有具体的实现

RenderSystem

渲染系统包含以下的具体实现

  1. MetalRenderSystem
  2. D3D11RenderSystem
  3. D3D9RenderSystem
  4. GL3PlusRenderSystem
  5. GLES2RenderSystem
  6. NULLRenderSystem

显示图像

正如上面介绍Ogre的逻辑模块时所说的,在所有的CommandBuffer当中的命令都转化为GPU的指令后,mCompositorManager2::_swapAllFinalTargets负责实现最终的渲染效果的呈现。这一步最关键的是两个步骤。

  1. 设置显示的texture地址
  2. 提交渲染命令

以下主要基于Cocoa和Metal的API进行介绍,其余类型的API有相似的原理。在程序一开始创建RenderWindow时,系统本质上调用了如下语句:

mMetalView = [[OgreMetalView alloc] initWithFrame:frame]

该语句创建一个NSView的子类,根据这一类型可以进一步获得CAMetalLayer类型的对象

mMetalLayer = (CAMetalLayer*)mMetalView.layer

该对象在系统调用例如_setViewport函数时会被使用,并获得每一帧的CAMetalDrawable对象,该对象即是窗口当中渲染结果的地址。该地址随即被保存在MTLRenderPassColorAttachmentDescriptor的对象当中。

mCurrentDrawable = [mMetalLayer nextDrawable]

mColourAttachmentDesc.texture = mCurrentDrawable.texture

MTLRenderPassColorAttachmentDescriptor是MTLRenderPassDescriptor的组成部分,因此地址被再次传入这一新的对象,并且该对象最终进入渲染命令队列中等待被执行:

passDesc.colorAttachments[i] = [mCurrentColourRTs[i]->mColourAttachmentDesc copy]

[mActiveDevice->mCurrentCommandBuffer renderCommandEncoderWithDescriptor:passDesc]

在每一轮渲染当中,都会调用nextDrawable从mMetalLayer获得新的渲染目标位置,并且渲染管线将结果到保存在该位置,最终调用一下命令将渲染结果呈现在屏幕上:

[mOwnerDevice->mCurrentCommandBuffer presentDrawable:mCurrentDrawable]

由于Metal的现代GPU API设计,所有的操作都进入CommandBuffer直到系统提交命令到GPU中才可以被渲染,即:

[mCurrentCommandBuffer commit]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值