ogre的监听模式在渲染流程中得到大量的应用。
自己控制渲染时候可以在mfc的ontimer或者while语句中,直接调用root的renderOneFrame函数。
bool Root::renderOneFrame(void)
{
if(!_fireFrameStarted())//开始
return false;
if (!_updateAllRenderTargets())//更新
return false;
return _fireFrameEnded();//结束
}
看代码的三句话,按表面意思就是:帧开始->更新渲染物状态->帧结束。
渲染之帧开始
bool Root::_fireFrameStarted()
{
FrameEvent evt;
populateFrameEvent(FETT_STARTED, evt);//我们没有设置FrameEvent,先设置FrameEvent,得到距上一frame的时间。
return _fireFrameStarted(evt);
}
跟进
bool Root::_fireFrameStarted(FrameEvent& evt)
{
OgreProfileBeginGroup("Frame", OGREPROF_GENERAL);
// Remove all marked listeners,从监听者中移除标记为移除的。移除时候是先将listener标记为移除的,在frame开始时候真的移除的
set<FrameListener*>::type::iterator i;
for (i = mRemovedFrameListeners.begin();
i != mRemovedFrameListeners.end(); i++)
{
mFrameListeners.erase(*i);
}
mRemovedFrameListeners.clear();
// Tell all listeners
for (i= mFrameListeners.begin(); i != mFrameListeners.end(); ++i)
{
if (!(*i)->frameStarted(evt))
return false;
}
return true;
}
先看下mFrameListeners都有啥东西吧。
根据先后顺序
(1)FrameTimeControllerValue(渲染帧时间控制器),在root初始化时由mControllerManager的创建而调用。
(2)自己程序通过Root::addFrameListener添加的监听者。一般自己控制动画时都需要继承自监听者。
第一个是必须的,用以计算最新frame time
所有,帧开始办了三件事(1)真的移除掉标记为移除的帧监听者(2)通知了渲染帧时间控制器计算frame time,(3)调用了其他监听者的帧开始
渲染之更新渲染物
_updateAllRenderTargets
bool Root::_updateAllRenderTargets(void)
{
// update all targets but don't swap buffers,更新但不替换buffer
mActiveRenderer->_updateAllRenderTargets(false);
// 用户用的帧刷新
bool ret = _fireFrameRenderingQueued();//触发所有的frameRenderingQueued
// block for final swap
mActiveRenderer->_swapAllRenderTargetBuffers(mActiveRenderer->getWaitForVerticalBlank());
// This belongs here, as all render targets must be updated before events are
// triggered, otherwise targets could be mismatched. This could produce artifacts,
// for instance, with shadows.
for (SceneManagerEnumerator::SceneManagerIterator it = getSceneManagerIterator(); it.hasMoreElements(); it.moveNext())
it.peekNextValue()->_handleLodEvents();
return ret;
}
跟进_updateAllRenderTargets
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);//更新
}
mPrioritisedRenderTargets变量为renderwindow、movableObject等渲染物的优先级及渲染物指针的map。必有的就是renderwindow
RenderTarget::update->updateImpl
_beginUpdate();//告知监听者要开始更新了,同时设置状态
_updateAutoUpdatedViewports(true);//更新自动更新视口
_endUpdate();//结束更新,设置状态
_updateAutoUpdatedViewports跟进
void RenderTarget::_updateAutoUpdatedViewports(bool updateStatistics)
{
// Go through viewports in Z-order,根据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;
}
}
跟进_updateViewport
void RenderTarget::_updateViewport(Viewport* viewport, bool updateStatistics)
{
assert(viewport->getTarget() == this &&
"RenderTarget::_updateViewport the requested viewport is "
"not bound to the rendertarget!");
fireViewportPreUpdate(viewport);//告诉监听者准备更新视口了
viewport->update();//视口更新
if(updateStatistics)//更新状态
{
mStats.triangleCount += viewport->_getNumRenderedFaces();
mStats.batchCount += viewport->_getNumRenderedBatches();
}
fireViewportPostUpdate(viewport);
}
viewport->update()调用视口绑定的相机来渲染场景mCamera->_renderScene(this, mShowOverlays)
跟进
void Camera::_renderScene(Viewport *vp, bool includeOverlays)
{
OgreProfileBeginGPUEvent("Camera: " + getName());//调用渲染系统的beginProfileEvent,触发下渲染系统的开始前的准备事件
//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->_renderScene(this, vp, includeOverlays);//场景管理器去渲染场景
// Listener list may have change
listenersCopy = mListeners;
//notify postrender scene
for (ListenerList::iterator i = listenersCopy.begin(); i != listenersCopy.end(); ++i)
{
(*i)->cameraPostRenderScene(this);
}
OgreProfileEndGPUEvent("Camera: " + getName());
}
mSceneMgr->_renderScene继续跟进
OgreProfileGroup("_renderScene", OGREPROF_GENERAL);
Root::getSingleton()._pushCurrentSceneManager(this);
mActiveQueuedRenderableVisitor->targetSceneMgr = this;//设置队列访问者
mAutoParamDataSource->setCurrentSceneManager(this);//设置当前场景管理器,为多管理器使用
// Also set the internal viewport pointer at this point, for calls that need it
// However don't call setViewport just yet (see below)
mCurrentViewport = vp;
// reset light hash so even if light list is the same, we refresh the content every frame
LightList emptyLightList;
useLights(emptyLightList, 0);
if (isShadowTechniqueInUse())
{
// Prepare shadow materials
initShadowVolumeMaterials();
}
// Perform a quick pre-check to see whether we should override far distance
// When using stencil volumes we have to use infinite far distance
// to prevent dark caps getting clipped
if (isShadowTechniqueStencilBased() && //stencil的阴影策略
camera->getProjectionType() == PT_PERSPECTIVE &&
camera->getFarClipDistance() != 0 &&
mDestRenderSystem->getCapabilities()->hasCapability(RSC_INFINITE_FAR_PLANE) &&
mShadowUseInfiniteFarPlane)
{
// infinite far distance,设置无限远的远切面
camera->setFarClipDistance(0);
}
mCameraInProgress = camera;
// Update controllers
ControllerManager::getSingleton().updateAllControllers();
// Update the scene, only do this once per frame
unsigned long thisFrameNumber = Root::getSingleton().getNextFrameNumber();
if (thisFrameNumber != mLastFrameNumber)
{
// Update animations,更新动画
_applySceneAnimations();
updateDirtyInstanceManagers();
mLastFrameNumber = thisFrameNumber;
}
{
// Lock scene graph mutex, no more changes until we're ready to render,解释的很清楚,锁住场景画面
OGRE_LOCK_MUTEX(sceneGraphMutex)
/灯光下的阴影材质设置
if (mIlluminationStage != IRS_RENDER_TO_TEXTURE && mFindVisibleObjects)
{
// Locate any lights which could be affecting the frustum,在视锥内有用的灯
findLightsAffectingFrustum(camera);
// Are we using any shadows at all?
if (isShadowTechniqueInUse() && vp->getShadowsEnabled())
{
// Prepare shadow textures if texture shadow based shadowing
// technique in use
if (isShadowTechniqueTextureBased())
{
OgreProfileGroup("prepareShadowTextures", OGREPROF_GENERAL);
// *******
// WARNING
// *******
// This call will result in re-entrant calls to this method
// therefore anything which comes before this is NOT
// guaranteed persistent. Make sure that anything which
// MUST be specific to this camera / target is done
// AFTER THIS POINT
prepareShadowTextures(camera, vp);
// reset the cameras & viewport because of the re-entrant call
mCameraInProgress = camera;
mCurrentViewport = vp;
}
}
}
// Update scene graph for this camera (can happen multiple times per frame),为本相机更新画面,一帧可能走好几次(一般都是好几次,次数决定于rendertarget的数量)
{
OgreProfileGroup("_updateSceneGraph", OGREPROF_GENERAL);
_updateSceneGraph(camera);//真正的刷新场景画面的地方,node更新,包围盒更新
// Auto-track nodes,相机跟随node的设置
AutoTrackingSceneNodes::iterator atsni, atsniend;
atsniend = mAutoTrackingSceneNodes.end();
for (atsni = mAutoTrackingSceneNodes.begin(); atsni != atsniend; ++atsni)
{
(*atsni)->_autoTrack();
}
// Auto-track camera if required,如果设置了相机的自动跟踪某个节点,在这儿处理
camera->_autoTrack();
}
// Invert vertex winding?,用到这是、反射效果时需要设置
if (camera->isReflected())
{
mDestRenderSystem->setInvertVertexWinding(true);
}
else
{
mDestRenderSystem->setInvertVertexWinding(false);
}
//下面的几项设置gpu编程的 默认参数的值。
// Tell params about viewport,视口
mAutoParamDataSource->setCurrentViewport(vp);
// Set the viewport - this is deliberately after the shadow texture update
setViewport(vp);
// Tell params about camera,相机
mAutoParamDataSource->setCurrentCamera(camera, mCameraRelativeRendering);
// Set autoparams for finite dir light extrusion,灯
mAutoParamDataSource->setShadowDirLightExtrusionDistance(mShadowDirLightExtrudeDist);
// Tell params about current ambient light,环境光
mAutoParamDataSource->setAmbientLightColour(mAmbientLight);
// Tell rendersystem
mDestRenderSystem->setAmbientLight(mAmbientLight.r, mAmbientLight.g, mAmbientLight.b);
// Tell params about render target
mAutoParamDataSource->setCurrentRenderTarget(vp->getTarget());
// Set camera window clipping planes (if any),设置相机剪切平面
if (mDestRenderSystem->getCapabilities()->hasCapability(RSC_USER_CLIP_PLANES))
{
mDestRenderSystem->resetClipPlanes();
if (camera->isWindowSet())
{
mDestRenderSystem->setClipPlanes(camera->getWindowPlanes());
}
}
// Prepare render queue for receiving new objects,准备渲染队列,以接受新的物体
{
OgreProfileGroup("prepareRenderQueue", OGREPROF_GENERAL);
prepareRenderQueue();//设置有阴影的物体,设置无阴影的材质,把工作队列分组
}
if (mFindVisibleObjects)
{
OgreProfileGroup("_findVisibleObjects", OGREPROF_CULLING);
// Assemble an AAB on the fly which contains the scene elements visible
// by the camera.
CamVisibleObjectsMap::iterator camVisObjIt = mCamVisibleObjectsMap.find( camera );
assert (camVisObjIt != mCamVisibleObjectsMap.end() &&
"Should never fail to find a visible object bound for a camera, "
"did you override SceneManager::createCamera or something?");
// reset the bounds
camVisObjIt->second.reset();
// Parse the scene and tag visibles,找到相机中可见的 object(在设置物体visibleflag时候设置的是否可见)
firePreFindVisibleObjects(vp);
_findVisibleObjects(camera, &(camVisObjIt->second),
mIlluminationStage == IRS_RENDER_TO_TEXTURE? true : false);
firePostFindVisibleObjects(vp);
mAutoParamDataSource->setMainCamBoundsInfo(&(camVisObjIt->second));
}
// Add overlays, if viewport deems it,overlay的更新
if (vp->getOverlaysEnabled() && mIlluminationStage != IRS_RENDER_TO_TEXTURE)
{
OverlayManager::getSingleton()._queueOverlaysForRendering(camera, getRenderQueue(), vp);
}
// Queue skies, if viewport seems it,天空的更新
if (vp->getSkiesEnabled() && mFindVisibleObjects && mIlluminationStage != IRS_RENDER_TO_TEXTURE)
{
_queueSkiesForRendering(camera);
}
} // end lock on scene graph mutex
//渲染系统顶点数清零
mDestRenderSystem->_beginGeometryCount();
// Clear the viewport if required,视口清理
if (mCurrentViewport->getClearEveryFrame())
{
mDestRenderSystem->clearFrameBuffer(
mCurrentViewport->getClearBuffers(),
mCurrentViewport->getBackgroundColour(),
mCurrentViewport->getDepthClear() );//清理
}
// Begin the frame,激活视剪切
mDestRenderSystem->_beginFrame();
// Set rasterisation mode,光栅化模式(点、网格、体)
mDestRenderSystem->_setPolygonMode(camera->getPolygonMode());
// Set initial camera state,投影矩阵
mDestRenderSystem->_setProjectionMatrix(mCameraInProgress->getProjectionMatrixRS());
mCachedViewMatrix = mCameraInProgress->getViewMatrix(true);
//更新相机位置,若相机设置为相对坐标的话
if (mCameraRelativeRendering)
{
mCachedViewMatrix.setTrans(Vector3::ZERO);
mCameraRelativePosition = mCameraInProgress->getDerivedPosition();
}
mDestRenderSystem->_setTextureProjectionRelativeTo(mCameraRelativeRendering, camera->getDerivedPosition());
//设置投影矩阵
setViewMatrix(mCachedViewMatrix);
// Render scene content
{
OgreProfileGroup("_renderVisibleObjects", OGREPROF_RENDERING);
_renderVisibleObjects();//内部调用_renderQueueGroupObjects,不同阴影策略不同的渲染方法,再底层调用renderSingleObject(其中进行shader程序的运行,设置渲染系统的世界矩阵,gpu选择材质pass)->RenderSystem::_render->bindVertexElementToGpu,(glVertexPointer、glTexCoordPointer设置顶点和文字坐标)更新顶点位置、uv等,更新深度偏移->底层调用glDrawElements(若用gl),调用glClientActiveTextureARB,
}
// End frame
mDestRenderSystem->_endFrame();
// Notify camera of vis faces
camera->_notifyRenderedFaces(mDestRenderSystem->_getFaceCount());
// Notify camera of vis batches
camera->_notifyRenderedBatches(mDestRenderSystem->_getBatchCount());
Root::getSingleton()._popCurrentSceneManager(this);
SceneManager::_updateSceneGraph跟进
firePreUpdateSceneGraph(cam);//告知监听者
Node::processQueuedUpdates();
getRootSceneNode()->_update(true, false);
firePostUpdateSceneGraph(cam);
渲染物的更新ManualObject::_updateRenderQueue
SceneManager::_renderQueueGroupObjects
SceneManager::renderSingleObject
SceneManager::updateGpuProgramParameters
GLRenderSystem::_render
位置的计算->node->renderable
核心代码的流程:
Root::renderOneFrame->
1、计算evt的两个时间(timeSinceLastEvent、timeSinceLastFrame),用作后面的更新
2、移除掉标记为已删除的监听者
3、告知所有监听者帧开始
4、调用_updateAllRenderTargets的更新所有渲染物(所谓渲染物即渲染对象,可以理解为渲染到哪里)
5、结束
Root::_updateAllRenderTargets->
1、调用RenderSystem::_updateAllRenderTargets更新所有的渲染物(RenderTexture、RenderWindow),更新node、camera、animation、顶点材质等
2、调用用户的帧渲染函数_fireFrameRenderingQueued ,并告知所有监听者
3、替换所有渲染buffer
RenderSystem::_updateAllRenderTargets->
调用RenderTarget::update,更新所有优先级渲染物(如果该渲染物是被激活的并且是自动更新的)
RenderTarget::update->
调用updateImpl更新渲染物
RenderTarget::updateImpl->
1、更新开始,告知监听器,对渲染物状态的两个参数(triangleCount、batchCount)清零
2、调用_updateAutoUpdatedViewports更新所有自动更新的视口
3、结束更新
RenderTarget::_updateAutoUpdatedViewports->
调用所有视口的_updateViewport
RenderTarget::_updateViewport->
1、告知视口时间监听者,视口更新准备函数
2、调用Camera::_renderScene
Camera::_renderScene->
1、根据相机类型(投影、正交)设置像素比例,//直接关系到渲染出来的结果是否真实
2、告知相机事件监听者准备渲染
3、调用SceneManager::_renderScene渲染场景(场景中包含的灯光、阴影、顶点、材质、动画等)
SceneManager::_renderScene->
1、设置当前队列的访问者,设置gpu编程参数场景管理器,设置当前视口,设置当前相机
2、阴影效果用到的材质初始化(只初始化一次)
3、根据情况设置相机远切面是否无限远
4、更新场景中的ogre通用控制器(材质中纹理动画管理器、粒子系统动画管理等)
5、更新帧数,更新动画
6、设置根据场景的视锥中灯的情况,设置阴影材质,可能又会回调到renderscene,因为阴影纹理也是rendertarget
7、更新场景画面,包含节点(node)、包围盒,并在之前和之后告知监听者;同时若相机是有设置了跟踪某个节点,在此时设置相机方位。
8、相机设置了反射、折射后的对渲染系统的特殊设置(gpu编程时候使用)
9、设置gpu编程参数(视口、相机、相机相关、产生阴影的方向灯光照射距离、环境光颜色、渲染物)
10、渲染系统剪切平面设置
11、准备渲染队列
12、遍历,查询到所有该相机视锥内的可见对象(通过八叉树的场景管理方式查询所有时候比较快),包括overlay、天空盒
13、渲染系统在更新前的相关设置:批数目清零、帧缓存清理、帧开始测试、三角模式(网格、顶点或模型)、投影矩阵设置、纹理投影设置、视口矩阵设置
14、调用_renderVisibleObjects,渲染所有可见物体
15、结束一帧
16、告知相机渲染面信息、批信息。弹出当前场景管理器
_renderVisibleObjects->
调用用户自己设置频率的渲染函数或者调用默认的渲染函数renderVisibleObjectsDefaultSequence
renderVisibleObjectsDefaultSequence->
1、告知监听者队列渲染准备
2、告知开始渲染队列
3、调用_renderQueueGroupObjects,渲染队列组中的物体
4、告知队列渲染结束
SceneManager::_renderQueueGroupObjects->
根据不同的阴影策略,调用不同的函数,下面的进入默认情况下 的函数
SceneManager::renderBasicQueueGroupObjects->
1、渲染优先级队列排序
2、调用renderObjects
SceneManager::renderObjects->
1、设置队列访问者
2、调用acceptVisitor
QueuedRenderableCollection::acceptVisitor->
根据组织模式不同,调用不同函数。
以pass分组的进入acceptVisitorGrouped;以相机距离递减的访问acceptVisitorDescending;以相机距离递增的访问acceptVisitorAscending
acceptVisitorGrouped->
调用visit,对所有pass分组下,所有renderable进行访问
SceneMgrQueuedRenderableVisitor::visit->
1、判断是否有效
2、调用renderSingleObject,渲染单个儿object
SceneManager::renderSingleObject->
1、获取renderable,获取pass中 的gpu程序对象
2、设置渲染系统的世界矩阵
3、设置三角模式(pass的)
4、灯列表
5、告知监听者要渲染单个物体
6、渲染系统设置深度偏移
7、更新gpu程序参数,最后一次绑定ogre中的参数与gpu程序的参数
8、调用渲染系统_render渲染
8、重置view的投影模式
GLRenderSystem::_render
1、调用基类_render
2、获取顶点声明信息等相关顶点信息
3、调用bindVertexElementToGpu,将顶点信息同gpu绑定(vertexprogram的顶点、uv信息输入),底层调用的glVertexPointer、glNormalPointer、glColorPointer、glTexCoordPointer等更新顶点及uv信息。
4、顶点索引buffer获取
5、根据顶点信息、索引信息,调用glDrawElements渲染出画面。
6、解绑必要的属性