真·千人同屏 | 虛擬世界的“煙火氣”

我們?yōu)槭裁葱枰扒送痢保?/strong>
在過去的兩年,各式各樣的虛擬場景層出不窮,有展廳展館、有展覽與音樂會、有景區(qū)古跡,但這些場景中多是寥寥數(shù)人穿梭其中,附加站在某個角落的NPC,這樣的體驗難免給人一種“單機游戲“的既視感,而千人同屏技術(shù)能夠打破這樣的既視感,是帶給擬世界臨場感的基石功能之一!

點擊體驗千人同屏
![]()

觀看虛擬演出和表演,能感受到不亞于現(xiàn)場音樂節(jié)的熱鬧氛圍

(光遇歐若拉演唱會)
體驗虛擬文旅活動,能和更多五湖四海的伙伴同游暢聊,更有現(xiàn)實生活

(逆水寒汴京夜市)
對于線上峰會、發(fā)布會等會議,上千人會議現(xiàn)場更有“人聲鼎沸”的熱議氛圍

對于線上社區(qū),大家真實的互動、分享、交流將會直觀視覺化激發(fā)用戶的參與感


千人同屏并不是全新的技術(shù),在數(shù)年前的游戲中早以可以實現(xiàn)。但是在虛擬空間中,沒有游戲化的APP載體來充分釋放性能,沒有用戶有等待游戲加載的耐心,并且在以網(wǎng)頁為主要實現(xiàn)方式的當(dāng)下,虛擬空間需要快速載入、兼顧性能,并可實現(xiàn)多端兼容(如不同app/瀏覽器/AR/VR設(shè)備...),宛如帶著鐐銬起舞。

雖有如此難度,但“千人同屏”仍被輕易地附加在各類概念化的描述之后,看似可以被輕易地實現(xiàn)。這里想分享下我們理解的“千人同屏”:

用戶可以在各式各樣的虛擬人中無限制地選擇自己喜歡的風(fēng)格。在千人同屏技術(shù)的加持下,整個虛擬世界不再是整齊劃一的虛擬人,而是像聚集了來自五湖四海的各色人兒,呈現(xiàn)千人千面、繽紛多彩的世界。


在真正的千人同屏里,用戶不再是被限制在某個位置坐下或者站立(這樣的千人萬人同屏毫無意義),而是可以自由地操作自己的虛擬形象,去四處漫游,能做各種社交互動,在熱鬧的場域下充分感受真實的人與人之間交流,人與物之間的交流。


在虛擬空間中,能夠認識新朋友遇見老朋友,是在虛擬世界中難忘的經(jīng)歷,如果此時看上去人山人海,實則只是程序操控下的機器人,那大可不必來到這里享受一個人的狂歡。

當(dāng)然還有一些文字游戲類的說法,比如“實現(xiàn)千人同屏觀看”這樣以直接偷換了概念的方式硬套千人同屏技術(shù),無不在說明千人同屏技術(shù)真實的難度。

為什么要實現(xiàn)“真·千人同屏”這么難?
在網(wǎng)頁端要實現(xiàn)同屏千人流暢的實時互動,不同的環(huán)節(jié)都有巨大的挑戰(zhàn):后端對于用戶位置信息的匯總處理、前后端通信細節(jié)的優(yōu)化、前端渲染開銷的控制......

下面具體分享下我們技術(shù)處理方案:

說到渲染性能優(yōu)化,降低 draw call 數(shù)往往是最先想到也最有效的做法。一個 draw call 是 CPU 向 GPU 發(fā)送一次繪制命令,會產(chǎn)生兩者間的一次通信開銷。一旦 draw call 過多,這些開銷積累起來,便導(dǎo)致渲染性能下降。

在以往的項目架構(gòu)中,每一個角色人物作為一個單獨的模型,渲染時都需要一次新的draw call,因此當(dāng)在線人數(shù)大量增加時,draw call 數(shù)也就線性地猛增。
而實際上角色人物的種類并不多,只有三四個,因此大部分人物都是同一個模型的重復(fù)渲染,如果可以把這些重復(fù)的模型同時渲染出來,自然就可以大大降低 draw call 了。
說到重復(fù)模型,對于3d渲染有所了解的話,自然就會想到實例化繪制(Instanced Drawing), 它可以將同樣的模型以實例化的方式合并成一次 draw call 繪制出來,是 WebGL 提供的能力,正好可以解決我們的問題。

