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排序了。