一种简单的材质排序方法

        在实时渲染中,渲染不同物体时进行的材质切换是影响效率的主要因素之一。为了减少材质切换带来的负面影响,对需要渲染的物体进行材质排序是优化的主要思路。通过将材质进行排序,可以尽可能的减少材质切换的次数和每次切换带来的效率损耗。本文通过引入一种简单的数据结构来完成这个功能。该方案目前仅限于设计和功能实现阶段,没有进行实际的测试和优化。材质排序主要需要考虑两个方面,1-尽可能少的切换次数,2-尽可能低的效率影响。在实际操作中,这两个要求其实是相互制约的。举个例子:假设在整个渲染周期最复杂的材质包括以下3部分:纹理贴图,光照,Alpha混合。需要渲染的物体为4个:A,B,C,D。它们的材质分布如图:

 

       简单的统计一下可以看到:纹理有3种,灯火有3种,Alpha有2种。如果按照最小切换次数的思想,应该先优化Alpha的切换,然后是纹理和灯光。但在实际操作中,切换纹理的消耗要远高于Alpha的切换,因为应该先考虑纹理的优化,然后是灯光,最后才是Alpha。

       这里就引入以优先级的概念,即在材质中,每种属性都有其优先级。优先级高的属性需要尽可能的减少切换次数。优先级的设定是用户制定的,可能会随开发平台或不同的渲染底层有相应的改变。本文使用 纹理 - 灯光 - alpha 从高到低的顺序。

       对材质进行排序必然需要一种能保存排序结果的数据结构。通过以上分析,可以推导出该数据结构至少需要满足以下条件:

1. 无歧义的保存排序结果

2. 动态的添加和删除

3. 能遍历该结构,每次返还的结果与上次返还的结果需要进行的状态切换最优化。

4. 能根据用户指定的优先级进行排序

同时我们再添加一个要求,在常数时间完成遍历和删除功能。

 

 

下图是一种满足以上条件的数据结构,由于它是按元素的差异值作为排序的关键操作的,因此可以称它为Diff-Tree。

 

Diff-Tree中的每个节点都有4个指针。分别为:NextPtr,PrevPtr,FirstChildPtr,ParentPtr。

NextPtr指向其右邻居,PrevPtr指向左邻居,FirstChildPtr指向第一个子节点。ParentPtr指向父节点。其中ParentPtr比较特殊,只有在一个节点本身是另一个节点的第一个子节点时,该指针才有效。即上图中阴影显示的节点。

       Node提供Diff操作,该操作返回两个Node的差异值。差异值的计算如下:按材质属性的优先级顺序从高到低比较,如果该属性不相同,返还优先级。

         

   enum ShaderFeature

              
{

                     SF_PAD 
= 0,

                     SF_BASETEXTURE 
= 1,

                     SF_LIGHT     
= 2,

                     SF_ALPHAFUNC 
= 3,

                     SF_MAX_COUNT

              }
;
int SG_Shader::operator - (const SG_Shader& rkShader)

{

        
for(int i = SF_PAD + 1; i < SF_MAX_COUNT; i++)

        
{

              ShaderFeature eFeature 
= (ShaderFeature)i;

              
if(GetShaderFeature(eFeature) != rkShader.GetShaderFeature(eFeature))

                     
return i;

        }


        
return SF_MAX_COUNT; //equal

}



 

如上面的代码片断,如果两个材质的纹理不同,Diff操作返回1,如果纹理相同,灯光不同,返回2,如果只有Alpha不同,返回3

[ x, x, x]的格式来表示一个材质,第1x是纹理编号,第2x是灯光编号,第3个是Alpha混合类型编号。在添加的多个节点后,Diff Tree能成为一种深度优先的结构:


 

DiffTree保证在按深度遍历时,每次返还的结果与上次返还的结果需要进行的状态切换最优化。如上图,首先保证了纹理切换的次数最少(先遍历完所有纹理为1的节点)。

DiffTree的插入排序算法如下:

    1.如果树为空,root = insert

  2check = rootdepth = 1

 3.遍历check和它所有同级节点,返回与插入节点差异度最大的节点 beinsert.

  4.如果insertbeinsert相同,插入结束。

  5.如果insertbeinsert差异度小于等于depthinsert被插入为beinsert的邻居节点,返回。

6.如果insertbeinsert差异度大于depth,并且beinsert没有子节点,insertbeinsert的子节点。

7check = beinsert. FirstChildPtr,跳回第3步。

 

以下是向树中插入一个[1,2,3]的材质的过程。

















附上代码:
/********************************************************************
created:    2:7:2007   11:35
filename:     SG_Shader.h
author:        Badkeeper
purpose:    Shader is uesd order scenenode render. scene node with same shader
            will render in a batch
********************************************************************
*/


#ifndef _SG_SHADER_H
#define _SG_SHADER_H

#include 
"SG_SceneNode.h"

namespace MayEX
{
    
class SG_Shader
    
{
    
public:
        SG_Shader();
        
~SG_Shader();
        
enum ShaderFeature
        
{
            SF_PAD 
= 0,
            SF_BASETEXTURE 
= 1,
            SF_LIGHT     
= 2,
            SF_ALPHAFUNC 
= 3,
            SF_MAX_COUNT
        }
;

        
bool operator == (const SG_Shader& rkShader);
        
//this operator is used in shader order tree sorting
        int  operator -  (const SG_Shader& rkShader); 
        
int  operator -  (const SG_Shader& rkShader) const;
        
        
void SetShaderFeature(ShaderFeature eFeature,int ID);
    
        
int  GetShaderFeature(ShaderFeature eFeature);
        
int  GetShaderFeature(const ShaderFeature eFeature) const;

        SG_Shader
* GetNext();
        SG_Shader
* GetFirstChild();
        SG_Shader
* GetPrev();
        SG_Shader
* GetParent();
    
        
const SG_Shader* GetNext() const;
        
const SG_Shader* GetFirstChild() const;
        
const SG_Shader* GetPrev() const;
        
const SG_Shader* GetParent() const;
        
        
void       SetNext(SG_Shader* pkShader);
        
void       SetFirstChild(SG_Shader* pkShader);

    
protected:
        
void       SetPrev(SG_Shader* pkShader);
        
void       SetParent(SG_Shader* pkShader);


        
int     m_aiShaderFeatures[SF_MAX_COUNT];

        SG_Shader
* m_pkNextShader;
        SG_Shader
* m_pkChildShader;
        SG_Shader
* m_pkPrevShader;
        SG_Shader
* m_pkParentShader;
    }
;
}




#endif

 

 

 

#include "SG_Shader.h"

using namespace MayEX;

//---------------------------------------------------------
SG_Shader::SG_Shader()
: m_pkChildShader(NULL)
, m_pkNextShader(NULL)
, m_pkPrevShader(NULL)
, m_pkParentShader(NULL)
{
    
for(int i = SF_PAD; i < SF_MAX_COUNT; i++)
        m_aiShaderFeatures[i] 
= -1;
}

//---------------------------------------------------------
SG_Shader::~SG_Shader()
{
    
if(m_pkNextShader)
    
{
        delete m_pkNextShader;
        m_pkNextShader 
= NULL;
    }

    
if(m_pkChildShader)
    
{
        delete m_pkChildShader;
        m_pkChildShader 
= NULL;
    }


    
//destory this
}

//---------------------------------------------------------
int SG_Shader::operator - (const SG_Shader& rkShader)
{
    
for(int i = SF_PAD + 1; i < SF_MAX_COUNT; i++)
    
{
        ShaderFeature eFeature 
= (ShaderFeature)i;
        
        
if(GetShaderFeature(eFeature) != rkShader.GetShaderFeature(eFeature))
            
return i;
    }


    
return SF_MAX_COUNT; //equal
}

//---------------------------------------------------------
int SG_Shader::operator - (const SG_Shader& rkShader) const
{
    
for(int i = SF_PAD + 1; i < SF_MAX_COUNT; i++)
    
{
        ShaderFeature eFeature 
= (ShaderFeature)i;

        
if(GetShaderFeature(eFeature) != rkShader.GetShaderFeature(eFeature))
            
return i;
    }


    
return SF_MAX_COUNT; //equal
}