可事情并沒有這么簡單。
我們的角色人物由用戶操控,會跑、會跳,實際上是帶動作骨骼的蒙皮模型(Skinned Mesh),這些動作互相獨立并受人物當(dāng)前活動狀態(tài)控制。而 現(xiàn)有的移動端3d框架并不支持簡單地將骨骼模型實例化,如果只是簡單將模型實例化的話,只會得到一堆無法表現(xiàn)任何動作的僵硬實例,在世界中平移。顯然這是不可接受的。

(未支持骨骼蒙皮模型時實例化繪制時效果)

(正常帶動作的人物模型)
為了解決這個問題,可以有多種處理方案,其中一種是將動畫烘焙到貼圖材質(zhì)中,這樣就可以在 GPU 代碼中通過采樣的方式獲取數(shù)據(jù)完成頂點變形;另一種是將動畫的插值計算放在 CPU 上,再實時將當(dāng)前動畫數(shù)據(jù)上傳至 GPU 處理。
前一種的優(yōu)點是性能更優(yōu),缺點是需要對動畫的特殊處理,而且 GPU 編程限制更多也更復(fù)雜;后一種方案的優(yōu)點是與現(xiàn)有的邏輯相適配,代碼上也更簡單,缺點是如果想要更加優(yōu)化性能,還需要做其他特殊處理。
這里,分享下比較簡單的處理方案。
如果我們搞清楚現(xiàn)有框架是如何實現(xiàn)非蒙皮模型的實例化、以及單個蒙皮模型的動作變形,再將這兩種能力組合起來,就可以實現(xiàn)帶動作蒙皮模型的實例化啦!

具體來說,骨骼蒙皮模型能夠渲染出動作動畫,在于它的各個頂點已經(jīng)綁定了對應(yīng)哪幾塊骨頭以及它們影響的權(quán)重。當(dāng)骨骼的位置、旋轉(zhuǎn)、大小變化時(即動作進行中),渲染時便會逐幀去取到最新骨頭的變換矩陣并將其作用到頂點位置上以改變原有的位置,這樣就形成了動畫。

(控制模型動作的骨骼)
要把這些邏輯放入實例化繪制,也需要有同樣的操作。
頂點與骨骼的綁定與單個模型是一致的,只需要復(fù)用即可,而不同的地方主要在于之前只需要從一套骨骼中去取骨頭的變換矩陣,而實例化后需要從多套骨骼中去取當(dāng)前實例對應(yīng)骨骼的變換矩陣。
因為每一個人物都有自己的動作狀態(tài),因此可以為每一個實例綁定一套骨骼,并由動畫控制器去實現(xiàn)動作的計算。之后再將這些動作數(shù)據(jù)以數(shù)據(jù)貼圖(data texture)的形式上傳至 GPU,然后在頂點著色器(vertex shader)代碼中讀取使用即可。
當(dāng)一個模型有20多塊骨頭時,每塊骨頭需要4個像素來存儲共16個數(shù)字的變換矩陣,一套骨骼可用16*16像素的貼圖且還有大量空余。即使實例數(shù)量(人物)增加至1000個,貼圖大小也不會超過512*512像素!

在新的著色器代碼中,我們?nèi)〉叫碌墓趋雷儞Q矩陣便可完成頂點變形。
完成實例化后,當(dāng)場景中有1000個角色時,draw call 數(shù)也不會像之前增加到1000,而仍然僅為1,在渲染層面大大優(yōu)化了性能!

雖然 draw call 已大為降低,但1000個模型仍是1000個模型,頂點數(shù)和模型面數(shù)都不會減少,仍然是 GPU 計算中不小的開銷。
為了減小大量模型渲染時的面數(shù),我們采用了多細節(jié)層級(LOD)的技術(shù)方案。
簡單來說,依據(jù)人物模型與相機的距離,可以選擇不同細節(jié)層級的模型。這是因為當(dāng)角色距離較遠,其精細細節(jié)本就難以分辨,這個時候再去渲染一個精細的模型也沒有必要,完全可以用一個更加粗糙的類似模型來替代。

粗糙的模型頂點數(shù)可以減少很多,而且還可以有多個層級,以更細化區(qū)分距離,達到既減少面數(shù)、又在視覺上無差別的效果。
另外,還可以在角色距離相機一定距離外完全隱藏模型,進一步優(yōu)化性能。
因為不同的層級對應(yīng)不同的模型,所以在實例化繪制時,也需要引入新的實例化模型,同時增加幾個 draw call。但這幾個 draw call 仍然遠小于此前每個實例單獨一個 draw call 時的數(shù)量,收益遠大于開銷!

