`

场景的管理 :RenderQueue

 
阅读更多

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

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics