RenderQueue渲染队列是送给渲染系统渲染的最终结果集,本质上它与查询队列没什么不同,但由于它的内部做过多次分类排序,所以它查询队列复杂。
RenderQueue :
1.RenderQueueGroupID :即是key又是优先级别
enum RenderQueueGroupID { /// Use this queue for objects which must be rendered first e.g. backgrounds RENDER_QUEUE_BACKGROUND = 0, // 背景 RENDER_QUEUE_SKIES_EARLY = 5, // 背景之后的第一个队列,用于天空盒 RENDER_QUEUE_1 = 10, RENDER_QUEUE_2 = 20, RENDER_QUEUE_WORLD_GEOMETRY_1 = 25, RENDER_QUEUE_3 = 30, RENDER_QUEUE_4 = 40, RENDER_QUEUE_MAIN = 50, // 默认渲染队列 RENDER_QUEUE_6 = 60, RENDER_QUEUE_7 = 70, RENDER_QUEUE_WORLD_GEOMETRY_2 = 75, RENDER_QUEUE_8 = 80, RENDER_QUEUE_9 = 90, RENDER_QUEUE_SKIES_LATE = 95, // 前景(控件),之前的最后一个队列,用于天空盒 RENDER_QUEUE_OVERLAY = 100, // 这个是给2D界面上的控件用的,在最前面 RENDER_QUEUE_MAX = 105 // 这个应该是预留的,如果你想显示在控件的前面就必须得是它了 };
从小到大:这个是按照对象离屏幕距离来排列的,里面天空盒出现了两次。理论上来说,还需要根据材质进行分组,不过不应该是现在,而是在裁剪之后,因为裁剪之后就没有先后顺序了,也就没有什么遮挡不遮挡的了。
addRenderable :为渲染队列添加渲染对象
void RenderQueue::addRenderable(Renderable* pRend, uint8 groupID, ushort priority) { // Find group RenderQueueGroup* pGroup = getQueueGroup(groupID); Technique* pTech; // tell material it's been used if (!pRend->getMaterial().isNull()) pRend->getMaterial()->touch(); // Check material & technique supplied (the former since the default implementation // of getTechnique is based on it for backwards compatibility if(pRend->getMaterial().isNull() || !pRend->getTechnique()) { // Use default base white MaterialPtr baseWhite = MaterialManager::getSingleton().getByName("BaseWhite"); pTech = baseWhite->getTechnique(0); } else pTech = pRend->getTechnique(); if (mRenderableListener) { // Allow listener to override technique and to abort if (!mRenderableListener->renderableQueued(pRend, groupID, priority, &pTech, this)) return; // rejected // tell material it's been used (incase changed) pTech->getParent()->touch(); } pGroup->addRenderable(pRend, pTech, priority); }
getQueueGroup(groupID)如果不存储会创建,这样使其具有唯一性。pGroup->addRenderable(pRend, pTech, priority);表示这个方法其实除了对材质做了些判断什么都没做。
pGroup = OGRE_NEW RenderQueueGroup(this, mSplitPassesByLightingType, mSplitNoShadowPasses, mShadowCastersCannotBeReceivers); mGroups.insert(RenderQueueGroupMap::value_type(groupID, pGroup)); void RenderQueue::setSplitPassesByLightingType(bool split) { mSplitPassesByLightingType = split; RenderQueueGroupMap::iterator i, iend; i = mGroups.begin(); iend = mGroups.end(); for (; i != iend; ++i) { i->second->setSplitPassesByLightingType(split); } }
在RenderQueue里面有3个字段mSplitPassesByLightingType,mSplitNoShadowPasses和
mShadowCastersCannotBeReceivers他们其实是给RenderQueueGroup使用的,这里强调的是RenderQueue的所有RenderQueueGroup的这三个属性都是一样的,一旦改变都会改变。
RenderQueueGroup :
void addRenderable(Renderable* pRend, Technique* pTech, ushort priority) { // Check if priority group is there PriorityMap::iterator i = mPriorityGroups.find(priority); RenderPriorityGroup* pPriorityGrp; if (i == mPriorityGroups.end()) { // Missing, create pPriorityGrp = OGRE_NEW RenderPriorityGroup(this, mSplitPassesByLightingType, mSplitNoShadowPasses, mShadowCastersNotReceivers); if (mOrganisationMode) { pPriorityGrp->resetOrganisationModes(); pPriorityGrp->addOrganisationMode((QueuedRenderableCollection::OrganisationMode)mOrganisationMode); } mPriorityGroups.insert(PriorityMap::value_type(priority, pPriorityGrp)); } else { pPriorityGrp = i->second; } // Add pPriorityGrp->addRenderable(pRend, pTech); }
RenderQueueGroup 的作用与RenderQueue 没什么两样,它的存在让我想起一个词“诟病”,这是在秀自己的设计能力吗?只能说这个类的能力过分简单,它只做了一件事,就是在RenderQueue 的基础上再分类。
RenderPriorityGroup :
QueuedRenderableCollection mSolidsBasic; // 不启用阴影(即不投射也不接收阴影)或启用modulative阴影或启用additive阴影但处于环境光阶段。 QueuedRenderableCollection mSolidsDiffuseSpecular; // 启用additive阴影,且处于逐个光源上色阶段 QueuedRenderableCollection mSolidsDecal; // 启用additive阴影,且处于纹理映射阶段。 QueuedRenderableCollection mSolidsNoShadowReceive; // 开启阴影但不接收阴影(如自发光体)(阴影可投可不投) QueuedRenderableCollection mTransparentsUnsorted; //透明的被排序的 QueuedRenderableCollection mTransparents; // 透明的未被排序的
void RenderPriorityGroup::addRenderable(Renderable* rend, Technique* pTech) { // Transparent and depth/colour settings mean depth sorting is required? // Note: colour write disabled with depth check/write enabled means // setup depth buffer for other passes use. if (pTech->isTransparentSortingForced() || (pTech->isTransparent() && (!pTech->isDepthWriteEnabled() || !pTech->isDepthCheckEnabled() || pTech->hasColourWriteDisabled()))) { if (pTech->isTransparentSortingEnabled()) addTransparentRenderable(pTech, rend); else addUnsortedTransparentRenderable(pTech, rend); } else { if (mSplitNoShadowPasses && mParent->getShadowsEnabled() && ((!pTech->getParent()->getReceiveShadows() || rend->getCastsShadows()) && mShadowCastersNotReceivers)) { // Add solid renderable and add passes to no-shadow group addSolidRenderable(pTech, rend, true); } else { if (mSplitPassesByLightingType && mParent->getShadowsEnabled()) { addSolidRenderableSplitByLightType(pTech, rend); } else { addSolidRenderable(pTech, rend, false); } } } }
所有同一个RenderQueueGroup 的渲染对象,传递到RenderPriorityGroup的时候,会根据Technique分配给这几个队列。目前对这几个队列不是很了解,但是整体上来看它区分了阴影的类型和是否为透明。另外,前面一直在传递未被使用过的几个参数,被这个方法使用了。
QueuedRenderableCollection :相比前面几个类,这个类更实在。
enum OrganisationMode // 以何种方式分组和排序 { OM_PASS_GROUP = 1, // 根据pass分组 OM_SORT_DESCENDING = 2, // 升序排序 OM_SORT_ASCENDING = 6 // 降序排序 };
分组一般是在场景中的渲染对象比较多的时候使用,而排序是相对于相机的距离排序的,其实对于不透无阴影明体是不需要排序的。
PassGroupRenderableMap mGrouped; RenderablePassList mSortedDescending; static RadixSort<RenderablePassList, RenderablePass, uint32> msRadixSorter1; static RadixSort<RenderablePassList, RenderablePass, float> msRadixSorter2;
尽管这个类里面的字段比较多,但是重要的就这么几个,所有的渲染体分组都是放在mGrouped这个map中的,既然如此为什么还需要一个mSortedDescending呢?这个是用于存放不需要按照pass分类的渲染对象。
void QueuedRenderableCollection::addRenderable(Pass* pass, Renderable* rend) { // ascending and descending sort both set bit 1 if (mOrganisationMode & OM_SORT_DESCENDING) { mSortedDescending.push_back(RenderablePass(rend, pass)); } if (mOrganisationMode & OM_PASS_GROUP) { PassGroupRenderableMap::iterator i = mGrouped.find(pass); if (i == mGrouped.end()) { std::pair<PassGroupRenderableMap::iterator, bool> retPair; // Create new pass entry, build a new list // Note that this pass and list are never destroyed until the // engine shuts down, or a pass is destroyed or has it's hash // recalculated, although the lists will be cleared etPair = mGrouped.insert( passGroupRenderableMap::value_type( pass, OGRE_NEW_T(RenderableList, MEMCATEGORY_SCENE_CONTROL)() )); assert(retPair.second && "Error inserting new pass entry into PassGroupRenderableMap"); i = retPair.first; } // Insert renderable i->second->push_back(rend); } }
根据不同的mOrganisationMode类型,插入到不同的list里面。
void QueuedRenderableCollection::sort(const Camera* cam) { // ascending and descending sort both set bit 1 // We always sort descending, because the only difference is in the // acceptVisitor method, where we iterate in reverse in ascending mode if (mOrganisationMode & OM_SORT_DESCENDING) { // We can either use a stable_sort and the 'less' implementation, // or a 2-pass radix sort (once by pass, then by distance, since // radix sorting is inherently stable this will work) // We use stable_sort if the number of items is 512 or less, since // the complexity of the radix sort is approximately O(10N), since // each sort is O(5N) (1 pass histograms, 4 passes sort) // Since stable_sort has a worst-case performance of O(N(logN)^2) // the performance tipping point is from about 1500 items, but in // stable_sorts best-case scenario O(NlogN) it would be much higher. // Take a stab at 2000 items. if (mSortedDescending.size() > 2000) { // sort by pass msRadixSorter1.sort(mSortedDescending, RadixSortFunctorPass()); // sort by depth msRadixSorter2.sort(mSortedDescending, RadixSortFunctorDistance(cam)); } else { std::stable_sort( mSortedDescending.begin(), mSortedDescending.end(), DepthSortDescendingLess(cam)); } } // Nothing needs to be done for pass groups, they auto-organise }
这个排序也挺重要的,大于2000个渲染对象的时候,才排序,渲染对象不多的时候就没必要按照pass排序了。