//---------------------------------------------------------
void SG_Shader::SetShaderFeature(ShaderFeature eFeature,int ID)
{
    assert(eFeature 
!= SF_PAD && "Set error shader feature");
    m_aiShaderFeatures[eFeature] 
= ID;
}

//---------------------------------------------------------
int SG_Shader::GetShaderFeature(ShaderFeature eFeature)
{
    assert(eFeature 
!= SF_PAD && "Get error shader feature");
    
return m_aiShaderFeatures[eFeature];
}

//---------------------------------------------------------
int SG_Shader::GetShaderFeature(const ShaderFeature eFeature) const
{
    assert(eFeature 
!= SF_PAD && "Get error shader feature");
    
return m_aiShaderFeatures[eFeature];
}


//---------------------------------------------------------
SG_Shader* SG_Shader::GetNext()
{
    
return m_pkNextShader;
}

//---------------------------------------------------------
SG_Shader* SG_Shader::GetFirstChild()
{
    
return m_pkChildShader;
}

//---------------------------------------------------------
SG_Shader* SG_Shader::GetPrev()
{
    
return m_pkPrevShader;
}

//---------------------------------------------------------
SG_Shader* SG_Shader::GetParent()
{
    
return m_pkParentShader;
}

//---------------------------------------------------------
const SG_Shader* SG_Shader::GetNext() const
{
    
return m_pkNextShader;
}

//---------------------------------------------------------
const SG_Shader* SG_Shader::GetFirstChild() const
{
    
return m_pkChildShader;
}

//---------------------------------------------------------
const SG_Shader* SG_Shader::GetPrev() const
{
    
return m_pkPrevShader;
}

//---------------------------------------------------------
const SG_Shader* SG_Shader::GetParent() const
{
    
return m_pkParentShader;
}

//---------------------------------------------------------
void SG_Shader::SetNext(SG_Shader* pkShader)
{
    m_pkNextShader 
= pkShader;
    
if(pkShader)
        pkShader
->SetPrev(this);
}

//---------------------------------------------------------
void SG_Shader::SetFirstChild(SG_Shader* pkShader)
{
    m_pkChildShader 
= pkShader;
    
if(pkShader)
        pkShader
->SetParent(this);
}

//---------------------------------------------------------
void SG_Shader::SetPrev(SG_Shader* pkShader)
{
    m_pkPrevShader 
= pkShader;
}

//---------------------------------------------------------
void SG_Shader::SetParent(SG_Shader* pkShader)
{
    m_pkParentShader 
= pkShader;
}


/********************************************************************
created:    3:7:2007   11:35
filename:     SG_ShaderTree.h
author:        Badkeeper
purpose:    Shader Tree is used sort geometry render. 
********************************************************************
*/

#ifndef _SG_SHADER_TREE_H
#define _SG_SHADER_TREE_H

#include 
"c_string_utility.h"
#include 
"SG_Shader.h"

namespace MayEX
{
    
class SG_ShaderTree
    
{
    
public:
        SG_ShaderTree();
        
~SG_ShaderTree();

        
bool InsertShader(SG_Shader* pkShader);
        
bool RemoveShader(SG_Shader* pkShader);
        
int  Size();

        
void OutputShaderTreeStruct();
        
        SG_Shader
* GetFirstNode();
        SG_Shader
* GetNext(SG_Shader* pkNode);
        SG_Shader
* GetParent(SG_Shader* pkNode);



    
protected:
        
bool       _insertShader(SG_Shader* pkFirstNode,
                                 SG_Shader
* pkInsertShader,
                                 
int iTreeLevel);
        SG_Shader
* _findMaxDiffNodeOnThisLevel(const SG_Shader* pkFirstNode,
            
const SG_Shader* pkCompareShader);
        
        
void _outputShaderTreeStruct(const SG_Shader* pkTreeRoot,int iTreeLevel);
        
void _outputShaderStruct(const SG_Shader* pkNode,int iTreeLevel);

