wild magic3的Scene graph結構和Geometric State更新體系

來源:互聯網
上載者:User

剛看了《3D Game Engine Architecture》第3章“Scene graphs and renderers"的前兩節,很精彩,暫且不拿wild magic 3和其他引擎相比較,只是作為讀書筆記,記錄一下書中的核心內容。

我覺得第3章是此書的核心部分,全部內容就是scene graph的更新和渲染,其中第一節描述了wild magic3中的scene graph架構,第二節重點講解了scene grapha的Geometirc State 更新體系(updateGS)。

1)wild magic3 scene graphs

包括幾個核心類: Spatial, Node, Geometry
其中Spatial是Node和Geometry的父類,Spatial包含了local transform和world transform,以及world bound(世界空間的包圍體)。並且Spatial擁有上一層Spatial的指標(parent spatial)。

Node包含一組子節點(注意,子節點使用Spatial指標,而不是Node指標,因為子節點可能是Node也可能是Geometry,或者他們的子類),通過Spatial和Node組成了scene 的樹形結構。

而Geometry是有mesh的幾何實體,包括索引數組,模型空間的頂點數組,模型空間的法線數組,以及模型空間的包圍體,還有模型級的scale值。
同時他也是繼承自Spatial的,所以也可以變換,也被放置到scene grapha中,但是在wild magic3中,Geometry只能作為葉子節點(沒有子節點了,只有Node有子節點)

從這兒看,Node的作用就是一個group, Node不是實體,實體只能是葉子節點,一般是Node的子節點中有Geometry。
另外,對於共用模型資料,wild magic3是在low level實現的,即Geometry的子類可以共用模型資料。

2)wild magic3中的transform
Transformation 這個類,包含一個3X3 rotation matrix, 一個 vector3f translate,和一個vector3f表達的non-uniform scale。他為了減少計算量,沒有像irrlicht那樣直接用一個4X4 matrix。Transformation類中的Product(const Transformation& rkA, const Transformation& rkB)方法相當於矩陣相乘,用於在scene grapha更新時從上到下更新節點的世界變換。

3)wild magic3的geometric updates

為了方便看,把相關代碼列到一起

class Spatial: public Object
{
    public:
        Transformation Local, World;
        bool WorldIsCurrent;

        BoundingVolumePtr WorldBound;
        bool WorldBoundIsCurrent;

        void UpdateGS(double dAppTime=-Mathd::MAX_REAL, bool bInitiator=true)
        {
            UpdateWorldData(dAppTime);
            UpdateWorldBound();
            if(bInitiator)
            {
                PropagateBoundToRoot();
            }
        }

        void UpdateBS()
        {
            UpdateWorldBound();
            PropagateBoundToRoot();
        }

    protected:
        virtual void UpdateWorldData(double dAppTime)
        {
            UpdateControllers(dAppTime);//control可能直接修改local或world transform
            if(!WorldIsCurrent)
            {
                if(m_pkParent)
                    World.Product(m_pkParent->World, Local);
                else
                    World = Local;
            }
        }

        virtual void UpdateWorldBound() = 0;

        void PropagateBoundToRoot()
        {
            if(m_pkParent)
            {
                m_pkParent->UpdateWorldBound();
                m_pkParent->PropagateBoundToRoot();
            }
        }
};

class Geometry: public Spatial
{
    public:
        //省略其他資料,如indices, vertices, normals..
        BoundingVolumePtr ModelBound;

        void UpdateMS();
    protected:
        virtual void UpdateModelBound();
        virtual void UpdateModelNormals();
        virtual void UpdateWorldBound()
        {
            ModelBound->TransformBy(World, WorldBound);
        }
};

