• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            麒麟子

            ~~

            導(dǎo)航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統(tǒng)計(jì)

            常用鏈接

            留言簿(12)

            隨筆分類

            隨筆檔案

            Friends

            WebSites

            積分與排名

            最新隨筆

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            #

            Directional Light Map(Directional Irradiance)

            Light Map是一個(gè)比較經(jīng)典的技術(shù),目前來說應(yīng)該是一般游戲引擎中的標(biāo)配,它很好的在一種擬全局光效果的質(zhì)量和效率上做了中和。不過目前用的更多、質(zhì)量更好的應(yīng)該是Directional Light Map,它是原始LM的增強(qiáng)版,通過在預(yù)處理與實(shí)時(shí)還原中考量場景中表面的法向量進(jìn)而增強(qiáng)效果。DLM的基本操作方法如下:

            • 在采樣點(diǎn)處把其半球空間中的輻射照度用某種方法進(jìn)行采集并保存(比如離線的光線跟蹤);
            • 以某種方法存儲(chǔ)額外的、與該輻射照度相關(guān)的法線信息到光照貼圖中;
            • 實(shí)時(shí)渲染中通過光照貼圖對(duì)像素上的場景輻照度進(jìn)行還原(結(jié)合光照信息與方向信息)。

            目前常用的DLM實(shí)現(xiàn)方法主要有三種:

            Radiosity Normal Maps

            該方法最早是在Valve的Source引擎中用的一種模式,它的原理應(yīng)該是一種擬信號(hào)壓縮與重建的方法:

            • 在采樣點(diǎn)處選取三個(gè)正交的采樣方向作為基方向,采樣得到這些方向上的光照信息并保存(壓縮);
            • 實(shí)時(shí)渲染中通過三個(gè)方向及其上的光照值做為基函數(shù),對(duì)實(shí)際表面法向上的光照值進(jìn)行還原(重建)。

            其中的三個(gè)基方向(表面法向所在的局部切空間,如下圖示)分別為:

            , ,

            對(duì)應(yīng)的還原計(jì)算為:

            Valve的這種方法應(yīng)該說還是很不錯(cuò)的,雖然數(shù)學(xué)理論依據(jù)不太充分,但至少看起來效果很不錯(cuò),而且實(shí)現(xiàn)簡單,效率較高。不過其也會(huì)出現(xiàn)一些問題,那就是當(dāng)法線的方向與采樣光照的主方向夾角較大(即與采樣切平面的夾角較小時(shí))容易出現(xiàn)一些不太正確的光照還原。

            Dominant Directional Irradiance

            該方法的原理可以是看作將采樣點(diǎn)半球空間中的輻照信息處理為一個(gè)方向光(Directional Irradiance),這樣在實(shí)時(shí)渲染中就可以使用反射模型進(jìn)行快速還原;其中的Dominant axis就可以看作是指該平行光的方向。其操作如下:

            • 外理采樣點(diǎn)外部輻照信息為:方向光(方向:L,顏色:CL);
            • 渲染中直接反射模型模型來還原 。

            比如使用Lambert模型時(shí)對(duì)應(yīng)的還原計(jì)算為:

            另外,一般情況下也會(huì)使用方向貼圖的空閑的Alpha通道來存儲(chǔ)一個(gè)縮放因子,用其來控制該點(diǎn)上外部輻照度的方向性(即被dominant方向影響的力度)。當(dāng)然,這里也可以使用其它更復(fù)雜的一些shading model來操作,不過Lambert已經(jīng)足夠了。該方法的計(jì)算量相對(duì)也比較??;存儲(chǔ)空間也比較節(jié)省,只需要在傳統(tǒng)LM的基礎(chǔ)上再存儲(chǔ)一張方向貼圖就可以了(目前來說該方法較為流行,比如UE或Enlighten中就使用此方法)。

            Spherical Harmonics

            該方法與Radiosity Normal Map的方法類似(同樣與Light Probe的原理類似),只不過這里使用了理論與數(shù)學(xué)依據(jù)更為充分的球諧函數(shù)來實(shí)現(xiàn)外部輻射照度信息的壓縮與重建:

            • 把光照函數(shù)使用球諧函數(shù)進(jìn)行變換存儲(chǔ)(壓縮);
            • 實(shí)時(shí)渲染中使用時(shí)直接利用SH重建進(jìn)行還原即可(還原)。

            這種方法比上述兩種方法都更為高級(jí),一般來說對(duì)于任意Normal上的照度信息都能正確還原,而且適應(yīng)性較強(qiáng),較為靈活;但同時(shí)有很大的缺點(diǎn)那就是存儲(chǔ)空間較大(其LM中每個(gè)Pixel中存儲(chǔ)的數(shù)據(jù)量相當(dāng)于一個(gè)Light Probe對(duì)應(yīng)的存儲(chǔ)的內(nèi)容),因而其應(yīng)用范圍就有所限制。

            下述是LM與DLM的簡單效果對(duì)比,差異還是相當(dāng)明顯的:

            http://blog.csdn.net/bugrunner/article/details/7881819

            posted @ 2013-04-01 00:11 麒麟子 閱讀(587) | 評(píng)論 (0)編輯 收藏

            irrlicht引擎:硬件蒙皮骨骼動(dòng)畫

            這個(gè)東西很順利,僅用了半小時(shí)就找到了方法,最應(yīng)該感謝的還是Super TuxKart(簡稱STK,下面就都用這三字母了). 如果不明白STK,同時(shí)又對(duì)它感興趣的童鞋,可以訪問這里

            http://supertuxkart.sourceforge.net/

            由于墻的原因,需要各位搭梯子。

             

            上周末,在弄換裝的時(shí)候,發(fā)現(xiàn)irrlicht引擎本身是不支持硬件蒙皮的,多少令人有些失望。 心里就一直尋思著怎么擴(kuò)展一下,將它弄出來。

            值得說明的是STK對(duì)irrlicht引擎的用法是很簡單的,基本上可以說是裸用,并未在irrlicht接口上做修改。 而是對(duì)外進(jìn)行了一些必要的擴(kuò)展。

            當(dāng)然,STK也對(duì)外開放了一個(gè)irrlicht.dll,說是修改了其中的BUG。 但直接使用irrlicht是可以的。

             

            廢話不多說,來說說如何不修改irrlicht一行代碼,通過外部擴(kuò)展來實(shí)現(xiàn)硬件骨骼動(dòng)畫吧

             

            首先,能夠使我們不修改irrlicht代碼的原因,是因?yàn)镮SkinnedMesh提供了一個(gè)setHardwareSkinning接口,默認(rèn)為false.

            雖然這個(gè)接口的說明是"(This feature is not implemented in irrlicht yet)”,但并不代表,設(shè)置與不設(shè)置無差別。

            查看代碼可以發(fā)現(xiàn),當(dāng)你設(shè)置了這個(gè)為true以后,irrlicht就完全不管你的動(dòng)畫了。 意思就是,要是你非要讓我干我不干不了的事,那就只有您另請高明了。

            irrlicht連CPU計(jì)算都不會(huì)參與。 這正好讓我們有機(jī)可乘,完全用GPU接管。

             

            而要讓一個(gè)頂點(diǎn)參與骨骼計(jì)算,那骨骼索引則是少不了的。所以,我們需要想辦法讓頂點(diǎn)數(shù)據(jù)能夠?qū)⒐趋浪饕隨HADER中。

            在STK中用了一種巧妙的方法, 就是使用了頂點(diǎn)的顏色數(shù)據(jù), 雖然這樣一來,頂點(diǎn)顏色就用不了了。 但在模型渲染時(shí),頂點(diǎn)顏色很少被使用到的。 也就是說,頂點(diǎn)顏色在STK的動(dòng)畫模型中,被用作了骨骼索引。

            初始化骨骼索引的方法很簡單,用下面的代碼遍歷即可。

            設(shè):我們有一個(gè)骨骼動(dòng)畫模型是 ISkinnedMesh* pSkinnedMesh = …

            那么:初始化代碼如下

            for(u32 i = 0;i < pSkinnedMesh ->getMeshBuffers().size();++i)
            {
                for(u32 g = 0;g < pSkinnedMesh ->getMeshBuffers()[i]->getVertexCount();++g)
                {
                    pSkinnedMesh ->getMeshBuffers()[i]->getVertex(g)->Color = video::SColor(0,0,0,0);
                }
            }

            //初始化完畢以后,就是需要真正的索引賦值了,通過以下代碼可以完成

            const core::array<scene::ISkinnedMesh::SJoint*>& joints = pSkinnedMesh ->getAllJoints();
            for(u32 i = 0;i < joints.size();++i)
            {
                const core::array<scene::ISkinnedMesh::SWeight>&    weights = joints[i]->Weights;
                for(u32 j = 0;j < weights.size();++j)
                {
                    int buffId = weights[j].buffer_id;

                    int vertexId = pSkinedMesh->getAllJoints()[i]->Weights[j].vertex_id;
                    video::SColor* vColor = &pSkinedMesh->getMeshBuffers()[buffId]->getVertex(vertexId)->Color;

                    if(vColor->getRed() == 0)
                        vColor->setRed(i + 1);
                    else if(vColor->getGreen() == 0)
                        vColor->setGreen(i + 1);
                    else if(vColor->getBlue() == 0)
                        vColor->setBlue(i + 1);
                    else if(vColor->getAlpha() == 0)
                        vColor->setAlpha(i + 1);
                }
            }

             

            //經(jīng)過以上兩個(gè)步驟,頂點(diǎn)數(shù)據(jù)改造完成。 值得注意的是, 在這里, 索引 0 是被認(rèn)為是無效的

             

            然后,我們來創(chuàng)建一個(gè)SHADER作為渲染。

            假設(shè) 我們將這個(gè)pSkinnedMesh綁定了到了一個(gè)IAnimatedSceneNode* node 上。

            那,我們?yōu)檫@個(gè)結(jié)點(diǎn)創(chuàng)建一個(gè)材質(zhì) 在創(chuàng)建材質(zhì)前,我們需要準(zhǔn)備一個(gè)SHADER回調(diào)。 SHADER回調(diào)就像下面一樣就可以了。

            class HWSkinCallBack:public video::IShaderConstantSetCallBack
            {
                scene::IAnimatedMeshSceneNode* m_pNode;
            public:
                HWSkinCallBack(scene::IAnimatedMeshSceneNode* node):m_pNode(node)
                {

                }
                virtual void OnSetConstants(video::IMaterialRendererServices* services,
                    s32 userData)
                {
                    scene::ISkinnedMesh* mesh = (scene::ISkinnedMesh*)m_pNode->getMesh();
                    f32 joints_data[55 * 16];
                    int copyIncrement = 0;

                    const core::array<scene::ISkinnedMesh::SJoint*> joints = mesh->getAllJoints();
                    for(u32 i = 0;i < joints.size();++i)
                    {
                        core::matrix4 joint_vertex_pull(core::matrix4::EM4CONST_NOTHING);
                        joint_vertex_pull.setbyproduct(joints[i]->GlobalAnimatedMatrix, joints[i]->GlobalInversedMatrix);

                        f32* pointer = joints_data + copyIncrement;
                        for(int i = 0;i < 16;++i)
                            *pointer++ = joint_vertex_pull[i];

                        copyIncrement += 16;
                    }

                    services->setVertexShaderConstant("JointTransform", joints_data, mesh->getAllJoints().size() * 16);
                }
            };

             

            好了,現(xiàn)在我們來創(chuàng)建一個(gè)材質(zhì)

            s32 hwskm = gpu->addHighLevelShaderMaterialFromFiles(
                    "../../skinning.vert","main",video::EVST_VS_2_0,
                    "","main",video::EPST_PS_2_0,&hwc,video::EMT_SOLID);

            //用新創(chuàng)建出來的材質(zhì)賦值給這個(gè)結(jié)點(diǎn)

            node->setMaterialType((video::E_MATERIAL_TYPE)hwskm );

             

            //到此,設(shè)置完畢。

            //最后,就是skinning.vert本身的內(nèi)容了。 貼出來即可,沒有太多技巧,就是一個(gè)普通的蒙皮。

            // skinning.vert

            #define MAX_JOINT_NUM 36
            #define MAX_LIGHT_NUM 8

            uniform mat4 JointTransform[MAX_JOINT_NUM];

            void main()
            {
                int index;
                vec4 ecPos;
                vec3 normal;
                vec3 light_dir;
                float n_dot_l;
                float dist;

                mat4 ModelTransform = gl_ModelViewProjectionMatrix;
                index = int(gl_Color.r * 255.99);
                mat4 vertTran = JointTransform[index - 1];
                index = int(gl_Color.g * 255.99);
                if(index > 0)
                    vertTran += JointTransform[index - 1];

                index = int(gl_Color.b * 255.99);
                if(index > 0)
                    vertTran += JointTransform[index - 1];
                index = int(gl_Color.a * 255.99);
                if(index > 0)
                    vertTran += JointTransform[index - 1];
                ecPos = gl_ModelViewMatrix * vertTran * gl_Vertex;
                normal = normalize(gl_NormalMatrix * mat3(vertTran) * gl_Normal);
                gl_FrontColor = vec4(0,0,0,0);
                for(int i = 0;i < MAX_LIGHT_NUM;i++)
                {
                    light_dir = vec3(gl_LightSource[i].position-ecPos);
                    n_dot_l = max(dot(normal, normalize(light_dir)), 0.0);
                    dist = length(light_dir);
                    n_dot_l *= 1.0 / (gl_LightSource[0].constantAttenuation + gl_LightSource[0].linearAttenuation * dist);
                    gl_FrontColor += gl_LightSource[i].diffuse * n_dot_l;
                }
                gl_FrontColor = clamp(gl_FrontColor,0.3,1.0);

                ModelTransform *= vertTran;
                gl_Position = ModelTransform * gl_Vertex;
                gl_TexCoord[0] = gl_MultiTexCoord0;
                gl_TexCoord[1] = gl_MultiTexCoord1;
                /*
                // Reflections.
                vec3 r = reflect( ecPos.xyz , normal );
                float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) );
                gl_TexCoord[1].s = r.x/m + 0.5;
                gl_TexCoord[1].t = r.y/m + 0.5;
                */
            }

             

             

            //注:這是GLSL 2.0, 在用IRR做測試的時(shí)候,要選GL驅(qū)動(dòng)方式。

             

            還是上個(gè)圖吧,不上圖感覺沒有真像。 雖然圖看不出來什么動(dòng)作

            image

            為了說明它真的在動(dòng),不得不上第二張。

             

            image 

             

            在此,十分感謝Super Tux Kart. 提供了一個(gè)學(xué)習(xí)和擴(kuò)展irrlicht的榜樣.

            posted @ 2013-03-26 00:08 麒麟子 閱讀(3348) | 評(píng)論 (0)編輯 收藏

            irrlicht引擎:實(shí)現(xiàn)天龍八部的RPG角色換裝

            看了看時(shí)間,已經(jīng)3點(diǎn)過了,突然想寫點(diǎn)什么,卻又不知從何說起。

            那就從今天這個(gè)用irrlicht做天龍八部的模型換裝說起吧。

             

            也不知道是為什么,最近又搗鼓起了OGRE和irrlicht. 并且,總想用irrlicht實(shí)現(xiàn)一些OGRE中的東西。

            當(dāng)然,這不是商業(yè)項(xiàng)目,也沒有商業(yè)目的,純屬蛋疼而已。

             

            一切行動(dòng)的由來,都來自于vczh那天晚上的舉動(dòng)。

            記得有一天晚上在群里聊天,大伙就稱贊各位菊苣是多么的厲害。

            最后vc發(fā)了一個(gè)自己的桌面截圖說:讓你們看看菊苣是如何練成的(這不是原話,和話的字眼有出入,在此不想負(fù)任何責(zé)任,如果真有想看的,去翻群的聊天記錄)

            那天晚上,我想了很久。想想自己自從轉(zhuǎn)做頁游以后,是如何虛渡光陰的。

            終于忍不住了,翻開了自己的移動(dòng)硬盤,看看自己曾經(jīng)做過的小東西。90%是建好工程就沒理了。

            這才明白,我花在思考上的時(shí)間遠(yuǎn)遠(yuǎn)大于了行動(dòng)。 于是,我決定改變自己,找回那個(gè)真的我。

             

            3D游戲是我的真愛, 真愛到就算畫面差一點(diǎn),只要是3D,我也會(huì)很喜歡。

            于是,我覺得自己還是應(yīng)該接著先前的路走下去。 什么服務(wù)器,什么 AS3. 都是浮云, 不喜歡就是不喜歡。

            私下又開始研究irrlicht了。

            猛地一發(fā)現(xiàn),自己是多么的搞笑, 從09年到11年,一直在做引擎開發(fā), 也翻過irrlicht和ogre無數(shù)遍。 卻從來就沒有寫完過一個(gè)完整的DEMO。

            連功能測試用例都沒有寫過。突然覺得之前的一些設(shè)計(jì)似乎有些脫離了實(shí)際。沒有真正使用過,又怎知如何是好,如何是壞呢?

             

            這一次是真的玩irrlicht了, 中間也糾結(jié)過是不是OGRE更適合。 但在目前這個(gè)時(shí)間有限的空間下,我更愿意玩irrlicht.小巧,輕便。 當(dāng)然,意味著更多東西要自己實(shí)現(xiàn)。 不過對(duì)于一個(gè)代碼控來說,也反而更自得其樂。 正好可以在短路的時(shí)候,去參考一下其它引擎,用來擴(kuò)充irrlicht.

            我要做的不是把irrlicht整得牛B,而是想自己弄弄,加上移動(dòng)平臺(tái)的崛起,我覺得irrlicht更加適合吧。 據(jù)說gameloft也有使用(僅是據(jù)說)。

             

            可能很多兄弟會(huì)說我這講的東西,其實(shí)就是一坨屎了。 不過,我覺得再壞的評(píng)論,也表示一種關(guān)注。 批評(píng)好過于無視啊~~~~

            ----------------------------------------------------------下面說說我遇上的糾結(jié)------------------------------------------------

            糾結(jié)1:換裝需要場景節(jié)點(diǎn)配合

              在irrlicht中,并沒有提供普通引擎中的submesh或者bodypart這種東西,用于直接支持換裝。 在irrlicht中,如果想要換裝,最直接的方法就是依賴于場景結(jié)點(diǎn)

            比如,在我的示例中,可以更換頭發(fā),帽子,衣服,護(hù)腕,靴子,面容。 那就需要7個(gè)場景節(jié)點(diǎn),1個(gè)作為根節(jié)點(diǎn),用于控制整個(gè)角色的世界坐標(biāo),平移,縮放,旋轉(zhuǎn)等屬性。另外6個(gè)場景節(jié)點(diǎn)則分別綁有各個(gè)部件的模型

            貼一下我的角色類的代碼,行數(shù)不多

            class CCharactor
            {
                IrrlichtDevice* m_pDevice;
                IAnimatedMeshSceneNode* m_pBodyParts[eCBPT_Num];
                ISceneNode* m_pRoot;
            public:
                CCharactor(IrrlichtDevice* pDevice)
                    :m_pDevice(pDevice)
                {
                    memset(m_pBodyParts,0,sizeof(m_pBodyParts));
                    m_pRoot = pDevice->getSceneManager()->addEmptySceneNode(NULL,12345);
                }
                void changeBodyPart(ECharactorBodyPartType ePartType,stringw& meshPath,stringw& metrialPath)
                {
                    ISceneManager* smgr = m_pDevice->getSceneManager();

                    IAnimatedMeshSceneNode* pBpNode = m_pBodyParts[ePartType];
                    IAnimatedMesh* pMesh = smgr->getMesh(meshPath.c_str());
                    if(pMesh==NULL)
                        return;

                    if(pBpNode==NULL)
                    {
                        pBpNode = smgr->addAnimatedMeshSceneNode(pMesh,m_pRoot);
                        m_pBodyParts[ePartType] = pBpNode;
                    }
                    else
                    {
                        pBpNode->setMesh(pMesh);
                    }
                    ITexture* pTexture = m_pDevice->getVideoDriver()->getTexture(metrialPath.c_str());
                    if(pTexture)
                        pBpNode->setMaterialTexture(0,pTexture);
                }
            };

             

            //然后,我用了一個(gè)結(jié)構(gòu)體來構(gòu)建部件信息

            struct SBodyPartInfo
            {
                stringw Desc;
                ECharactorBodyPartType Type;
                stringw MeshPath;
                stringw MeterialPath;
            };

             

            糾結(jié)2:共享骨骼

            首先,irrlicht 1.8中對(duì)OGRE模型的格式支持在代碼中,最高只看到了1.40版本的解析,更高的就會(huì)被無視。 天龍八部的模型有幾個(gè)是1.30的,而用于換裝和主角的,都是1.40的。 可能是解析不全的原因,導(dǎo)致1.40的骨骼動(dòng)畫無法正常播放。 這個(gè)問題整了幾個(gè)小時(shí),沒有解決,明天繼續(xù)

            其次,多個(gè)模型共享骨骼只能通過場景節(jié)點(diǎn)的useAnimationFrom來完成,并且傳入的是一個(gè)Mesh參數(shù)。這點(diǎn)讓人蛋疼, 天龍八部的角色動(dòng)作是分開了的,不同的攻擊動(dòng)作是一個(gè)skeleton文件。 想要實(shí)現(xiàn)共享,有點(diǎn)麻煩。

             

            糾結(jié)3:模型文件格式

            irrlicht不像OGRE那樣有一個(gè)強(qiáng)大且成熟的模型文件格式,雖然提供了.irr格式,但僅是用于irrEdit的場景信息輸出。先看一張圖

            image

            這張圖是irrlicht samples中的MeshViewer的提示框內(nèi)容。 上面列出了可以支持的模型文件類型。 大家可以看看,又有多少模型格式是可以直接拿來放到項(xiàng)目上用的呢? mdl和ms3d可以考慮,dae的話,我在開源游戲0 A.D. 中見到使用過。 其它的話,就完全不熟悉了。 OGRE的 .mesh支持也不完全。 難道真要自己整一個(gè)。

            我能想到的,就是選一個(gè)插件完整和模型和動(dòng)畫格式都比較好的作為與美術(shù)工具交互的格式。 自己再寫一個(gè)工具,轉(zhuǎn)換成自己的格式。

             

            糾結(jié)4:硬件蒙皮

            我以為像NIKO那樣的技術(shù)狂,怎么會(huì)放掉這一個(gè)特性。 很高興地在場景節(jié)點(diǎn)上發(fā)現(xiàn)了硬件蒙皮的函數(shù)接口。但一看注釋,把我咽著了。

            //! (This feature is not implemented in irrlicht yet)
            virtual bool setHardwareSkinning(bool on);

             

            其它地方,還沒有去整,就先不發(fā)表言論了。 繼續(xù)著這個(gè)很傻B,很天真的搗鼓之路。

            上個(gè)圖,紀(jì)念一下我的irrlicht產(chǎn)物。

             

            布衣

            image

             

            換了身盔甲

            image

             

            換了帽子和靴子

            image

             

            PS:頭發(fā)沒有紋理,所以是白的。

            posted @ 2013-03-24 04:08 麒麟子 閱讀(3637) | 評(píng)論 (0)編輯 收藏

            請教大家一個(gè)關(guān)于EPOLLET和EPOLLLT的問題

            今天在查看EPOLLET和EPOLLLT的細(xì)節(jié)的時(shí)候,發(fā)現(xiàn)一篇文章。 但不知文中說的是否有道理,望各位大大給個(gè)明確的答復(fù)。
            游戲服務(wù)器,我們用的是ET方式。

            剖析 epoll ET/LT 觸發(fā)方式的性能差異誤解(定性分析)


            平時(shí)大家使用 epoll 時(shí)都知道其事件觸發(fā)模式有默認(rèn)的 level-trigger 模式和通過 EPOLLET 啟用的 edge-trigger 模式兩種。從 epoll 發(fā)展歷史來看,它剛誕生時(shí)只有 edge-trigger 模式,后來因容易產(chǎn)生 race-cond 且不易被開發(fā)者理解,又增加了 level-trigger 模式并作為默認(rèn)處理方式。

            二者的差異在于 level-trigger 模式下只要某個(gè) fd 處于 readable/writable 狀態(tài),無論什么時(shí)候進(jìn)行 epoll_wait 都會(huì)返回該 fd;而 edge-trigger 模式下只有某個(gè) fd 從 unreadable 變?yōu)?readable 或從 unwritable 變?yōu)?writable 時(shí),epoll_wait 才會(huì)返回該 fd。

            通常的誤區(qū)是:level-trigger 模式在 epoll 池中存在大量 fd 時(shí)效率要顯著低于 edge-trigger 模式。

            但從 kernel 代碼來看,edge-trigger/level-trigger 模式的處理邏輯幾乎完全相同,差別僅在于 level-trigger 模式在 event 發(fā)生時(shí)不會(huì)將其從 ready list 中移除,略為增大了 event 處理過程中 kernel space 中記錄數(shù)據(jù)的大小。

            然而,edge-trigger 模式一定要配合 user app 中的 ready list 結(jié)構(gòu),以便收集已出現(xiàn) event 的 fd,再通過 round-robin 方式挨個(gè)處理,以此避免通信數(shù)據(jù)量很大時(shí)出現(xiàn)忙于處理熱點(diǎn) fd 而導(dǎo)致非熱點(diǎn) fd 餓死的現(xiàn)象。統(tǒng)觀 kernel 和 user space,由于 user app 中 ready list 的實(shí)現(xiàn)千奇百怪,不一定都經(jīng)過仔細(xì)的推敲優(yōu)化,因此 edge-trigger 的總內(nèi)存開銷往往還大于 level-trigger 的開銷。

            一般號(hào)稱 edge-trigger 模式的優(yōu)勢在于能夠減少 epoll 相關(guān)系統(tǒng)調(diào)用,這話不假,但 user app 里可不是只有 epoll 相關(guān)系統(tǒng)調(diào)用吧?為了繞過餓死問題,edge-trigger 模式的 user app 要自行進(jìn)行 read/write 循環(huán)處理,這其中增加的系統(tǒng)調(diào)用和減少的 epoll 系統(tǒng)調(diào)用加起來,有誰能說一定就能明顯地快起來呢?

            實(shí)際上,epoll_wait 的效率是 O(ready fd num) 級(jí)別的,因此 edge-trigger 模式的真正優(yōu)勢在于減少了每次 epoll_wait 可能需要返回的 fd 數(shù)量,在并發(fā) event 數(shù)量極多的情況下能加快 epoll_wait 的處理速度,但別忘了這只是針對(duì) epoll 體系自己而言的提升,與此同時(shí) user app 需要增加復(fù)雜的邏輯、花費(fèi)更多的 cpu/mem 與其配合工作,總體性能收益究竟如何?只有實(shí)際測量才知道,無法一概而論。不過,為了降低處理邏輯復(fù)雜度,常用的事件處理庫大部分都選擇了 level-trigger 模式(如 libevent、boost::asio等)

            結(jié)論:
            • epoll 的 edge-trigger 和 level-trigger 模式處理邏輯差異極小,性能測試結(jié)果表明常規(guī)應(yīng)用場景 中二者性能差異可以忽略。
            • 使用 edge-trigger 的 user app 比使用 level-trigger 的邏輯復(fù)雜,出錯(cuò)概率更高。
            • edge-trigger 和 level-trigger 的性能差異主要在于 epoll_wait 系統(tǒng)調(diào)用的處理速度,是否是 user app 的性能瓶頸需要視應(yīng)用場景而定,不可一概而論。

            歡迎就此話題進(jìn)行深入調(diào)研、討論!

            參考資料:
            • linux kernel source:fs/eventpoll.c
            • “Comparing and Evaluating epoll, select, and poll Event
            Mechanisms”:http://bcr2.uwaterloo.ca/~brecht/papers/getpaper.php?file=ols-2004.pdf
            • “Edge-triggered interfaces are too difficult?”:http://lwn.net/Articles/25137/

            By QingWu


            posted @ 2013-02-25 13:05 麒麟子 閱讀(7777) | 評(píng)論 (1)編輯 收藏

            ID Tech 5 中"Megatexture"針對(duì)地形的D3D9 基本實(shí)現(xiàn)原理

            ID Tech 5 中"Megatexture"針對(duì)地形的D3D9
            基本實(shí)現(xiàn)原理
            姚勇
            H3D
            2007-8
            本文對(duì)ID SOFTWARE 使用的"megatexture"技術(shù),針對(duì)地表貼圖應(yīng)用的D3D9 的硬件實(shí)
            現(xiàn),做了概要式的介紹。并對(duì)技術(shù)的實(shí)現(xiàn),優(yōu)化,擴(kuò)展,和實(shí)用性進(jìn)行了刨析。轉(zhuǎn)載請注名
            作者和H3D。
            一,綜述
            ID TECH5是PC 3D游戲之父和DOOM之父John Carmack最新推出的一項(xiàng)新的游戲制作
            技術(shù)。核心內(nèi)容為一種命名為"Megatexture"的動(dòng)態(tài)貼圖管理技術(shù)。實(shí)際上是一種動(dòng)態(tài)卸載和
            裝載渲染資源的技術(shù)統(tǒng)稱。Idsoftware DOOM3引擎的lighting/shadow/shader方案統(tǒng)一以后,
            同UNREAL3一樣,面對(duì)次世代游戲?qū)Y源的無窮盡需求,需要盡快建立起一種實(shí)際工程技術(shù)
            方案,加快構(gòu)建3D虛擬現(xiàn)實(shí)的生產(chǎn)。

            image
            圖片來源:Quake Wars
            這個(gè)技術(shù)最主要方向就是要把VR3D建造從有限的顯卡內(nèi)存中釋放出來。把美術(shù)從繁重
            的貼圖重用繪制中解脫出來。因?yàn)閂R3D主要資源消耗都在貼圖上,所以這個(gè)技術(shù)主要針對(duì)
            texture。同時(shí)geometry也可以作為動(dòng)態(tài)資源。一旦rendering相關(guān)資源作為動(dòng)態(tài)流進(jìn)行管理,
            所帶來的另一個(gè)好處就是可以讓VR世界變?yōu)閯?dòng)態(tài)的。隨時(shí)改變texture/geometry。
            總得來講,ID這項(xiàng)針對(duì)工程應(yīng)用技術(shù)主要致力于解決:1-3DVR資源需求的爆炸增長和
            有限顯卡顯存的矛盾;2-美術(shù)資源需求的爆炸增長,以及生產(chǎn)力低、成本高昂的矛盾
            在ID TECH5演示中,實(shí)時(shí)演算播放了一個(gè)細(xì)節(jié)非常豐富的室外地形,同時(shí)結(jié)合了一個(gè)
            小型的室內(nèi)場景。所有模型和貼圖制作,按照Carmack介紹,只讓幾個(gè)美術(shù)花了4天時(shí)間。
            就制作出20G的游戲美術(shù)資源。而游戲引擎負(fù)責(zé)把幾十G的資源動(dòng)態(tài)裝載到顯卡進(jìn)行渲染。
            本文就以下幾個(gè)問題做初步講述:1,綜述地形渲染以及貼圖技術(shù);2,Clipmap介紹;
            3,基于D3D9的解決方案描述;4,優(yōu)化和擴(kuò)展;5,方案可行性
            1-1

            Mega
            Texture
            Megatexture 的具體實(shí)現(xiàn),在DOOM3 中的地形繪制已經(jīng)加入。具體實(shí)現(xiàn)細(xì)節(jié)簡單描述
            如下:
            DOOM3 的地形貼圖最大支持32768x32768 。即5.46 G。在DOOM3 中,以128x128
            大小的塊進(jìn)行儲(chǔ)存便于快速讀取。以最大2048x2048 的clipmap 尺寸進(jìn)行處理。其實(shí)可以計(jì)
            算出在D3D9 標(biāo)準(zhǔn)的顯卡上,normalmap+6 clipmap stack 的最大理論支持貼圖數(shù)據(jù)大小為
            (2048 * 2^6 )^2 *4= 68T bytes. 這個(gè)大小針對(duì)現(xiàn)在的PC 硬盤是綽綽有余的。
            DOOM3 使用這張全局metatexture 紋理渲染地形,不使用LOD,GEOM-MORPHING。
            1-2
            ,
            傳統(tǒng)
            地形貼圖渲染
            當(dāng)使用Tiling 方式繪制貼圖,等于把貼圖圖素(texel)密度成倍擴(kuò)大。以重復(fù)紋理信息量
            的代價(jià)保證在一定分辨率下屏幕上貼圖的精細(xì)程度。在繪制大范圍地形時(shí),使用不Tiling 的
            全局貼圖無法表現(xiàn)貼地處的地表細(xì)節(jié)。所以使用一些可以重復(fù)(Tiling)的不規(guī)則圖案的貼圖
            來進(jìn)行模擬。
            1-2-1
            Tiling
            地形貼圖
            由于地形貼圖尺寸很大,所以無法使用全局貼圖。而是把多層Tiling 的紋理使用alpha
            通道互相融合起來。諸如WOW[1],天堂2 等大型室外地形渲染多采用此技術(shù)。此技術(shù)在每
            一層Tiling 貼圖依然用到了一張全局alpha 貼圖。以及一張全地形唯一的靜態(tài)光影貼圖。在
            multitexture 中根據(jù)顯卡的multitexture 處理單元數(shù)量,進(jìn)行multi-pass 和multitexture 的混合
            渲染。
            比如,混合綠草,土路,野花的一塊室外地形,需要至少3 層地表貼圖。分別是一塊
            256x256 的草貼圖,128x128 的土路貼圖,和128x128 的野花貼圖。每層紋理使用Tiling 模
            式進(jìn)行繪制,在繪制的同時(shí),每層紋理帶有一張ALPHA 灰度圖(第一層不用)。用來表明
            本層紋理在融合中所占的比重。把3 張地表貼圖,2 張ALPHA 貼圖,以及一張LIGHTMAP
            貼圖混合起來,就有如下地表效果??梢钥闯鐾谅泛筒莸刂g的均勻過渡。

            image
            圖片來源:H3D
            如果進(jìn)行優(yōu)化,4 層ALPHA 可以混合稱為一張貼圖。Lightmap 一張。那么也至少需要
            2 張全局貼圖進(jìn)行地表繪制。
            1-2-2
            全局地表紋理混合Tiling
            細(xì)節(jié)貼圖[2]
            在更大廣度的地形渲染,有時(shí)候需要一種更加快速的方法。在FAR CRY 引擎中,使用
            了這個(gè)技術(shù)。簡單描述為,使用一張全局地表紋理,渲染地表所有植被和光影信息。在離視
            點(diǎn)近的范圍內(nèi),使用一張表現(xiàn)當(dāng)?shù)氐乇砑?xì)節(jié)的貼圖,以Tiling 方式進(jìn)行融合疊加。這樣在細(xì)
            節(jié)度上,以Tiling 方式的貼圖以高細(xì)節(jié)圖素紋理,掩蓋了下面那一層全局貼圖的粗糙顆粒。
            總的來講,分層Tiling Alpha 混合地表貼圖,和全局地表混合Tiling 細(xì)節(jié)貼圖,兩種方
            法都是靠貼圖的Tiling 來增大視點(diǎn)附近貼圖圖素的密度。防止比屏幕分辨率小的2 圖素之間
            進(jìn)行線性差值。影響真實(shí)感。
            二,Clip
            maps
            基本介紹
            針對(duì)受顯卡顯存制約,無法使用大尺寸貼圖進(jìn)行繪制,1998年SGI公司發(fā)表了一篇名為
            <The Clipmap: A Virtual Mipmap>[3]的論文。論文里詳細(xì)闡述了一種以有限硬件性
            能,來實(shí)現(xiàn)相對(duì)無限大貼圖的渲染。論文是一整套硬件與軟件結(jié)合的方案。它可以實(shí)現(xiàn)以一
            平方米一個(gè)圖素,用單一貼圖進(jìn)行整個(gè)地球表面的渲染。并且結(jié)合硬件數(shù)據(jù)傳輸帶寬,以及
            相應(yīng)優(yōu)化方法,魯棒的保證以任意速度漫游時(shí)保持在60Hz(針?biāo)俾剩O旅嫦葘?duì)此技術(shù)做一定
            簡要介紹。在第三節(jié)給出基于D3D9硬件的解決方案思路。下圖為Clipmap實(shí)現(xiàn)的美國國家地
            圖實(shí)時(shí)漫游的一小部分。美國約塞米蒂國家公園(Yosemite National Park)南部的一半。整個(gè)
            美國使用一張170G的貼圖。在顯卡中的clipmap貼圖緩存為16M。這個(gè)貼圖內(nèi)存用量在今天
            的PC硬件上是完全可以實(shí)現(xiàn)的。

            image
            圖片來源:<The Clipmap: A Virtual Mipmap>
            2-1
            ,
            Mipmap
            工作原理及其分析
            我們先從mipmap[4]工作原理分析開始。如圖:

            image
            圖片來源:<The Clipmap: A Virtual Mipmap>
            Mipmap 的工作原理是,把一張貼圖按照2 的倍數(shù)進(jìn)行縮小。直到1X1。把縮小的圖都
            儲(chǔ)存起來。在渲染時(shí),根據(jù)一個(gè)象素(pixel,注意pixel 和texel 區(qū)別。Pixel 是屏幕上一個(gè)點(diǎn)。
            Texel 是貼圖上一個(gè)圖素)離眼睛位置的距離,來判斷從一個(gè)合適的圖層中取出texel 顏色賦
            給象素。D3D 和OGL 都有想對(duì)應(yīng)的API 控制接口。如下圖

            image
            圖片來源:<The Clipmap: A Virtual Mipmap>
            通過觀察mipmap 工作原理我們可以發(fā)現(xiàn),硬件總是根據(jù)眼睛到目標(biāo)的距離,來選取最
            適合當(dāng)前屏幕象素分辨率的圖層。假設(shè)有一張32768x32768 mipmap 貼圖。當(dāng)前屏幕分辨率
            為1024×1024。眼睛距離物體比較近時(shí),mipmap 最大也只可能從1024×1024 的mipmap
            圖層選取texel。再次,當(dāng)使用三線性過濾(trilinear)時(shí),最大也只訪問到2048x2048 的圖層選
            取texel,來和1024x1024 圖層中的圖素進(jìn)行線性插值。
            這樣一個(gè)基本事實(shí)告訴我們,在使用任何尺寸貼圖渲染任意距離的物體時(shí),貼圖采樣只
            會(huì)用到不大于屏幕分辨率2 倍的mipmap 層。根據(jù)這個(gè)事實(shí),我們可以創(chuàng)建clipmap 對(duì)大尺
            寸貼圖進(jìn)行渲染時(shí)的優(yōu)化。
            2-2

            Clipmap
            原理
            2-2-1
            Clipmap
            構(gòu)建
            在原始clipmap 論文中,clipmap 在顯卡內(nèi)存中的構(gòu)建如圖:
            image

            圖片來源:<The Clipmap: A Virtual Mipmap>
            Clipmap Pyramid(clipmap 棱椎)是我們PC 顯卡中常見的mipmap 形式。Clipmap Stack
            是不同于mipmap 實(shí)現(xiàn)的地方。橫線繪制的棱錐代表完整貼圖的mipmap 形式。但是在顯存
            中,大于clipmap Pyramid 最高層尺寸的貼圖,都以clipmap pyramid 尺寸存放,但是相應(yīng)的,
            clipmap stack 中的貼圖代表的原始貼圖面積,一層比一層小。比如clipmap pyramid 最大
            2048x2048。在之上一層,mipmap 表達(dá)是4096x4094 圖素。在clipmap stack 中,還是儲(chǔ)存
            2048x2048 大小的貼圖。顯然此代表的就是mipmap 尺寸4096 的一半邊長,即原始面積的
            1/4大小。依次推上去,每層都是下層代表面積的1/4。Clipmap pyramid最大邊長即為Clip
            size
            。
            Clipmap 貼圖的中心點(diǎn),在整張貼圖的位置坐標(biāo),我們稱為ClipCenter
            。ClipCenter 由
            當(dāng)前攝像機(jī)在整個(gè)世界中的相對(duì)位置來決定。
            實(shí)際中,目前D3D9 級(jí)別的PC 顯卡硬件并不支持這種儲(chǔ)存形式的mipmap。具體實(shí)現(xiàn)
            方法將在第三章講解。
            2-2-2
            渲染Clipmap
            在渲染中,針對(duì)一個(gè)Pixel,根據(jù)當(dāng)前Pixel,找到相應(yīng)的clipmap 層,如果處于clipmap
            pyramid,直接按照mipmap 傳統(tǒng)方式采樣取圖素。如果處于clipmap stack,則把貼圖坐標(biāo)根
            據(jù)當(dāng)前clipmap stack 代表整體面積的比例,進(jìn)行縮放,得到對(duì)應(yīng)位置圖素。這樣就可以正確
            渲染屏幕上的任意物體。如下圖:

            image
            圖片來源:<The Clipmap: A Virtual Mipmap>
            把CLIPMAP 所有圖層繪制出來,從正上方看,就是象北京二環(huán)內(nèi),二環(huán),三環(huán),四環(huán)
            為分割。繪制中心區(qū)域的貼圖分辨率最大,但是代表的面積最小。繪制最外圍四環(huán)之外的貼
            圖分辨率最小。是因?yàn)榈搅薱lipmap pyramid 層,一層就代表一張全局貼圖。但是分辨率已
            經(jīng)被縮小了很多倍。而分辨率最大的clipmap stack 頂層,由于尺寸必須保持clipmap pyramid
            的最大尺寸,所以所代表的面積就小了很多。這種折中正是由于基于繪制屏幕上的任何物體
            都不可能使用大于屏幕分辨率的貼圖尺寸,所以在顯存中只提供剛剛滿足繪制在一針內(nèi),從
            一個(gè)視點(diǎn)看過去的所有場景(地形)分辨率的貼圖內(nèi)容。
            2-2-3
            clima
            p
            LOD
            計(jì)算以及每層貼圖坐標(biāo)計(jì)算
            給定一個(gè)根據(jù)ClipCenter換算過的貼圖坐標(biāo),下面需要計(jì)算出在屏幕上的一個(gè)物體的象
            素,從clipmap中選取一層,索引貼圖圖素。由mipmap LOD(LOD數(shù)在本文用俗稱層數(shù)描述)
            計(jì)算公式得知,clipmap LOD計(jì)算和mipmap一樣。并且可以保證任意pixel計(jì)算出來的climap
            層必然落在clipmap stack中。一般PC 3D顯卡硬件計(jì)算都采用Heckbe算法。簡單描述如下:
            LOD = log2[f(x, y)]; f(x,y)為屏幕x,y軸方向貼圖坐標(biāo)變化率的最大值。貼圖坐標(biāo)變
            化率計(jì)算由屏幕象素X,Y坐標(biāo)分別遞進(jìn)一個(gè)象素單位的變化投影到貼圖坐標(biāo)系得出。具體描
            述參考[5]。
            2-3
            ,存儲(chǔ)效率
            Clipmap由于采用clipmap stack和clipmap pyramid結(jié)合的方式,使用clipsize為2^m的
            clipmap,把一張2^n邊長貼圖容量從(4n+1 - 1)/3 字節(jié)下降為4m(n - m + 4/3) - 1/3 字
            節(jié)。更具體的數(shù)據(jù)如下:
            類型/邊長512 1024 4096 32768 67108864
            Full Mipmap 682KB 2.7MB 42.7MB 2.7GB 10923TB
            512 Clipmap 682KB 1.1MB 2.2MB 3.7MB 9.1MB
            1024 Clipmap 682KB 2.7MB 6.7MB 12.7MB 34.7MB
            2048 Clipmap 682KB 2.7MB 18.7MB 42.7MB 131.7MB
            可見clipmap極大降低了顯卡內(nèi)存對(duì)渲染超大容量貼圖的要求。目前D3D9的主流顯卡從
            128M到256M不等。在1024的Clipsize下,幾乎所有顯卡都可以滿足要求。
            2-4
            ,繪制整個(gè)地球表面的方案
            --
            虛擬
            clipmap
            (簡述)
            當(dāng)繪制國家或者星球表面這樣規(guī)模的實(shí)時(shí)渲染時(shí),整張貼圖尺寸為2^26 的平方。這樣
            一來,需要在顯卡硬件內(nèi)部計(jì)算的工作,諸如貼圖坐標(biāo)換算等都難以在精度為IEEE 32
            FLOAT 下正確進(jìn)行。
            在原有clipmap 基礎(chǔ)上,在U,V 方向加入一個(gè)坐標(biāo)偏移向量。在clipmap stack 的縱向方
            向,加入一個(gè)偏移數(shù)值。形成如下圖解構(gòu):

            image
            圖片來源:<The Clipmap: A Virtual Mipmap>
            系統(tǒng)只要能夠保證從圖中的3 種情況中正確尋址,就能夠保證超大貼圖的clipmap 渲染。
            三,基于D3D9
            的簡化實(shí)現(xiàn)
            從之前對(duì)Clipmap 的描述中可以看出?,F(xiàn)代D3D9 顯卡具備了渲染clipmap 的大部分功
            能。唯一欠缺的是clipmap stack 的模擬。因?yàn)閏lipmap pyramid 就是一張標(biāo)準(zhǔn)的mipmap 貼
            圖。所以D3D9 只要能夠把clipmap stack 模擬出來,使用shader model3 可以很容易的實(shí)現(xiàn)
            clipmap。
            3-1

            Multitexture
            我們利用顯卡硬件的multitexture 模擬clipstack。這是D3D9 簡化實(shí)現(xiàn)的最基本思想。
            PC 顯卡支持clip pyramid。在pixel shader 中,如果想利用clipmap stack,就必須有某種途徑
            讓pixel shader 使用除了clip pyramid 之外的貼圖。自然我們想到了使用multitexture。D3D9
            標(biāo)準(zhǔn)顯卡應(yīng)該支持最少8 層紋理單元(不支持的硬件很快會(huì)絕跡,所以可以不考慮)。假設(shè)
            我們不使用法線貼圖進(jìn)行凹凸象素光照。
            使用第8 層裝載clip pyramid,使用2048x2048 32bit DDS 貼圖。從第一層到第七層我們
            都可以使用clip stack 貼圖。這樣,分別裝載4096, 8192, 16384,32768,65536,131072,262144 邊
            長mipmap 的clipmap。每一層占用2048x2048*4*/4*1.33 /1024 /1024 = 5.32Mb 。這樣一共8
            層貼圖總共43MB 貼圖顯存,可以容納一張274TB 的貼圖。當(dāng)我們使用每層1024×1024
            貼圖時(shí),使用12MB 顯存就可以模擬68TB 的貼圖。顯然對(duì)于游戲來講綽綽有余。注意這
            里只是簡化計(jì)算,沒有把trilinear 考慮進(jìn)去。
            3-2
            ,
            Toroidal addressing
            在第四章我們將考慮優(yōu)化和動(dòng)態(tài)管理clipmap。我們知道用MULTITEXTURE 模擬的
            clipmap 只是當(dāng)前視角觀察的貼圖用量。當(dāng)攝像機(jī)移動(dòng)時(shí),clipmap 的貼圖組必須同時(shí)更新。
            除了最底層clipmap pyramid 貼圖不用更新,其他幾張clipmap stack 貼圖必須根據(jù)攝像機(jī)移
            動(dòng),改變ClipCenter 位置。然后從磁盤上把clipcenter 周圍的貼圖調(diào)入。這涉及到顯存寫入
            問題??梢韵胍姰?dāng)所有clipmap stack 貼圖同時(shí)頻繁裝載總共十幾MB 容量的內(nèi)存,對(duì)于磁
            盤IO 和顯存帶寬都是極大考驗(yàn)。所以針對(duì)clipmap 更新特點(diǎn),在貼圖尋址方面采用wrap 循
            環(huán)尋址。然后對(duì)貼圖的更新采用如下方式:

            image

            image


            圖片來源:NVSDK
            3-
            3,
            p
            ixel
            shader
            實(shí)現(xiàn)思想概要
            由于有了第二章climpa 渲染的大概描述,以及前兩節(jié)關(guān)于clipmap stack 和貼圖尋址方
            式,我們利用shader model 3 的pixel shader 功能可以直接實(shí)現(xiàn)具體算法。描述如下:
            1,計(jì)算mipmap LOD。
            2,計(jì)算光照
            3,假如應(yīng)該采樣clipmap pyramid,直接采樣,混合光照,完成
            4,假如應(yīng)該采樣clipmap stack 貼圖,重新計(jì)算貼圖坐標(biāo)
            5,根據(jù)mipamapLOD 選擇相應(yīng)的stack 貼圖。采樣,混合光照,完成
            首先,計(jì)算mipmap LOD。這里可以采用三線性插值。
            例如:
            PSOut PS_Trilinear(PSIn input)
            {
            PSOut output;
            float2 dx = ddx( input.texCoord * g_TextureSize.x );
            float2 dy = ddy( input.texCoord * g_TextureSize.y );
            float d = max( sqrt( dot( dx.x, dx.x ) + dot( dx.y, dx.y ) ) , sqrt( dot( dy.x, dy.x ) +
            dot( dy.y, dy.y ) ) );
            // 計(jì)算出
            float mipLevel = log2( d );
            float blendGlobal = saturate(g_StackDepth - mipLevel);
            float4 color0 = PyramidTexture.Sample( samplerLinear, input.texCoord );
            If (blendGlobal)//如果使用了全局clipmap pyrimad,及早退出
            {
            output.color = color0;
            }
            Else
            {//使用了clipmap stack 貼圖,計(jì)算貼圖坐標(biāo),然后動(dòng)態(tài)分支判斷應(yīng)該采樣第幾層
            貼圖
            // This fractional part defines the factor used for blending
            // between two neighbour stack layers
            float blendLayers = modf(mipLevel, mipLevel);
            blendLayers = saturate(blendLayers);
            int nextMipLevel = mipLevel + 1;
            nextMipLevel = clamp( nextMipLevel, 0, g_StackDepth - 1 );
            mipLevel = clamp( mipLevel, 0, g_StackDepth - 1 );
            // Here we need to perform proper scaling for input texture coordinates.
            // For each layer we multiply input coordinates by g_ScaleFactor / pow( 2,
            layer ).
            // We add 0.5 to result, because our stack center with coordinates (0.5, 0.5)
            // starts from corner with coordinates (0, 0) of the original image.
            float2 clipTexCoord = input.texCoord / pow( 2, mipLevel );
            clipTexCoord *= g_ScaleFactor;
            //只是簡單的雙線性采樣
            if (mipLevel==0)
            {
            //use clipStackTexture0.Sample(...)
            }
            If (mipLevel ==1)
            {
            //use clipStackTexture1.Sample(...)
            }
            }
            Return output;
            }
            3-
            4,
            D3D10 的實(shí)現(xiàn)【
            7】
            D3D10 引入了一種叫Texture Array 的功能??梢栽趐ixel shader 中直接利用一個(gè)整
            數(shù)下標(biāo)來尋找所需要的貼圖,然后進(jìn)行采樣。思路同上,只不過不用動(dòng)態(tài)分支,直接進(jìn)行數(shù)
            組索引后采樣。

            image 
            圖片來源:NVSDK
            圖中綠色部分就是texture array。
            四,一些優(yōu)化和擴(kuò)展
            Clipmap 在顯存中的存在,只是應(yīng)對(duì)某一個(gè)固定攝像機(jī)位置和角度的場景貼圖需求。當(dāng)
            攝像機(jī)移動(dòng)時(shí),必須對(duì)clipmap 貼圖進(jìn)行更新。具體更新方法第三章已經(jīng)提過。但是,在攝
            像機(jī)每一針運(yùn)動(dòng)都使用磁盤IO 和阻塞渲染管道的內(nèi)存搬運(yùn),是不具實(shí)用性的。所以在實(shí)用
            中,必須對(duì)clipmap 動(dòng)態(tài)更新進(jìn)行高度優(yōu)化。這也是此技術(shù)的核心所在。
            同時(shí),對(duì)clipmap 的一些擴(kuò)展是為了增強(qiáng)游戲的表現(xiàn)力。以下4-6 到4-9 的擴(kuò)展方面沒
            有進(jìn)行任何實(shí)際論文和實(shí)例支持,只是一種對(duì)現(xiàn)有技術(shù)擴(kuò)充的預(yù)想和估計(jì)。希望專家給與更
            深入的建議。
            4-1
            ,動(dòng)態(tài)
            clipmap
            管理
            Clipmap 的動(dòng)態(tài)管理,必須滿足幾個(gè)條件。1,攝像機(jī)移動(dòng)時(shí)的平滑實(shí)時(shí)渲染;2,不能
            對(duì)攝像機(jī)的移動(dòng)速度和位置有所限制;3,動(dòng)態(tài)裝載更新應(yīng)該是外部不可見,自動(dòng)進(jìn)行的。
            4-2

            2
            級(jí)
            cache
            由一般異步動(dòng)態(tài)裝載卸載應(yīng)用技術(shù)得知,在大數(shù)據(jù)量進(jìn)行從磁盤到內(nèi)存的交換時(shí),必須
            有足夠的緩沖。由于磁盤到內(nèi)存,內(nèi)存到顯存的帶寬大不一樣,磁盤到內(nèi)存比內(nèi)存到顯存慢
            得比較多。所以,對(duì)于從硬盤到顯卡內(nèi)存的緩沖,需要存在至少2 步CACHE。
            第一步,從磁盤到主內(nèi)存的緩沖。對(duì)clipmap stack 貼圖周邊的圖素,必須預(yù)先讀入一些
            到主存CACHE 中來。并且這個(gè)動(dòng)作應(yīng)該在整個(gè)游戲期間保持不停頓。預(yù)先讀入的數(shù)據(jù)根據(jù)
            玩家移動(dòng)方向做一定預(yù)測。
            在模擬星球表面的海量貼圖庫中進(jìn)行更新,同時(shí)還要考慮不同磁盤陣列相應(yīng)速度等等。
            所以磁盤到主存的緩沖策略非常重要。
            第二步,從主存裝入內(nèi)存,每次更新每個(gè)clipmap stack 貼圖周邊的半圈貼圖內(nèi)容。
            image

            圖片來源:<The Clipmap: A Virtual Mipmap>
            棕色區(qū)域是每次更新的內(nèi)容。
            4-3
            ,
            多線程
            IO
            控制
            在主循環(huán)中對(duì)貼圖進(jìn)行磁盤IO,解壓,然后寫入clipmap stack 貼圖緩存,會(huì)同時(shí)阻礙
            CPU 和GPU 的工作流水線。使得處理器停滯,影響效率。所以,對(duì)磁盤到主存的讀取IO,
            解壓工作,可以放在另外一個(gè)線程中進(jìn)行。寫入貼圖顯存的工作由于本身會(huì)阻斷D3D9 級(jí)別
            顯卡的流水線。但是,可以利用一些策略延緩一次性裝入所有CLIPMAP STACK 貼圖的并
            發(fā)。這將在4-6 講解。
            4-1 中提到的攝像機(jī)平滑移動(dòng)渲染,主要是指對(duì)clipmap stack 貼圖更新的步驟一定要
            短,不能極大幅度影響主渲染循環(huán)的執(zhí)行。在固定帶寬內(nèi),對(duì)clipmap stack 貼圖邊帶更新的
            數(shù)據(jù)量并不很大?;究梢员3忠粋€(gè)平滑渲染的過程。
            在WINDOWS 系統(tǒng),使用無緩沖的異步IO 模式是最適合此應(yīng)用的。且文件以硬盤簇大
            小的整數(shù)倍放置,讀取效率最快。
            4-4
            ,
            使用
            MaxTextureLOD
            在4-1 中提到,攝像機(jī)以任意速度移動(dòng),都不能影響渲染的平滑。我們采用如下策略。
            1,當(dāng)攝像機(jī)以設(shè)計(jì)速度移動(dòng)時(shí),clipmap 正常更新。
            2,當(dāng)攝像機(jī)超過設(shè)計(jì)速度,clipmap 更新速度無法趕上攝像機(jī)移動(dòng)速度。則自動(dòng)調(diào)整
            max texture LOD。Max texture LOD 的意義是clipmap 工作在最高clipmap stack 的貼圖層數(shù)。
            這個(gè)參數(shù)同時(shí)影響clipmap upload 和渲染pixel shader 中stack 選取。降低最高細(xì)節(jié)stack 意
            味著降低climap update 的負(fù)荷。
            3 當(dāng)出現(xiàn)極端情況時(shí),所有clip stack 更新都趕不上camera 移動(dòng),則只使用clipmap
            pyramid 貼圖進(jìn)行渲染。雖然圖象貼圖分辨率急劇下降,但是保證了camera 以任意速度漫游。
            4-5
            ,對(duì)地形幾何渲染擴(kuò)展
            --geometry
            clipmap
            參考GPU GEMS2 的geometry clipmap 介紹文章【6】。簡單描述為:使用clipmap 進(jìn)行
            頂點(diǎn)生成。把高度圖做為clipmap 進(jìn)行更新,然后做為vertex texture 的源。用以生成地形網(wǎng)
            格。這么做的好處是,地形網(wǎng)格削減被放入了climap 的LOD 過程中。而不用CPU 做任何
            額外計(jì)算工作。并且支持相對(duì)無限大地形。動(dòng)態(tài)地形改變。
            4-6

            double
            clipmap
            buffer
            ,無效邊帶
            4-3 指出,使用多線程把磁盤IO 和解壓縮,可以促進(jìn)CPU 和GUP 協(xié)同工作。在對(duì)磁
            盤文件進(jìn)行預(yù)讀取時(shí)有著極大好處。在對(duì)JPEG 進(jìn)行分塊讀取和REALTIME 解壓縮時(shí),可
            以在多核系統(tǒng)上獲得額外的負(fù)載減輕。
            Clipmap 原先的設(shè)計(jì),存在一個(gè)叫無效邊帶的技術(shù)。它可以使得SGI 硬件底層一邊渲染
            一邊進(jìn)行邊帶的貼圖更新。等于是主存到顯存的第0 級(jí)CACHE。PC 和有的CONSOLE 顯
            卡不具備這樣的功能。對(duì)于寫入顯存操作,都是把一張貼圖顯存LOCK 住,進(jìn)行寫入。在
            這個(gè)期間此貼圖無法進(jìn)行渲染。但是受此啟發(fā),我們可以把CACHE 的粒度放為每層
            CLIPMAP STACK 貼圖。結(jié)合多線程與DOULBE BUFFER 思想,方法如下:
            1,在需要?jiǎng)?chuàng)建clipmap stack 貼圖時(shí),對(duì)每層貼圖創(chuàng)建一個(gè)BACK BUFFER。
            2,在渲染CLIPMAP 同時(shí),多線程進(jìn)行另外一組stack texture 的更新。雖然可以預(yù)知假
            如寫入一張貼圖要阻斷整個(gè)GPU 工作管道的話,但是由于多線程把一組stack texture 按順
            序更新。所以避免了在主循環(huán)并發(fā)一次性更新整個(gè)stack texture array。如果顯卡可以同時(shí)渲
            染和寫入back buffer 的話,則效率應(yīng)該會(huì)提高。
            3,每針渲染完畢,調(diào)換STACK BACK BUFFER 到前臺(tái)。
            這個(gè)方法DOULBE 了CLIPMAP 占用內(nèi)存。但是在多核環(huán)境下,尤其顯卡支持渲染時(shí)
            并發(fā)寫入貼圖顯存功能的話,CLIPMAP 更新效率會(huì)大大提高。內(nèi)存的使用,前面進(jìn)行過計(jì)
            算,適合游戲的貼圖,采用1024 大小的DDS CLIPMAP,顯存占用不大。
            4-7
            ,支持非
            2

            N
            次方全局貼圖
            在游戲中,一個(gè)全局的世界如果只能是2 的N 次方大小,會(huì)極大限制美術(shù)工作。如果
            是正方形世界,貼圖只能是4G,16G,64G(都乘1.33)這個(gè)數(shù)量級(jí)別。考慮引入MxN 大小
            的貼圖。M 和N 都不是2 的冪。
            我們在之上取任意一點(diǎn)P,k<Px<M-k, k<Py<N-k, k=2^q(q=1,2,...),k 為接近分辨率的一
            個(gè)2 的冪整數(shù)。圍繞P 建立一個(gè)邊長為k 的正方形。以k 正方形為基礎(chǔ),創(chuàng)建clipmap。這
            個(gè)clipmap 代表在P 點(diǎn)繪制需要的貼圖內(nèi)容。
            隨著攝像機(jī)移動(dòng),原先不用更新的clipmap pyramid 也需要根據(jù)ClipCenter 移動(dòng)進(jìn)行更
            新。保持貼圖棱錐還是以2 的冪進(jìn)行更新。換句話說,除了clipmap stack 貼圖更新,clipmap
            pyramid 貼圖以及其mipmap 層,都需要更新。
            這樣一來,我們就可以用clipmap 進(jìn)行任意長寬世界貼圖的渲染。
            4-8
            ,
            支持法線貼圖進(jìn)行象素級(jí)別光照
            在ID TECH5 演示中,地表都具有凹凸貼圖象素光照。在這里無法給出確切的實(shí)現(xiàn)方法。
            只是嘗試列出可能的實(shí)現(xiàn)方案。希望更進(jìn)一步的探討。
            法線貼圖同樣使用一張全局貼圖。
            1, multipass 方法。使用第二張clipmap 裝載法線貼圖。
            2, 在更新diffuse color clipmap 時(shí),利用3 張clipmap stack 貼圖的ALPHA 通道,
            裝載法線貼圖的z,y,z。或者只裝載x,y(在pixel shader 計(jì)算z)。然后在pixel shader
            重組,進(jìn)行光照計(jì)算。2 層stack clipmap 代表一層normalmap。我們只在近距離使用
            normalmap,遠(yuǎn)處就用一個(gè)RGB(0.5,0.5,1)的藍(lán)色象素代替。在超出clipmap stack 視野區(qū)
            域使用mipmap 的trilinear 插值。
            3, 使用clipmap 其中的一層或者兩層裝載法線貼圖,因?yàn)檫h(yuǎn)距離地形景物不需要象素
            級(jí)別的光照。所以直接對(duì)近景進(jìn)行象素光照即可。凹凸象素光照消失的地方使用一些三線性
            插值,平滑過渡。
            4-9
            非地形應(yīng)用
            Clipmap 主要用于大規(guī)模地形渲染的貼圖調(diào)度。在此基礎(chǔ)上,對(duì)非地形場景的渲染,應(yīng)
            用clipmap 也稱為可能。Clipmap 實(shí)際是對(duì)貼圖數(shù)據(jù)庫進(jìn)行了一種線性組織和查找??梢愿?
            據(jù)簡單的x,y 坐標(biāo)以及u.v 貼圖坐標(biāo)定位所需要貼圖在clipmap texture database 的位置。地形
            應(yīng)用的天然方便之處在于,地形網(wǎng)格的空間位置本身就說明了其貼圖紋理在clipmap texture
            database 中的位置。
            根據(jù)此思想,如果能夠在場景中固定幾何物體信息中標(biāo)致好所在clipmap 數(shù)據(jù)庫中的位
            置。并且有效組織場景中的幾何體貼圖存放位置也和其世界空間位置相關(guān)。這樣就能在幾何
            體與clipmap 之間建立起一種聯(lián)系。隨著攝像機(jī)的移動(dòng),clipmap 貼圖不斷更新,相應(yīng)場景
            中的物體就被賦上材質(zhì)。當(dāng)視角遠(yuǎn)離物體,此物體貼圖不再被使用,從顯存中清除。這樣就
            實(shí)現(xiàn)了非地形的海量幾何物體貼圖的clipmap 管理。
            此思想只是停留在思考階段。需要在今后的工作中實(shí)踐。
            五,是否值得
            5-1
            ,地形渲染精度和容量
            我們來計(jì)算一下實(shí)際游戲大概需要的貼圖容量。
            假設(shè)地形,高度圖的一個(gè)象素代表一個(gè)格子。一個(gè)格子代表現(xiàn)實(shí)一米。屏幕分辨率
            r=1024x1024。人眼離地面最低距離d=1 米。FOV=90。離地面最近距離為人眼直視下方。根
            據(jù)透視關(guān)系,可以推算出在屏幕上2 米的格子將會(huì)撐滿整個(gè)屏幕。
            一張非TILING 的貼圖,平鋪在地下,我們可以計(jì)算出,如果需要屏幕的一個(gè)象素恰好
            被一個(gè)貼圖圖素覆蓋(mipmapLOD=0)。則需要2x2 的格子貼一張1024 非TILING 的貼圖。
            對(duì)于一個(gè)格子貼512 象素才不會(huì)被filter 進(jìn)行插值模糊。這個(gè)貼圖細(xì)節(jié)度是非常驚人的。
            按這個(gè)計(jì)算, 要一張全局RGB24BIT 貼圖貼滿1 平方公里的區(qū)域, 就需要
            1024*1024*512*512*3*1.33 字節(jié)= 1Tb 的RGB 貼圖。粗略估計(jì)JPEG 大概為40-50G 之間。
            但是1T 的海量貼圖相當(dāng)于一張1024x1024 圖的26 萬倍,這基本不太可能由美術(shù)手工完成。
            而且,一公里的距離根本不足以表現(xiàn)大型世界。假設(shè)人站在512,512 的坐標(biāo),500 米的直
            線距離,景物遠(yuǎn)遠(yuǎn)沒有達(dá)到人眼視界之外。人眼在晴天看遠(yuǎn)處山脈可以有十幾公里的視界。
            就算在遠(yuǎn)處不用精細(xì)的貼圖,但是人既然能夠移動(dòng)過去,那么必然要制作這么大范圍的全局
            貼圖。利用512 象素代表一米顯然是不現(xiàn)實(shí)的。
            如果我們認(rèn)為不需要那么高的精度。觀察WOW,粗略估計(jì)可以接受的精度為,128-256
            象素代表一米(WOW 基本為256)。我們計(jì)算人眼離地1 米時(shí)一個(gè)圖素覆蓋4 個(gè)象素插值的
            情況:128*128*1024*1024*3*1.33 = 65G. JPEG 估計(jì)3G。4 平方公里就是48G。
            對(duì)于付出如此巨大代價(jià),在一平方公里之內(nèi),讓美術(shù)進(jìn)行了相當(dāng)于一萬六千多張
            1024x1024 地圖的手繪,實(shí)時(shí)渲染進(jìn)行非常復(fù)雜的CACHE 和多線程異步操作,得到的效果
            只是4 個(gè)象素模糊一個(gè)圖素的圖形細(xì)節(jié)。而且如果游戲想承載4 平方公里的地域,基本就已
            經(jīng)超出目前CONSOLE 和PC 能夠接受一個(gè)游戲容量的范圍。即便是藍(lán)光,也快到達(dá)了極限。
            而4 平方公里對(duì)次世代游戲來說,簡直是九牛一毛。有很多游戲(CRYSIS)對(duì)于表現(xiàn)幾十
            平方公里都已經(jīng)非常得心應(yīng)手。數(shù)量也在幾個(gè)G 之內(nèi)。所以單純使用clipmap 實(shí)現(xiàn)連續(xù)大型
            地形渲染,以目前硬件容量,還是比較吃力。需要其他優(yōu)化手段配合。
            接下來計(jì)算貼圖更新速度需求。假設(shè)人類行走1.5 米/秒.如果按照斜前方行走。1 米128
            圖素。Clipmap 為1024。Clipmap stack 為6 層。在一秒人類行走經(jīng)過的地域,以1024 為邊
            界,正方形邊帶圖素個(gè)數(shù)大約為1.5/sqrt(2) *128* 1024 * 2 = 2172Kb。1 秒鐘需要更新的
            圖素內(nèi)存為6x2172x3 = 39096Kb 。大約40MB。假設(shè)30Hz 刷新,一針里更新的數(shù)據(jù)為
            39096/30 = 1.3Mb。一針一MB 的數(shù)據(jù)流量,也是非??陀^。
            5-2
            ,對(duì)傳統(tǒng)地形渲染方法進(jìn)行
            STREAMING
            5-1 節(jié)對(duì)使用全局貼圖進(jìn)行大范圍渲染的數(shù)量和最小貼圖傳輸量做了估計(jì)。接下來我們
            看一下,應(yīng)用clipmap 思想,傳統(tǒng)地形渲染方法是否有可擴(kuò)展的空間。
            在1-2-1 和1-2-2 使用tiling 的兩種地形渲染方法中,都提到要應(yīng)用至少1-2 張全局地
            表紋理貼圖。在使用全局貼圖繪制過程中,完全可以借鑒clipmap 的技術(shù)。把全局alpha 和
            lightmap,或者全局diffuse 顏色圖放入clipmap 管理。由于其他圖層都是靠tiling 實(shí)現(xiàn)。理
            論上可以實(shí)現(xiàn)貼圖密度無限制,貼圖大小無限制的全面解決方案。而制作全局diffuse 顏色
            圖和制作clipmap 全局圖一樣。Alpha map 靠手工繪制。Lightmap 可以自動(dòng)生成。并不會(huì)比
            只使用一張全局地形貼圖麻煩多少。
            六,結(jié)論
            Clipmap 極大的釋放了顯存限制,把應(yīng)用對(duì)貼圖細(xì)節(jié)的無限增長需求轉(zhuǎn)化為對(duì)clipmap
            實(shí)時(shí)更新的技術(shù)解決方案。此應(yīng)用不僅可以用于大規(guī)模地形渲染,也可以應(yīng)用與大型場景中
            固定幾何物體的貼圖管理。
            Clipmap 可以做為一種空間和貼圖數(shù)據(jù)庫的對(duì)應(yīng)管理系統(tǒng)。并且在象素級(jí)別進(jìn)行LOD。
            結(jié)合geometry clipmap 的應(yīng)用,可以相對(duì)提高虛擬現(xiàn)實(shí)制作的生產(chǎn)力。
            同時(shí)Clipmap 的更新成為了一個(gè)新的瓶頸。在PC 硬件架構(gòu)上,需要極大精力進(jìn)行更新
            的優(yōu)化。
            另外,在對(duì)次世代游戲質(zhì)量和渲染精度的標(biāo)準(zhǔn)下,對(duì)clipmap 采用全局世界紋理的可行
            性做了分析,并提出了質(zhì)疑。同時(shí)結(jié)合傳統(tǒng)地形渲染方法,給出了與clipmap 相結(jié)合的可能
            性。
            來信請給puzzy4d@yahoo.com.cn,歡迎討論。
            索引
            【1】WORLD OF WOLFCRAFT,http://www.wowchina.com, Blizzard,2004
            【2】Far Cry, http://www.crytek.com/, Crytek,2007
            【3】<The Clipmap: A Virtual Mipmap>,Christopher C. Tanner, Christopher J. Migdal, and Michael
            T. Jones,Silicon Graphics Computer Systems,1998
            【4】EWINS Jp,WALLER MD,WHITE M,et a1.MIP—Map Level Selection for Texture
            Mapping[J].IEEE TRANSACTIONS ON VISUAIAZATION AND COMPUTE R GRAPHICS,
            1998,4(4)
            【5】Mipmap 映射技術(shù)中d 值計(jì)算方法的探討,韓慧健,徐振中,張誠潔,計(jì)算機(jī)應(yīng)用,第
            24 卷第12 期,2004
            【6 】Terrain Rendering Using GPU-Based Geometry Clipmaps,<GPU GEMS 2>,
            Hoope,NVIDIA,2005
            【7】NVIDIA, SDK10,clipmaps

             

            http://wenku.baidu.com/view/d20b147e5acfa1c7aa00cc42.html

            posted @ 2013-02-23 23:19 麒麟子 閱讀(1429) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題
            共38頁: First 3 4 5 6 7 8 9 10 11 Last 
            国产色综合久久无码有码| 亚洲?V乱码久久精品蜜桃| MM131亚洲国产美女久久| 久久不射电影网| 伊人久久大香线蕉综合网站| 亚洲综合精品香蕉久久网| 久久精品一区二区国产| 深夜久久AAAAA级毛片免费看| 久久婷婷色综合一区二区| 91精品国产乱码久久久久久| 久久99精品国产麻豆不卡| 99久久夜色精品国产网站| 国产叼嘿久久精品久久| 亚洲精品乱码久久久久久蜜桃不卡| 亚洲国产成人久久综合碰碰动漫3d| 久久亚洲精品国产精品婷婷| 色综合合久久天天综合绕视看| 精品久久久久久久久免费影院| 国产午夜久久影院| 无码专区久久综合久中文字幕| 人妻无码久久精品| 色综合合久久天天综合绕视看| 伊人久久久AV老熟妇色| 欧美无乱码久久久免费午夜一区二区三区中文字幕 | 亚洲婷婷国产精品电影人久久| 伊人久久大香线蕉亚洲| 精品久久国产一区二区三区香蕉| 久久久婷婷五月亚洲97号色| 国产精品久久新婚兰兰| 很黄很污的网站久久mimi色| 91精品国产色综合久久| 一本色道久久综合狠狠躁| 亚洲精品第一综合99久久| 国产69精品久久久久9999| 国产精品免费看久久久| 熟妇人妻久久中文字幕| 国产精品中文久久久久久久| 久久精品国产一区二区| 国产一区二区精品久久岳| 亚洲一本综合久久| 亚洲国产成人久久精品动漫|