        SG_Shader
*   m_pkRootShader;    
        
int             m_iSize;
    }
;
}




#endif


#include "SG_ShaderTree.h"
#include 
"c_logger_manager.h"

using namespace MayEX;
using namespace LogSystem;

//-----------------------------------------------------
SG_ShaderTree::SG_ShaderTree()
: m_pkRootShader(NULL)
, m_iSize(
0)
{

}

//---------------------------------------------------------
SG_ShaderTree::~SG_ShaderTree()
{
    
if(m_pkRootShader)
    
{
        delete m_pkRootShader;
        m_pkRootShader 
= NULL;
    }

}

//---------------------------------------------------------
bool SG_ShaderTree::RemoveShader(SG_Shader* pkShader)
{
    
if(!pkShader)
        
return false;
    SG_Shader
* pkReplaceShader = NULL;
    
if(pkShader->GetFirstChild())
    
{
        pkReplaceShader 
= pkShader->GetFirstChild();
        pkReplaceShader
->SetFirstChild(pkReplaceShader->GetNext());
        pkReplaceShader
->SetNext(pkShader->GetNext());
    }

    
else if(pkShader->GetNext())
    
{
        pkReplaceShader 
= pkShader->GetNext();
    }


    SG_Shader
* pkPrevShader = pkShader->GetPrev();
    
if(pkPrevShader)
    
{
        assert(pkPrevShader
->GetNext() == pkShader && "ShaderTree Critical Error");
        pkPrevShader
->SetNext(pkReplaceShader);
    }


    SG_Shader
* pkParentShader = pkShader->GetParent();
    
if(pkParentShader)
    
{
        assert(pkParentShader
->GetFirstChild() == pkShader && "ShaderTree Critical Error");
        assert(
!pkPrevShader && "ShaderTree Critical Error");
        pkParentShader
->SetFirstChild(pkReplaceShader);
    }


    
return true;
}

//---------------------------------------------------------
bool SG_ShaderTree::InsertShader(SG_Shader* pkShader)
{
    
if(!pkShader)
        
return false;

    
if(!m_pkRootShader)
    
{
        m_pkRootShader 
= pkShader;
        m_iSize
++;
        
return true;
    }

    
    SG_Shader
* pkRootShader =  _findMaxDiffNodeOnThisLevel(m_pkRootShader,pkShader);
    
return _insertShader(pkRootShader,pkShader,1);
}

//---------------------------------------------------------
bool SG_ShaderTree::_insertShader(SG_Shader* pkFirstNode,
                                  SG_Shader
* pkInsertShader,int iTreeLevel)
{
    
int iDiff = *pkFirstNode - *pkInsertShader;
    
if(iDiff == SF_MAX_COUNT)
    
{
        
//Equal TODO
        return true;
    }


    
if(iDiff <= iTreeLevel)
    
{
        SG_Shader
* pkFirstNeigborNode = pkFirstNode->GetNext();
        pkFirstNode
->SetNext(pkInsertShader);
        pkInsertShader
->SetNext(pkFirstNeigborNode);
        m_iSize
++;
        
return true;
    }


    
if(pkFirstNode->GetFirstChild() == NULL)
    
{
        pkFirstNode
->SetFirstChild(pkInsertShader);
        m_iSize
++;
        
return true;
    }

    
else
    
{
        SG_Shader
* pkFirstChildNode = pkFirstNode->GetFirstChild();
        SG_Shader
* pkParent = _findMaxDiffNodeOnThisLevel(pkFirstChildNode,pkInsertShader);
        assert(pkParent 
&& "ShaderTree insert failure");

        
return _insertShader(pkParent,pkInsertShader,iTreeLevel+1);
    }

}