class Node: public Spatial
{
    protected:
        virtual void UpdateWorldData(double dAppTime)
        {
            Spatial::UpdateWorldData(dAppTimie);
            for(int i=0; i
            {
                Spatial* pkChild = m_kChild[i];
                if(pkChild)
                    pkChild->UpdateGS(dAppTime, false);
            }
        }

        virtual void UpdateWorldBound()
        {
            if(!WorldBoundIsCurrent)
            {
                bool bFoundFirstBound = false;
                for(int i=0; i
                {
                    Sptial* pkChild = m_kChild[i];
                    if(pkChild)
                    {
                        if(bFoundFirstBound)
                        {
                            //Merge current world bound with child world bound
                            WorldBound->GrowToContain(pkChild->WorldBound);
                        }
                        else
                        {
                            //set world bound to first nonull child world bound
                            bFoundFirstBound = true;
                            WorldBound->CopyFrom(pkChild->WorldBound);
                        }
                    }
                }
            }
        }
};

scene grapha的update主要做兩件事情,一是從上到下更新world transform,二是從下往上更新world bound。其中包圍體是要包括所有子節點的包圍體的,而只有Geometry類型的節點需要計算自己的包圍體(從頂點資料計算)。

wild magic3中,更新不是自動的,必須手工調用,而且要選擇從哪一個節點開始更新,一般是某節點需要更新(比如local transform變了)並且他上面沒有需要更新的父節點,那麼就要調用該節點的UpdateGS,這樣的節點有幾個就調用幾次UpdateGS。 UpdateGS裡面通過遍曆和遞迴,做了上面說的兩件事情。UpdateGS的參數bInitiator 表明這個node是更新的起點,只有這個node的UpdateGS裡面才會向上更新world bound volume直到root,而其他的node只會更新自己的world bound不會向上傳播,這是為了提高效率。(因為這是在transform和world bound更新完成之後才調用的,所以只要更新的起點node向上更新包圍體就足夠了,下層的node沒必要向上傳播,否則也是被覆蓋,浪費計算)

計算transform是在UpdateWorldData 裡面做的,對於spatial的UpdateWorldData,主要就是把自己的local transform和parent的world transform級聯起來,得到自己的world transform,但是這之前首先會使用controller進行更新,controller可能對transform系統產生影響也可能不影響,使用 controller時通過設定WorldIsCurrent來決定是否controller已經更新了world transform而不需要級聯的更新方式。(比如skin controller)。而另外一些controller如普通key frame和IK,只是改變了local transform,還是需要使用級聯的方式更新world transform的,他就不會設定WorldIsCurrent。另外一些controller沒有影響到transform,也不會設定 WorldIsCurrent,比如morphe controller,但是他需要調用Geometry的UpdateMS來更新模型的一些資料(ModelBound).
Geometry沒有override UpdateWorldData,所以和Spatial是一樣的。
Node 的UpdateWorldData裡面首先是直接調用了Spatial的UpdateWorldData來更新自己的world transform,然後對於他的所有子節點(Spitial,可能是Node或Geometry)遍曆調用UpdateGS(bInitiator參數為false,因為子節點肯定不需要傳播bound更新到root)。這就形成了UpdateGS的遞迴調用。這是一個深度優先的樹遍曆。樹的每一層都是先計算好自己的transform然後讓子節點去UpdateGS,所有子節點的調用都完成後才會計算自己的world bound,最終所有層次的UpdateGS都執行完畢,回到調用的起點節點那兒,接著執行那個節點的UpdateWorldBound。因為起始調用的節點的bInitiator是true,所以會執行PropagateBoundToRoot,向上更新world bound直到root。

更新UpdateWorldBound在spatial裡面是個純虛函數,Geometry的實現就是使用world transform變換model bound得到world bound,而Node裡面則是計算一個包含所有子節點的world bound的總包圍體。其中WorldBoundIsCurrent的作用是,當某個node的world bound可以通過其他途徑得到時避開正常的計算流程,比如一個房間是固定的可以預先計算好一個world bound不再改變,不管其中的子節點怎麼動怎麼變都不再計算這個房間的world bound了。

整個過程就是這樣的:
三個類(Spatial, Node, Geomotry)
三個主要函數(UpdateWorldData,UpdateWorldBound,以及PropagateBoundToRoot)
三個重要標誌的設定(UpdateGS的bInitiator參數,Spatial的成員WorldIsCurrent和WorldBoundIsCurrent)
node/spatial的遍曆,UpdateGS的遞迴
完成了world transform和world bound的更新。

另外UpdateBS是另外一個公開的介面,當model bound變化時,而transform沒變化時,就只要調用UpdateBS就可以了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.