一个基于dx的2D端游客户端程序猿的忧虑。
想从学习Ogre引擎中学习3D引擎渲染知识!学习模式设计!学习很多东西!
从零基础第一篇了解下Ogre的基本渲染流程(不对之处还请指出)
渲染的过程一般是:
1、 设置物体顶点 – Vertex更新
2、 设置空间变换 – Transform更新
3、 设置Camera和投影视口变换 – Viewport更新
4、 设置纹理数据 – Texture更新
5、 使用需要的Gpu Shader,开始渲染,管线执行,
重复上述步骤,绘制所有物体
最后,绘制完一帧提交渲染结果
启动
INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR cmdLine, INT)
{
OgreBites::SampleBrowser brows (nograb, startUpSampleIdx);
brows.go();
}
开始循环
class SampleContext : public ApplicationContext, public InputListener
{
virtual void go(Sample* initialSample = 0)
{
while (!mLastRun)
{
mLastRun = true; // assume this is our last run
initApp();
loadStartUpSample();
if (mRoot->getRenderSystem() != NULL)
{
mRoot->startRendering(); // start the render loop
}
closeApp();
mFirstRun = false;
}
}
};
开始更新
void Root::startRendering(void)
{
OgreAssert(mActiveRenderer != 0, "no RenderSystem");
mActiveRenderer->_initRenderTargets();
// Clear event times
clearEventTimes();
// Infinite loop, until broken out of by frame listeners
// or break out by calling queueEndRendering()
mQueuedEnd = false;
while( !mQueuedEnd )
{
if (!renderOneFrame())
break;
}
}
一帧的更新
renderOneFrame有两个fire函数,给用户以渲染前后的一些回调,如_fireFrameStarted就会对所以的frameListener进行处理
bool Root::renderOneFrame(void)
{
if(!_fireFrameStarted())
return false;
if (!_updateAllRenderTargets())
return false;
return _fireFrameEnded();
}
// Update all in order of priority
// This ensures render-to-texture targets get updated before render windows
// 根据优先级更新所有内容,确保在渲染到窗口之前更新渲染到纹理的对象
// 渲染物体到RenderBuffer时,一般会用到之前渲染好的RenderTexture,RenderTexture形式的RenderTarget需要在RenderBuffer之前进行更新。
常规的渲染操作都是直接将场景呈现到backbuffer中的,backbuffer说白了其实就是一个表面,再说白了就是一块内存,场景通过绘制函数载入显存后,再通过Present函数送至显示器。那么为什么
还要渲染到纹理呢?这是为了实现一些特殊的效果,比如常见的环境映射,简单的说,想象你有一个光滑的球体,它应该是可以反射周围环境的,这就是环境映射。
上面说了常规的渲染操作是将场景送至backbuffer,而backbuffer实际上是一个Surface,而纹理恰恰又包含了Surface,所以我们只需要取得纹理的Surface,其次将场景送至这个Surface,最后再把
这个纹理渲染到backbuffer中即可。举个例子,假设你要在一面墙壁上画一幅画,你有两种方法
1 直接在墙上画,这个很好理解,就对应常规的backbuffer渲染。
2 先将画绘制在纸上,然后将纸贴到墙上,这就对应渲染到纹理的过程。
这里墙壁相当于backbuffer,而纸张相当于纹理的Surface,在纸上作画相当于渲染到纹理,把纸贴到墙上相当于把纹理渲染到backbuffer。具体的步骤如下
1 创建纹理并获得纹理的表面(Surface)
2 向纹理的表面渲染场景
3 渲染纹理本身
bool Root::_updateAllRenderTargets(void)
{
...
mActiveRenderer->_updateAllRenderTargets(false);
...
mActiveRenderer->_swapAllRenderTargetBuffers();
...
}
更新全部渲染对象
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);
}
}
void RenderTarget::update(bool swap)
{
...
updateImpl();
...
}
void RenderTarget::updateImpl(void)
{
...
_updateAutoUpdatedViewports(true);
...
}
更新自动更新的视口
Viewport其实就是定义了某RenderTarget上的一块要进行更新的区域,所以一个RenderTarget是可以包含多个Viewport。多个viewport就可以在画面中开多个窗口。
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;
}
}
更新视口
在Ogre中,Viewport看成是保存Camera和RenderTarget这两者的组合;渲染时把Viewport中Camera拍摄到的东西渲染到RenderTarget上。Viewport有一个ZOrder的属性,ZOrder越小的,越先被渲染。如果两个Viewport区域重合,Zorder大的最后会覆盖掉Zorder小的内容。
void RenderTarget::_updateViewport(Viewport* viewport, bool updateStatistics)
{
...
viewport->update();
...
}
void Viewport::update(void)
{
...
mCamera->_renderScene(this, mShowOverlays);
...
}
场景渲染流程
void Camera::_renderScene(Viewport *vp, bool includeOverlays)
{
...
//render scene
mSceneMgr->_renderScene(this, vp, includeOverlays);
...
}
真正的场景渲染流程开始
利用所指定的Camera和Viewport,来把场景中的内容渲染到Viewport所指定的RenderTarget的某块区域中。
根据Camera,可以获取计算出View Matrix与Projection Matrix,还可以进行视锥体的剔除与裁剪,另外,可以只渲染Camera可见的物体。
_renderScene的任务比较多,一些特效的计算更新也放入其中
void SceneManager::_renderScene(Camera* camera, Viewport* vp, bool includeOverlays)
{
...
_updateSceneGraph(camera);
...
setViewport(vp);
...
OgreProfileGroup("prepareRenderQueue", OGREPROF_GENERAL);
prepareRenderQueue();
...
_findVisibleObjects(camera, &(camVisObjIt->second),
...
_renderVisibleObjects();
...
}
第一个重要函数是_updateSceneGraph( 场景中的每个节点都经过了更新,包括位置,缩放,旋转,还有节点的包围盒AABB)
Scene的更新从根Node开始。在Ogre中,Scene图是用Node节点来进行组织的,Node之间有父子关系,有一个根节点。所有的物体都需要挂接在某个Node上,任何一个Node可以进行位置,缩放,旋转的空间变换;并且会作用到挂接在这些节点上的具体的物体上,也就是说,Node保存了全局的World Transform。对于任何一个物体来说,操作Node的空间变换,物体也进行响应的空间变换。
Node节点还保存了AABB(包装盒),这个包装盒是一个包含Node上的所有物体的一个立体空间,它的主要是用于视锥体裁减的,如果Camera看不见某个节点的AABB,那么说明Camera就看不见Node上所挂接的所有物体,在渲染时可以忽略掉这个Node。
void SceneManager::_updateSceneGraph(Camera* cam)
{
...
getRootSceneNode()->_update(true, false);
...
}
接下来重要的是setViewport,设置Viewport中所包含的RenderTarget为当前所要渲染的目标,而Viewport中的区域为当前所要渲染的目标区域。
void SceneManager::setViewport(Viewport* vp)
{
...
}
接下来一个重要的概念RenderQueue。可以简单把它想成是一个容器,里面的元素就是Renderable,每个Renderable可以看成是每次绘制时需要渲染的物体,可以是一个模型,也可以是模型的一部分。在RenderQueue中,它会按材质Material来分组这些Renderable,还会对Renderable进行一定规则的排序。
RenderQueue 渲染队列,确保正确的渲染顺序和提高渲染效率,将相同的Pass的物体一起进行渲染,尽可能降低了渲染状态的切换。
RenderQueue
RenderQueueGroup(RenderQueueGroupMap) 根据RenderQueueGroupID分组,代表Objects的渲染顺序,主要包括天空盒、地形、界面等
RenderPriorityGroup(PriorityMap) 根据UShort的值分组,代表渲染的优先级,不指定id会加到默认的组,值为100
QueueRenderableCollection 存储Renderable和Pass的最终场所,通过多种排序实现Renderable 和Pass的有序化,排序包括小于排序,深度递减排序和基数排序
RenderableList(SolidRenderablePassMap) 根据PassGroupLess的值分组,组织Renderable和Pass,有三种,分别是按Pass分组,按与Camera的距离升序和按与Camera的距离降序
Renderable
在每一次调用SceneManager::_renderScene时,都会调用SceneManager::prepareRenderQueue来清理RenderQueue
void SceneManager::prepareRenderQueue(void)
{
...
}
然后再调用SceneManager::_findVisibleObjects来把当前摄像机所能看见的物体都加入到RenderQueue中。
void SceneManager::_findVisibleObjects(
Camera* cam, VisibleObjectsBoundsInfo* visibleBounds, bool onlyShadowCasters)
{
...
}
环境准备好了开始渲染
void SceneManager::_renderVisibleObjects(void)
{
...
renderVisibleObjectsDefaultSequence();
...
}
RenderQueue、RenderQueueGroup、RenderPriorityGroup都是管理器,正在的容器是QueueRenderableCollection
void SceneManager::renderVisibleObjectsDefaultSequence(void)
{
...
_renderQueueGroupObjects(pGroup, QueuedRenderableCollection::OM_PASS_GROUP);
...
}
void SceneManager::_renderQueueGroupObjects(RenderQueueGroup* pGroup,
QueuedRenderableCollection::OrganisationMode om)
{
...
renderBasicQueueGroupObjects(pGroup, om);
}
void SceneManager::renderBasicQueueGroupObjects(RenderQueueGroup* pGroup,
QueuedRenderableCollection::OrganisationMode om)
{
...
renderObjects(pPriorityGrp->getSolidsBasic(), om, true, true);
// Do unsorted transparents
renderObjects(pPriorityGrp->getTransparentsUnsorted(), om, true, true);
// Do transparents (always descending)
renderObjects(pPriorityGrp->getTransparents(),
...
}
对每个Renderable进行渲染
void SceneManager::renderObjects(const QueuedRenderableCollection& objs,
QueuedRenderableCollection::OrganisationMode om,
bool lightScissoringClipping,
bool doLightIteration,
const LightList* manualLightList,
bool transparentShadowCastersMode)
{
...
objs.acceptVisitor(mActiveQueuedRenderableVisitor, om);
...
}
根据不同的组织规则进行渲染
void QueuedRenderableCollection::acceptVisitor(
QueuedRenderableVisitor* visitor, OrganisationMode om) const
{
...
switch(om)
{
case OM_PASS_GROUP:
acceptVisitorGrouped(visitor);
break;
case OM_SORT_DESCENDING:
acceptVisitorDescending(visitor);
break;
case OM_SORT_ASCENDING:
acceptVisitorAscending(visitor);
break;
}
}
//-----------------------------------------------------------------------
void QueuedRenderableCollection::acceptVisitorGrouped(
QueuedRenderableVisitor* visitor) const
{
...
visitor->visit(ipass->first, const_cast<RenderableList&>(ipass->second));
...
}
//-----------------------------------------------------------------------
void QueuedRenderableCollection::acceptVisitorDescending(
QueuedRenderableVisitor* visitor) const
{
...
visitor->visit(const_cast<RenderablePass*>(&(*i)));
...
}
//-----------------------------------------------------------------------
void QueuedRenderableCollection::acceptVisitorAscending(
QueuedRenderableVisitor* visitor) const
{
...
visitor->visit(const_cast<RenderablePass*>(&(*i)));
...
}
最终在SceneManager::renderSingleObject中取出每个Renderable所保存的顶点、世界矩阵等信息来进行渲染
void SceneManager::SceneMgrQueuedRenderableVisitor::visit(RenderablePass* rp)
{
...
targetSceneMgr->renderSingleObject(rp->renderable, mUsedPass, scissoring,
autoLights, manualLightList);
...
}
void SceneManager::renderSingleObject(Renderable* rend, const Pass* pass,
bool lightScissoringClipping, bool doLightIteration,
const LightList* manualLightList)
{
...
// Reset view / projection changes if any
resetViewProjMode(passTransformState);
...
}
void SceneManager::issueRenderWithLights(Renderable* rend, const Pass* pass,
const LightList* pLightListToUse, bool fixedFunction,
bool lightScissoringClipping)
{
...
_issueRenderOp(rend, pass);
...
}
选择当前的渲染系统进行渲染(dx9 dx11 OpenGL)
void SceneManager::_issueRenderOp(Renderable* rend, const Pass* pass)
{
...
mDestRenderSystem->_render(ro);
...
}
void RenderSystem::_render(const RenderOperation& op)
{
...
}