雖然用實例化繪制在渲染能力上支持了1000人同時在線,但實際上在場景中不會始終都有1000人,而且因為采用了多細節(jié)層級,每個角色也可能會在幾個實例模型間切換,因此就會有實例顯隱的問題 。
因為底層實現(xiàn)的限制,想要控制實例化模型中實例的顯示還是隱藏,無法簡單在各個實例上去控制,而理論上最理想的方式是動態(tài)修改 count 屬性值。此時,實例模型中只有前面 count 這么多個實例會進入渲染流程,而后面的實例因為不參與渲染,因此性能上最優(yōu)。
我們在一開始也是這樣處理的,將場景角色的變換矩陣設(shè)定給前n個實例,然后設(shè)定好實例化模型的數(shù)量,之后當(dāng)角色退出場景或切換到其他模型時,只需要更新一個實例對應(yīng)的索引序號、并減小 count 的值即可。這樣就可以保持始終只渲染實際有效的模型。

(理想情況下實例顯隱控制)
但是在實際操作中,在切換數(shù)量及同步序號的瞬間,場景中對應(yīng)的實例偶爾會有閃動。雖然不是非常顯眼,但仍然是不穩(wěn)定的體驗。
為了解決這個問題,我們在探討權(quán)衡后決定不再瞬移實例,而是在 GPU 著色器代碼中處理實例顯隱,并控制計算復(fù)雜度。
雖然這樣性能會略微劣化一點,但好處是用戶體驗更為穩(wěn)定可靠。經(jīng)過測試,這樣雖然面數(shù)相對沒有減少,性能卻有不小的提高。

在處理實例化骨骼蒙皮模型時,我們還遇到了其他一些問題,其中包括模型影子。
因為框架只支持非實例化繪制的骨骼模型,在我們實現(xiàn)的實例化模型上,默認的獲取深度映射以渲染影子的著色器代碼無法直接處理。
因此需要再增加自定義的深度材質(zhì)并綁定自定義的著色器。此時將骨骼動畫的作用應(yīng)用在頂點位置變形上,即可以得到正確的深度映射啦!

(左:影子沒有跟隨動作變化;右:影子實時跟隨動作變化)
此外,為了陰影性能的考慮,我們也控制了陰影渲染的范圍。不過因為實例化繪制后draw call 數(shù)已大大下降,可以有更多角色有自己的影子啦!

(左:只有1個人有影子;右:四周的人都有影子)
不過陰影范圍增加同樣也造成了小問題,即陰影的邊緣過分清晰,整體效果不佳。
為解決這個問題,我們在顯示陰影的著色器代碼中加入了淡出的處理,使得整體效果更為自然。

(左:影子邊緣過分清晰;右:影子自然淡出)
此外,實例化繪制也并非都是優(yōu)化。非實例化渲染有一個優(yōu)點是視錐體剔除(Frustum Culling) 很容易實現(xiàn),當(dāng)人物不出現(xiàn)在相機視線中時,也不會再渲染。
而當(dāng)人物實例化之后,因為整個模型的邊界更難以計算,而且是整體的比較,所以視錐體剔除難以處理,如果想要實現(xiàn)類似單獨模型的剔除結(jié)果,就需要手動去控制實例的顯隱并優(yōu)化著色器代碼來控制開銷。

靈境至維團隊一直致力于打造一個既有人間煙火氣,又有無限想象力的虛實共生的世界!而真·千人同屏作為虛擬世界活力的重要基石,技術(shù)團隊也不會止步于此,會繼續(xù)去優(yōu)化它。當(dāng)然要實現(xiàn)團隊愿景,本就需要不斷地去挑戰(zhàn)這個領(lǐng)域里技術(shù)、設(shè)計、體驗等的眾多高峰。雖然很難,但對于我們,你可以期待更多!
編輯:小蕓、Neeson、久喬、山童、歐陽
技術(shù)顧問:久喬、海青
轉(zhuǎn)載請在文章開頭和結(jié)尾顯眼處標注:作者、出處和鏈接。不按規(guī)范轉(zhuǎn)載侵權(quán)必究。
未經(jīng)授權(quán)嚴禁轉(zhuǎn)載,授權(quán)事宜請聯(lián)系作者本人,侵權(quán)必究。
本文禁止轉(zhuǎn)載,侵權(quán)必究。
授權(quán)事宜請至數(shù)英微信公眾號(ID: digitaling) 后臺授權(quán),侵權(quán)必究。




評論
評論
推薦評論
暫無評論哦,快來評論一下吧!
全部評論(0條)