91aaa在线国内观看,亚洲AV午夜福利精品一区二区,久久偷拍人视频,久久播这里有免费视播

<strong id="fvuar"></strong>

  • <sub id="fvuar"><dl id="fvuar"><em id="fvuar"></em></dl></sub>

    1. 千鋒教育-做有情懷、有良心、有品質(zhì)的職業(yè)教育機構(gòu)

      手機站
      千鋒教育

      千鋒學(xué)習(xí)站 | 隨時隨地免費學(xué)

      千鋒教育

      掃一掃進入千鋒手機站

      領(lǐng)取全套視頻
      千鋒教育

      關(guān)注千鋒學(xué)習(xí)站小程序
      隨時隨地免費學(xué)習(xí)課程

      當(dāng)前位置:首頁  >  技術(shù)干貨  > Gpu Instancing技術(shù)

      Gpu Instancing技術(shù)

      來源:千鋒教育
      發(fā)布人:qyf
      時間: 2022-08-29 16:08:05 1661760485

        一、前言

        在3D游戲中,制作者希望能夠繪制越來越多的場景物體,比如場景中大量的植被(樹木,草,花等),能夠給玩家?guī)砀颖普娴捏w驗。但是這對對于設(shè)備(尤其是移動端)的性能是個極大的考驗,如果使用傳統(tǒng)的技術(shù),大批量渲染會導(dǎo)致drawcall增加,fps下降。unity推出了Gpu Instancing技術(shù),這對于大量相同物體的繪制提供了一個新的方案,我們嘗試在unity中使用gpu instance 技術(shù)。

        二、靜態(tài)物體使用Gpu Instance

        Gpu Instance是一種用來提高渲染大量物體效率的技術(shù),在場景里繪制越來越多的物體,這里面主要涉及兩個方面的性能瓶頸,一是cpu對gpu提交數(shù)據(jù)的次數(shù)(包括設(shè)置數(shù)據(jù)buffer,渲染狀態(tài)以及調(diào)用對渲染原語的繪制即drawcall),二是gpu上的繪制(包括頂點處理和像素繪制),隨著場景物體的提升,cpu和gpu的壓力都會上升。目前在一些典型的3D游戲的制作中,我們的經(jīng)驗值是全屏不超過10萬個頂點和200個draw call左右,不然對中端機器會有一定壓力。

        為了解決場景繪制效率這個問題,主要有以下幾種優(yōu)化方案:

        static batching: 即靜態(tài)合批,靜態(tài)合批的原理即化整為零,將多個場景物體預(yù)先合成一個大的物體進行繪制,unity5的實現(xiàn)就是整合成一個大的vbo,而不整合IBO,一次性提交vbo給gpu,然后并不是把整個vbo都繪制,而是每次需要繪制其中某個某些物體時改變IBO,選擇大vbo上的某一段進行繪制。靜態(tài)合批可以將多個小物體的繪制合并成一個大物體的繪制,減少對渲染狀態(tài)的改變,它一次并行繪制多個物體,理論上是最快的繪制方法,不過最大的缺點是因為合成新的大vbo需要耗費額外的大量內(nèi)存,同時不能渲染動態(tài)物體,因為合并vbo的時候已經(jīng)確定頂點數(shù)據(jù)了,頂點數(shù)據(jù)不能更改(例如unity5對LOD合批的實現(xiàn)也是講所有層次的lod都預(yù)先合并進去),另外一個vbo的大小是有限制的,如果物體數(shù)量過多,也會被拆成多個繪制。

        dynamic batching:動態(tài)合批,可以解決對頂點數(shù)據(jù)有變化的物體的合批,它動態(tài)的合并vbo進行提交,組建vbo的時間有消耗,為了減少這個消耗,unity對動態(tài)合批的vbo大小有限制,以致于很小頂點數(shù)的物體才有可能被動態(tài)合批。

        vertex constant instancing:Instancing 是不同于batching的另一種方案,它的原理是對于模型一致的物體,只提交原始的模型的vbo給gpu,然后將每個物體不同的屬性單獨抽出來組成buffer發(fā)給gpu,在顯卡中根據(jù)這一份vbo和每個物體不同的屬性來繪制多個物體,即一次提交,在gpu上繪制多個,對于大量同樣模型的物體繪制是一個很好的方案。vertex constant的instancing是利用頂點常量屬性來存儲這些per instance attributes,但是也需要一個大的vbo存儲所有未經(jīng)頂點變換的相同的n個原頂點數(shù)據(jù),在shader里面讀取不同的vertex constant內(nèi)容繪制不同的instance

        gpu instancing:這是最新渲染api提供的一種技術(shù),如果繪制1000個物體,它將一個模型的vbo提交給一次給顯卡,至于1000個物體不同的位置,狀態(tài),顏色等等將他們整合成一個per instance attribute的buffer給gpu,在顯卡上區(qū)別繪制,它大大減少提交次數(shù),它在不同平臺的實現(xiàn)有差異,例如gles是將per instance attribute也當(dāng)成一個vbo提交,然后gles3.0支持一種per instance步進讀取的vbo特性,來實現(xiàn)不同的instance得到不同的頂點數(shù)據(jù),這種技術(shù)對于繪制大量的相同模型的物體由于有硬件實現(xiàn),所以效率最高,最為靈活,避免合批的內(nèi)存浪費,并且原則上可以做gpu skinning來實現(xiàn)骨骼動畫的instancing。

        Unity5中實現(xiàn)instance

        unity中提供了兩種使用gpu instance的機制,自動和手動:

        自動:需要使用unity 標(biāo)準(zhǔn)的standar 或surfaceshader,然后在mat下面的instacne那里打勾,然后unity在條件合適的情況下自動instance,但是注意這種限制非常多,如不能static batch,不能liaghtmap,不能改變mat,不能帶動作,不能cull,等等,非常難,詳見https://docs.unity3d.com/Manual/GPUInstancing.html

        手動:通過使用Graphics.DrawMeshInstanced或者Graphics.DrawMeshInstancedIndirect這些底層api。

        由于unity自動的instance不穩(wěn)定且不能lightmap等等,于是我們的實現(xiàn)方案是自己用底層api去實現(xiàn)instance,并且自己去實現(xiàn)了支持lightmap和culling的instance。

        三、帶骨骼動畫的物體使用Gpu Instance

        gpuinstancing可以很好的工作在靜態(tài)物體上,例如草,樹。但遺憾的是暫時還無法對骨骼動畫使用這個特性。而我們游戲經(jīng)常使用上百個小兵單位作戰(zhàn),如果可以讓小兵使用這個特性,那么對于性能的提升無疑是很可觀的。于是有人提出了將動畫信息烘焙到貼圖中,在shader里面根據(jù)貼圖設(shè)置頂點位置,也就是我們的頂點動畫。這樣的話,模型就既可以像骨骼動畫那樣播放動作,又可以使用gpuinstancing合批了。做法也非常簡單,就是把Skinmesh變成MeshRender,然后把骨骼和動畫信息,記錄在一張貼圖上,然后把動畫的運算放到shader里。 本來這樣就可以了,但實際使用過程中卻發(fā)現(xiàn)了幾個問題。

        1.烘焙的貼圖過大,因為為了存儲浮點數(shù),必須使用rgbahalf的格式,這個格式每個像素有64個字節(jié),是真彩色的兩倍。假設(shè)一個小兵有1000個頂點,那么1s的動作就需要1000*64,也就是64000個字節(jié),而正常情況下,我們小兵在2000個頂點左右,動畫在5s以上,那么每個動畫貼圖大概就在2M以上,甚至有可能是4M。而我們有60多個兵種,這樣一算竟然有240M。雖然小米超神使用了RGMB來減少每個像素的大小,但那也高達120M的動畫貼圖了。而我們知道,原始的骨骼動畫數(shù)據(jù)其實只有幾百k左右。

        2.無法計算光照,因為法線始終保持T-pos形態(tài),在shader里面改變頂點位置的時候,無法重新計算法線。為了能夠使用正常的光照計算,必須將法線也一起烘焙。幸運的是法線都是單位向量,可以采用rgba存儲,但也需要大概1M左右的空間。

        3.沒有動畫之間的blend,為了實現(xiàn)blend,必須對兩個動作的貼圖進行采樣,然后lerp。這樣會導(dǎo)致shader里放兩張4M的貼圖,對手游來說還是不小的開銷。

        綜上所述,我最終還是采納了M神的建議,使用了烘焙骨骼信息的方案。

        來看看原理,烘焙頂點很好理解,就是把位置的值存到貼圖中。那么如何烘焙骨骼信息,然后得到頂點位置呢?首先我們要理解骨骼動畫的原理,這里引用UWA博客里面的一段話:

      圖片4

        當(dāng)然上面的描述很簡單,如果想要了解更加詳細的推倒過程,可以看Milo大神的書《游戲引擎架構(gòu)xxx》里面的蒙皮的數(shù)學(xué)這一章。

        總之,結(jié)論就是從當(dāng)前骨骼的bindpos一直左乘到根骨骼。

        代碼也非常簡單:

        for (int j = 0; j < bones.Length; j++)

        {

        GPUSkinningBone currentBone = bones[j];

        Matrix4x4 lastMat = currentBone.bindpose;

        while (true)

        {

        if (currentBone.parentBoneIndex == -1)

        {

        Matrix4x4 mat = Matrix4x4.TRS(currentBone.transform.localPosition, currentBone.transform.localRotation, currentBone.transform.localScale);

        if(rootBone.transform != go.transform)

        {

        mat = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, go.transform.localScale) * mat;

        }

        lastMat = mat * lastMat;

        break;

        }

        else

        {

        Matrix4x4 mat = Matrix4x4.TRS(currentBone.transform.localPosition, currentBone.transform.localRotation, currentBone.transform.localScale);

        lastMat = mat * lastMat;

        currentBone = bones[currentBone.parentBoneIndex];

        }

        }

        animMap.SetPixel(j * 3, k + 1, new Color(lastMat.m00, lastMat.m01, lastMat.m02, lastMat.m03));

        animMap.SetPixel(j * 3 + 1, k + 1, new Color(lastMat.m10, lastMat.m11, lastMat.m12, lastMat.m13));

        animMap.SetPixel(j * 3 + 2, k + 1, new Color(lastMat.m20, lastMat.m21, lastMat.m22, lastMat.m23));

        if (k == startFrame)

        {

        animMap.SetPixel(j * 3, k, new Color(lastMat.m00, lastMat.m01, lastMat.m02, lastMat.m03));

        animMap.SetPixel(j * 3 + 1, k, new Color(lastMat.m10, lastMat.m11, lastMat.m12, lastMat.m13));

        animMap.SetPixel(j * 3 + 2, k, new Color(lastMat.m20, lastMat.m21, lastMat.m22, lastMat.m23));

        }

        else if(k == curClipFrame1 + startFrame - 3)

        {

        animMap.SetPixel(j * 3, k + 2, new Color(lastMat.m00, lastMat.m01, lastMat.m02, lastMat.m03));

        animMap.SetPixel(j * 3 + 1, k + 2, new Color(lastMat.m10, lastMat.m11, lastMat.m12, lastMat.m13));

        animMap.SetPixel(j * 3 + 2, k + 2, new Color(lastMat.m20, lastMat.m21, lastMat.m22, lastMat.m23));

        }

        }

        最重要的部分就是生成矩陣的那里。這里有幾個注意點,一個是根骨骼可能有多個,那么你只需要將他們共同的父親放到根節(jié)點,把這個其實沒有骨骼的節(jié)點處理成默認矩陣的情況就可以。第二個是因為貼圖采樣有可能采樣到邊緣,為了防止精確度不夠引起動畫抖動,我前后各多增加了一幀,防止抖動。

        然后是shader部分:

        v2f vert(appdata v)

        {

        UNITY_SETUP_INSTANCE_ID(v);

        float start = UNITY_ACCESS_INSTANCED_PROP(Props, _AnimStart);

        float end = UNITY_ACCESS_INSTANCED_PROP(Props, _AnimEnd);

        float off = UNITY_ACCESS_INSTANCED_PROP(Props, _AnimOff);

        float speed = UNITY_ACCESS_INSTANCED_PROP(Props, _Speed);

        float _AnimLen = (end - start);

        float f = (off + _Time.y * speed) / _AnimLen;

        f = fmod(f, 1.0);

        float animMap_x1 = (v.uv2.x * 3 + 0.5) * _AnimMap_TexelSize.x;

        float animMap_x2 = (v.uv2.x * 3 + 1.5) * _AnimMap_TexelSize.x;

        float animMap_x3 = (v.uv2.x * 3 + 2.5) * _AnimMap_TexelSize.x;

        float animMap_y = (f * _AnimLen + start) / _AnimAll;

        float4 row0 = tex2Dlod(_AnimMap, float4(animMap_x1, animMap_y, 0, 0));

        float4 row1 = tex2Dlod(_AnimMap, float4(animMap_x2, animMap_y, 0, 0));

        float4 row2 = tex2Dlod(_AnimMap, float4(animMap_x3, animMap_y, 0, 0));

        float4 row3 = float4(0, 0, 0, 1);

        float4x4 mat = float4x4(row0, row1, row2, row3);

        float4 pos = mul(mat, v.vertex);

        float3 normal = mul(mat, float4(v.normal, 0)).xyz;

        v2f o;

        UNITY_TRANSFER_INSTANCE_ID(v, o);

        o.uv = TRANSFORM_TEX(v.uv, _MainTex);

        o.vertex = UnityObjectToClipPos(pos);

        o.color = float4(0, 0, 0, 0);

        o.worldNormal = UnityObjectToWorldNormal(normal);

        float3 normalDir = normalize(mul(float4(normal, 0.0), unity_WorldToObject).xyz);

        float frezz = UNITY_ACCESS_INSTANCED_PROP(Props, _Frezz);

        float3 normalWorld = o.worldNormal;

        fixed dotProduct = dot(normalWorld, fixed3(0, 1, 0)) / 2;

        dotProduct = max(0, dotProduct);

        o.color = dotProduct.xxxx * frezz;

        return o;

        }

        主要就是頂點著色器部分,我們把4x4的骨骼旋轉(zhuǎn)偏移矩陣存在貼圖里,因為最后一行是flaot4(0,0,0,1),為了節(jié)省空間,我們只存了3x4大小的矩陣,最后一行在shader里補上。然后直接將矩陣和頂點相乘,就可以得到蒙皮后的頂點位置。而且我們看到,法線也可以這么處理,就可以得到蒙皮后正確的法線。這里還有一個我沒有做的功能,就是骨骼權(quán)重,其實我將骨骼權(quán)重存進了頂點的uv2中,uv2.xy是第一根骨骼的索引和權(quán)重,uv2.zw是第二根骨骼的索引和權(quán)重,理論上需要將兩個骨骼結(jié)算的結(jié)果加權(quán)平均一下,但因為我測試發(fā)現(xiàn)精度夠了,就少采樣一次,節(jié)省點消耗。如果有需要,可以自己加上這個加權(quán)平均。

        還有一個未來需要做的,就是動畫之間的blend,需要額外增加一個變量控制blend的程度,對兩個時刻的動作分別采樣計算,然后lerp一下就可以了。

        我們看看用貼圖存儲骨骼需要的大小,假設(shè)一個小兵有25個骨骼,那么一個骨骼需要4x3個浮點數(shù),也就是3個像素,那么需要75個像素,一個1s的動畫,也只需要75*64,大概4800字節(jié)而已。而且重要的是我們不受到頂點數(shù)的限制,而一個小兵的骨骼正常情況下就是30以內(nèi),我們得到了一個可控的合理的結(jié)果。

        四、總結(jié)

        使用Gpuinstance技術(shù)能極大的提示游戲的渲染性能,讓游戲能夠渲染更多的植被和動態(tài)物體,提高玩家的游戲樂趣。

        更多關(guān)于unity游戲培訓(xùn)的問題,歡迎咨詢千鋒教育在線名師。千鋒教育擁有多年IT培訓(xùn)服務(wù)經(jīng)驗,采用全程面授高品質(zhì)、高體驗培養(yǎng)模式,擁有國內(nèi)一體化教學(xué)管理及學(xué)員服務(wù),助力更多學(xué)員實現(xiàn)高薪夢想。

      tags:
      聲明:本站稿件版權(quán)均屬千鋒教育所有,未經(jīng)許可不得擅自轉(zhuǎn)載。
      10年以上業(yè)內(nèi)強師集結(jié),手把手帶你蛻變精英
      請您保持通訊暢通,專屬學(xué)習(xí)老師24小時內(nèi)將與您1V1溝通
      免費領(lǐng)取
      今日已有369人領(lǐng)取成功
      劉同學(xué) 138****2860 剛剛成功領(lǐng)取
      王同學(xué) 131****2015 剛剛成功領(lǐng)取
      張同學(xué) 133****4652 剛剛成功領(lǐng)取
      李同學(xué) 135****8607 剛剛成功領(lǐng)取
      楊同學(xué) 132****5667 剛剛成功領(lǐng)取
      岳同學(xué) 134****6652 剛剛成功領(lǐng)取
      梁同學(xué) 157****2950 剛剛成功領(lǐng)取
      劉同學(xué) 189****1015 剛剛成功領(lǐng)取
      張同學(xué) 155****4678 剛剛成功領(lǐng)取
      鄒同學(xué) 139****2907 剛剛成功領(lǐng)取
      董同學(xué) 138****2867 剛剛成功領(lǐng)取
      周同學(xué) 136****3602 剛剛成功領(lǐng)取
      相關(guān)推薦HOT
      軟件開發(fā)管理流程中會出現(xiàn)哪些問題?

      一、需求不清需求不明確是導(dǎo)致項目失敗的主要原因之一。如果需求沒有清晰定義,開發(fā)人員可能會開發(fā)出不符合用戶期望的產(chǎn)品。二、通信不足溝通問...詳情>>

      2023-10-14 13:43:21
      軟件定制開發(fā)中的敏捷開發(fā)是什么?

      軟件定制開發(fā)中的敏捷開發(fā)是什么軟件定制開發(fā)中的敏捷開發(fā),從宏觀上看,是一個高度關(guān)注人員交互,持續(xù)開發(fā)與交付,接受需求變更并適應(yīng)環(huán)境變化...詳情>>

      2023-10-14 13:24:57
      什么是PlatformIo?

      PlatformIO是什么PlatformIO是一個全面的物聯(lián)網(wǎng)開發(fā)平臺,它為眾多硬件平臺和開發(fā)環(huán)境提供了統(tǒng)一的工作流程,有效簡化了開發(fā)過程,并能兼容各種...詳情>>

      2023-10-14 12:55:06
      云快照與自動備份有什么區(qū)別?

      1、定義和目標(biāo)不同云快照的主要目標(biāo)是提供一種快速恢復(fù)數(shù)據(jù)的方法,它只記錄在快照時間點后的數(shù)據(jù)變化,而不是所有的數(shù)據(jù)。自動備份的主要目標(biāo)...詳情>>

      2023-10-14 12:48:59
      服務(wù)器為什么要用Linux?

      服務(wù)器為什么要用Linux作為服務(wù)器操作系統(tǒng)的優(yōu)選,Linux在眾多選擇中脫穎而出。Linux作為服務(wù)器操作系統(tǒng)的優(yōu)選,有其獨特的優(yōu)勢和特點。包括其...詳情>>

      2023-10-14 12:34:11