//---------------------------------------------------------
SG_Shader* SG_ShaderTree::_findMaxDiffNodeOnThisLevel(const SG_Shader* pkFirstNode,
                                                      
const SG_Shader* pkCompareShader)
{
    
int iDiff = *pkFirstNode - *pkCompareShader;
    SG_Shader
* pkResult = const_cast<SG_Shader*>(pkFirstNode);
    SG_Shader
* pkDummy = pkResult;

    
while(pkDummy->GetNext())
    
{
        pkDummy 
= pkDummy->GetNext();
        
int iDiff2 = *pkDummy - *pkCompareShader;
        
if(iDiff2 >= iDiff)
        
{
            pkResult 
= pkDummy;
            iDiff 
= iDiff2;
        }

    }


    
return pkResult;
}

//---------------------------------------------------------
void SG_ShaderTree::OutputShaderTreeStruct()
{
    LOGLISTBEGIN(LOGTYPE_PROFILE,
"");
    _outputShaderTreeStruct(m_pkRootShader,
1);
    LOGLISTEND(LOGTYPE_PROFILE);
}

//---------------------------------------------------------
void SG_ShaderTree::_outputShaderTreeStruct(const SG_Shader* pkTreeRoot,
                                                           
int iTreeLevel)
{
    SG_Shader
* pkOutputShader = const_cast<SG_Shader*>(pkTreeRoot);
    
if(!pkOutputShader)
        
return;

    _outputShaderStruct(pkOutputShader,iTreeLevel);

    
//output child
    const SG_Shader* pkChildShader = pkTreeRoot->GetFirstChild();
    
if(pkChildShader)
    
{
        LOGLISTBEGIN(LOGTYPE_PROFILE,
"");
        _outputShaderTreeStruct(pkChildShader,iTreeLevel 
+ 1);
        LOGLISTEND(LOGTYPE_PROFILE);
    }


    
//output neighbor
    const SG_Shader* pkNeighborShader = pkTreeRoot->GetNext();
    
while(pkNeighborShader)
    
{
        _outputShaderTreeStruct(pkNeighborShader,iTreeLevel);
        pkNeighborShader 
= pkNeighborShader->GetNext();
    }

    
return ;
}

//---------------------------------------------------------
void SG_ShaderTree::_outputShaderStruct(const SG_Shader* pkNode,int iTreeLevel)
{
    
if(!pkNode)
        
return;
    
    Utility::StringType kResult 
= "[";
    
for(int i = SG_Shader::SF_PAD + 1; i < SG_Shader::SF_MAX_COUNT; i++)
    
{
        
char cFeature[32= {0};
        
int iID = pkNode->GetShaderFeature(SG_Shader::ShaderFeature(i));
        sprintf(cFeature,
" %i," ,iID);
        kResult 
+= cFeature;
    }

    
    kResult 
+= "]";
    
    LOGLISTITEM(LOGTYPE_PROFILE,kResult.c_str());

    
    
return;
}

//---------------------------------------------------------
SG_Shader* SG_ShaderTree::GetFirstNode()
{
    
return m_pkRootShader;
}

//---------------------------------------------------------
SG_Shader* SG_ShaderTree::GetParent(SG_Shader* pkNode)
{
    SG_Shader
* pkParent = NULL;
    
    
if(!pkNode->GetPrev())
        pkParent 
= pkNode->GetParent();
    
else
    
{
        SG_Shader
* pkPrev = pkNode->GetPrev();
        
while(pkPrev->GetPrev())
            pkPrev 
= pkPrev->GetPrev();
        assert(pkPrev);
        pkParent 
= pkPrev->GetParent();
    }

    
    
return pkParent;
}

//---------------------------------------------------------
SG_Shader* SG_ShaderTree::GetNext(SG_Shader* pkNode)
{
    
if(!pkNode)
        
return NULL;

    
if(pkNode->GetFirstChild())
        
return pkNode->GetFirstChild();
    
else if(pkNode->GetNext())
        
return pkNode->GetNext();
    
    SG_Shader
* pkParent = GetParent(pkNode);
    
    
while(pkParent && !pkParent->GetNext())
        pkParent 
= GetParent(pkParent);

    
if(pkParent)
        
return pkParent->GetNext();

    
return NULL;
}

posted @ 2007-07-05 16:32  BadKeeper  阅读(2239)  评论(3编辑  收藏  举报