實(shí)現(xiàn))
前幾年接手過幾個地方非遺文化數(shù)字化的活兒說實(shí)話一開始我是拒絕的。因?yàn)檫@類項(xiàng)目十有八九最后做成了一個能點(diǎn)的電子畫冊——幾張高清圖加一段文字說明再配點(diǎn)背景音樂交差完事。但甌繡這個題材不太一樣它是那種你必須湊近了、側(cè)著光看才能看明白門道的工藝。繡線的絲光隨著角度變化針腳的走向?qū)訉盈B疊平面的照片根本壓榨不出這種東西的味道。所以我給自己定了個目標(biāo)不做畫冊做一座能走進(jìn)去、能蹲下來看細(xì)節(jié)的3D虛擬展館。這篇就把整個基于Unity、3D技術(shù)和C#腳本層的實(shí)現(xiàn)思路、踩坑過程和我自己總結(jié)出來的經(jīng)驗(yàn)原原本本寫一遍給同在做文化數(shù)字化、虛擬展館、交互漫游系統(tǒng)的朋友當(dāng)個參考。1. 從能點(diǎn)的畫冊到能走的展館這個項(xiàng)目到底要解決什么我先說說立項(xiàng)時的判斷。非遺數(shù)字化項(xiàng)目最常見的失敗模式就是把線下展陳的思維直接搬到線上一張大圖掛在中間兩邊放說明文字用戶點(diǎn)一下翻頁。這套東西在網(wǎng)頁時代勉強(qiáng)能用但它有個致命問題——用戶沒有在場感。你沒法感受到甌繡作品的實(shí)際尺幅沒法判斷一根繡線到底多細(xì)更沒法理解一幅繡品上針腳疏密帶來的質(zhì)感差異。漫游系統(tǒng)要解決的恰恰是這個在場感缺失的問題。1.1 甌繡這門手藝的展示難點(diǎn)究竟在哪甌繡是浙江溫州的傳統(tǒng)刺繡工藝講究的是以針代筆、以線代墨針法上有齊針、接針、滾針、施針、打籽針等多種變化題材多取自花鳥、山水、人物。它的展示難點(diǎn)有三個層面。第一是尺度感一幅大幅繡品動輒一米見方繡面上一根線條可能只占幾毫米沒有參照物的情況下照片會讓人誤判大小。第二是材質(zhì)感繡線是蠶絲材質(zhì)具有強(qiáng)烈且方向性的高光同一塊繡面換一個觀察角度明暗關(guān)系完全不一樣這是照片永遠(yuǎn)表達(dá)不了的。第三是工藝層級一件繡品是底布、多層繡線疊加出來的立體結(jié)構(gòu)不是一張平整的貼圖只有從側(cè)面看才能看出堆疊的厚度。這三個難點(diǎn)決定了如果只做平面展示相當(dāng)于把這個項(xiàng)目最核心的價值全丟了。所以我在方案里堅(jiān)持用真實(shí)的3D空間來承載展品讓用戶能繞著走、能俯身看、能把鏡頭懟到繡面上。1.2 為什么是漫游而不是輪播輪播圖式的交互本質(zhì)上是一種被動接收用戶只能沿著設(shè)計(jì)者規(guī)定好的路徑一張張看下去無法建立空間記憶。而我想要的是主動探索。漫游這種形態(tài)的好處在于它把展館的空間結(jié)構(gòu)交給了用戶自己理解——序廳在哪、工藝區(qū)在哪、代表作品展墻在哪用戶走一遍就能在腦子里形成一張地圖。這種空間記憶會顯著提升信息留存這也是實(shí)體展館幾百年來一直沿用的邏輯。虛擬展館要做的就是把這套邏輯在數(shù)字世界里復(fù)現(xiàn)出來同時去掉實(shí)體場館里那些限制比如你可以瞬間飛到任何一件展品面前可以從任何角度看這在現(xiàn)實(shí)中是做不到的。明確了這個核心目標(biāo)之后后面所有的技術(shù)選型、資產(chǎn)制作、交互設(shè)計(jì)都是圍繞讓用戶在3D空間里舒適地看繡品這一件事來展開的。2. 技術(shù)棧定盤Unity加C#在這個場景里憑什么勝出確定要做3D漫游之后引擎選型就成了第一個要拍板的事。當(dāng)時擺在桌面上的一共有三個候選基于Three.js的純網(wǎng)頁方案、Unity、以及Unreal。很多人第一反應(yīng)會覺得網(wǎng)頁方案最輕量、傳播最方便但我最終選了Unity加C#的路線這個決定不是拍腦袋是逐條比出來的。2.1 三種技術(shù)路線的橫向?qū)Ρ任野旬?dāng)時評估的維度整理成了下面這張表這幾個維度基本能覆蓋展館項(xiàng)目的全部訴求。評估維度Three.js網(wǎng)頁方案Unity C#Unreal開發(fā)語言JavaScriptC#C/藍(lán)圖高精度繡面材質(zhì)表現(xiàn)中PBR需要手工接高標(biāo)準(zhǔn)PBR管線成熟很高但對本項(xiàng)目過剩編輯器可視化搭場景弱基本靠代碼布置強(qiáng)所見即所得強(qiáng)訪問便利性極好瀏覽器直開WebGL可發(fā)布需處理包體包體大網(wǎng)頁發(fā)布不友好二次開發(fā)與迭代速度中高C#上手快低編譯慢多端擴(kuò)展頭顯/桌面弱強(qiáng)一套工程多端發(fā)布強(qiáng)Three.js的優(yōu)點(diǎn)非常明確零安裝、打開網(wǎng)頁就能看。但它的短板同樣明顯——缺乏成熟的可視化編輯器展館里幾百個物件的擺放、燈光、碰撞體全靠代碼寫迭代效率極低。而且要在網(wǎng)頁端做高質(zhì)量PBR材質(zhì)和烘焙光照需要手工拼接各種渲染管線對一個以文化展示為主、團(tuán)隊(duì)里美術(shù)偏多的項(xiàng)目來說成本太高。Unreal的表現(xiàn)力確實(shí)最強(qiáng)但對這個項(xiàng)目來說是殺雞用牛刀。甌繡展館不需要電影級的實(shí)時渲染需要的是穩(wěn)定的漫游體驗(yàn)和細(xì)膩的材質(zhì)表現(xiàn)Unreal的編譯速度和包體大小反而會拖累迭代。2.2 C#腳本層在展館系統(tǒng)里到底承擔(dān)什么選Unity的核心原因之一是C#這門語言在業(yè)務(wù)邏輯層的開發(fā)效率。虛擬展館聽起來是渲染和美術(shù)的活但真正的工作量大頭在交互邏輯上。C#在這個項(xiàng)目里主要承擔(dān)四類工作漫游控制器處理WASD移動、鼠標(biāo)視角、手柄和觸屏輸入的統(tǒng)一抽象、展品交互系統(tǒng)射線拾取、懸停高亮、詳情面板彈出、數(shù)據(jù)驅(qū)動層把展品信息從配置里讀進(jìn)來綁定到對應(yīng)的3D物件上改內(nèi)容不用改代碼、以及多端適配層根據(jù)運(yùn)行平臺切換輸入方式和畫質(zhì)檔位。舉個例子我把展品的所有信息都放在一個ScriptableObject列表里每件展品用唯一的ID對應(yīng)一個場景中的物件。這樣運(yùn)營方后期想改某件繡品的文字說明只要在編輯器里填表格就行完全不需要程序員介入。這種數(shù)據(jù)和表現(xiàn)分離的設(shè)計(jì)思路是C#這個層級最能發(fā)揮價值的地方也是Three.js方案里要靠后端接口才能勉強(qiáng)做到的。3. 甌繡展品的3D資產(chǎn)生產(chǎn)鏈路從實(shí)物到可交互模型引擎定好之后最難啃的骨頭來了——繡品模型的制作。這是整個項(xiàng)目的技術(shù)核心也是最容易翻車的地方。一件平面的刺繡作品做成3D模型聽起來簡單實(shí)際上要考慮的問題比做一個人物角色還多。3.1 實(shí)物采集掃描和拍照建模怎么選采集環(huán)節(jié)我試過兩種主流方案。第一種是結(jié)構(gòu)光掃描用結(jié)構(gòu)光相機(jī)對繡品做逐面掃描優(yōu)點(diǎn)是能拿到比較準(zhǔn)的幾何表面缺點(diǎn)是結(jié)構(gòu)光對高反光表面非常不友好。繡線的絲質(zhì)高光會導(dǎo)致掃描點(diǎn)云出現(xiàn)大量空洞和噪聲特別是滾針這類反光強(qiáng)烈的針法區(qū)域掃出來的數(shù)據(jù)基本沒法用。第二種是基于多視角照片的攝影測量建模用一圈照片反算出表面幾何和紋理對反光有一定的容錯只要拍攝時打勻光、避免直射高光就能拿到可用的紋理。最終我采用的是混合方案幾何結(jié)構(gòu)上用攝影測量拿大形繡面的高頻細(xì)節(jié)不依賴幾何而是靠法線和置換貼圖來還原。原因很簡單——繡線的凹凸本身尺度太小如果真把它做成幾何一個繡面會產(chǎn)生幾十萬個面性能直接崩掉。用貼圖假造凹凸是性價比最高的做法。提示拍攝繡品時一定用柔光箱或者漫反射光源避免出現(xiàn)硬陰影和鏡面高光斑點(diǎn)否則反算出來的紋理上會留下洗不掉的亮斑。3.2 繡面材質(zhì)的PBR還原絲光、底布和法線材質(zhì)是這個項(xiàng)目最花心思的部分。甌繡的視覺特征可以拆成三塊底布是一層偏啞光的織物繡線是一層帶方向性高光的絲質(zhì)材料兩者疊在一起還有厚度差異。我在Unity里用的是標(biāo)準(zhǔn)PBR管線重點(diǎn)調(diào)三個參數(shù)。第一個是Smoothness光滑度。繡線區(qū)域給到0.6到0.75之間底布區(qū)域壓到0.2左右這樣側(cè)光一掃繡線會有明顯的高光流動底布則保持沉穩(wěn)。第二個是法線貼圖的強(qiáng)度。針腳的方向性很強(qiáng)齊針是一排排平行的線條施針則是斜向交錯。法線貼圖必須按照這些真實(shí)針腳的走向來做不能隨便找一張布料法線糊上去否則近看就是一片亂七八糟的噪點(diǎn)。我的做法是讓美術(shù)對照實(shí)物照片手工繪制針腳的走向圖再轉(zhuǎn)成法線。第三個是各向異性高光。繡線的絲光其實(shí)是各向異性的——沿著線的方向高光弱垂直方向高光強(qiáng)。Unity標(biāo)準(zhǔn)著色器不直接支持各向異性我通過自定義著色器疊加了一層方向性的高光計(jì)算來實(shí)現(xiàn)代價是DrawCall會多一些但近景觀察時的質(zhì)感提升非常明顯。3.3 模型減面與拓?fù)湔碚桂^里如果放了二三十件繡品每件模型的面數(shù)必須嚴(yán)格控制。我的經(jīng)驗(yàn)值是單件展品的主體網(wǎng)格控制在五千到兩萬面之間遠(yuǎn)看的大幅掛墻繡品甚至可以壓到兩千面以下靠法線貼圖撐細(xì)節(jié)。減面的時候有個坑要注意不要用自動減面工具直接糊那樣會導(dǎo)致繡品的邊框和裝裱結(jié)構(gòu)扭曲。正確做法是把繡品拆成裝裱框架和繡面兩部分分別處理框架結(jié)構(gòu)規(guī)整可以用硬邊低模繡面是平面可以用高密度網(wǎng)格保留輪廓再統(tǒng)一減面。展品類型建議面數(shù)場景位置觀察距離大幅掛墻繡品2000-8000主展墻中遠(yuǎn)距離中型立式展品8000-20000獨(dú)立展臺中近距離小型可把玩繡片20000-40000互動體驗(yàn)臺近距離4. 展館空間動線與漫游手感導(dǎo)航、碰撞和視角設(shè)計(jì)模型都準(zhǔn)備好了接下來是搭建空間和設(shè)計(jì)漫游體驗(yàn)。這個環(huán)節(jié)直接決定了用戶是逛得舒服還是逛得暈頭轉(zhuǎn)向。我見過很多虛擬展館項(xiàng)目空間做得金碧輝煌但一走起來就頭暈、卡墻、掉出場景體驗(yàn)極差。漫游的第一原則是讓用戶感覺不到系統(tǒng)的存在。4.1 展館平面布局與參觀動線我給甌繡展館設(shè)計(jì)的是一條環(huán)形動線從入口序廳開始依次經(jīng)過歷史沿革區(qū)、針法工藝區(qū)、代表作品區(qū)最后到互動體驗(yàn)區(qū)和文創(chuàng)區(qū)形成閉環(huán)。環(huán)形動線的好處是不會讓用戶產(chǎn)生我是不是走過頭了的焦慮逛完一圈自然回到起點(diǎn)附近??臻g尺度上有個反直覺的點(diǎn)虛擬展館的通道要比實(shí)體展館更寬。實(shí)體里人走路有真實(shí)的慣性參照而虛擬漫游里用戶對速度的感知是失真的通道太窄很容易頻繁撞墻。我的經(jīng)驗(yàn)值是主通道寬度至少三米展品之間的間隔至少兩米給人留出足夠的視角調(diào)整空間。4.2 第一人稱漫游的移動與視角實(shí)現(xiàn)移動方式上我提供了兩套方案第一人稱自由漫游和軌道環(huán)繞觀察。自由漫游適合逛展軌道環(huán)繞適合看單品。自由漫游的控制器用簡單的CharacterController就能實(shí)現(xiàn)下面是一段核心邏輯的簡化版本。public class VisitorController : MonoBehaviour { public float moveSpeed 3.0f; public float mouseSensitivity 2.0f; private CharacterController controller; private float pitch 0f; private float gravity -9.81f; private Vector3 velocity; void Start() { controller GetComponentCharacterController(); Cursor.lockState CursorLockMode.Locked; } void Update() { // 視角旋轉(zhuǎn) float mouseX Input.GetAxis(Mouse X) * mouseSensitivity; float mouseY Input.GetAxis(Mouse Y) * mouseSensitivity; pitch - mouseY; pitch Mathf.Clamp(pitch, -80f, 80f); transform.Rotate(Vector3.up * mouseX); Camera.main.transform.localRotation Quaternion.Euler(pitch, 0f, 0f); // 移動 float h Input.GetAxis(Horizontal); float v Input.GetAxis(Vertical); Vector3 move transform.right * h transform.forward * v; controller.Move(move * moveSpeed * Time.deltaTime); // 重力 if (controller.isGrounded velocity.y 0) velocity.y -2f; velocity.y gravity * Time.deltaTime; controller.Move(velocity * Time.deltaTime); } }有一點(diǎn)必須強(qiáng)調(diào)鼠標(biāo)Y軸也就是俯仰角一定要做角度鉗制我限制了±80度避免用戶把頭翻到完全倒過來導(dǎo)致畫面失控。這個約束看似小但沒有它用戶猛拉鼠標(biāo)的時候體驗(yàn)會非常糟糕。4.3 碰撞體與導(dǎo)航網(wǎng)格的取舍碰撞體這塊展館里可交互的物件多如果每個都掛精細(xì)的Mesh Collider物理開銷會很大。我的做法是分層處理墻壁、地板、大型展臺這些靜態(tài)物件用Box Collider簡單又高效只有需要精確交互的小型展品才用Mesh Collider而且要用減面后的低模碰撞網(wǎng)格。這樣既保證了漫游時不會被卡住也保證了交互拾取的精度。至于導(dǎo)航網(wǎng)格NavMesh我一開始想用它給展館加個自動導(dǎo)覽功能讓虛擬向?qū)е脩艄?。后來發(fā)現(xiàn)展館是開放動線自動尋路的意義不大反而增加了烘焙和維護(hù)成本就砍掉了。這里分享一個判斷標(biāo)準(zhǔn)只有當(dāng)空間復(fù)雜、路徑明確時導(dǎo)航網(wǎng)格才值得投入像這種環(huán)形開放展館用戶自由漫游體驗(yàn)更好。5. 展品交互層點(diǎn)選、懸停與詳情面板漫游能讓用戶走進(jìn)來但真正傳遞信息的是展品交互層。用戶在展館里的核心行為就是靠近一件繡品點(diǎn)一下看它是什么。這個交互鏈路聽起來簡單要做好卻有不少門道。5.1 基于包圍盒和射線的拾取方案拾取的本質(zhì)是從攝像機(jī)發(fā)出一根射線檢測它撞到了哪個展品。Unity里最直接的做法是Physics.Raycast配合Collider。但這里有個性能陷阱如果場景里有幾十上百個帶碰撞體的物件每幀對每個物件都做一次精細(xì)檢測開銷會累積起來。我用了兩級篩選。第一級用Renderer的包圍盒做粗略排除——每幀計(jì)算相機(jī)視錐和每個Renderer包圍盒的關(guān)系只有進(jìn)入視錐的物件才參與后續(xù)檢測。第二級再用射線做精確命中判斷。這樣即使展品多每幀真正參與射線檢測的也只有視野內(nèi)的那幾個。void Update() { if (Input.GetMouseButtonDown(0)) { Ray ray Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit, 100f, exhibitLayer)) { ExhibitItem item hit.collider.GetComponentExhibitItem(); if (item ! null) infoPanel.Show(item.data); } } }注意最后那個exhibitLayer參數(shù)我用LayerMask把射線限制在展品層這樣地面和墻壁再厚也不會被誤判成展品交互邏輯干凈很多。5.2 懸停高亮與描邊效果光有點(diǎn)選還不夠用戶得先知道這個東西是可以點(diǎn)的。我加了懸停高亮鼠標(biāo)移到展品上時物件輪廓會亮起。實(shí)現(xiàn)上有兩種主流方案一種是給物件加一層描邊材質(zhì)一種是縮放放大。描邊的視覺效果更專業(yè)但代價是要多渲染一遍模型??紤]到性能我的折中是只對當(dāng)前懸停的單個物件做描邊同一時刻描邊對象唯一開銷完全可接受。懸停檢測的頻率也不用每幀都跑我設(shè)成每兩幀檢測一次肉眼幾乎感知不到延遲CPU壓力卻降了一半。5.3 詳情面板與圖文數(shù)據(jù)綁定點(diǎn)開展品之后彈出的詳情面板是信息傳遞的最后一公里。這里我踩過一個坑一開始把面板的每個文字都硬編碼在預(yù)制體里結(jié)果后期運(yùn)營改文案得一件件去改預(yù)制體效率極低。后來我把所有展品數(shù)據(jù)抽成了配置表用ID綁定。[CreateAssetMenu(fileName ExhibitData, menuName Ouxiu/ExhibitData)] public class ExhibitData : ScriptableObject { public string id; public string title; [TextArea] public string description; public Sprite[] detailImages; public float scaleReference; // 實(shí)際尺寸用于比例提示 }特別說一下scaleReference這個字段。因?yàn)槭翘摂M展館用戶對展品實(shí)際大小沒概念我在面板里加了一條比例標(biāo)尺根據(jù)這個字段動態(tài)顯示本作品實(shí)際寬度約 120 厘米。這么一個小小的設(shè)計(jì)讓用戶對繡品的真實(shí)尺幅有了直觀認(rèn)知是漫游系統(tǒng)相比平面展示的一個獨(dú)有優(yōu)勢。6. 視覺表現(xiàn)和性能的拉扯陰影、光照與分辨率前面幾節(jié)講的都是功能這一節(jié)講講性能。虛擬展館最容易出現(xiàn)的翻車不是功能不全而是卡。用戶對流暢度的容忍度極低一旦掉幀再精美的場景也會被嫌棄。性能和表現(xiàn)之間的平衡是貫穿整個開發(fā)周期的持續(xù)工作。6.1 陰影問題的根因與烘焙策略我遇到的第一個大坑就是陰影。展館里燈光多如果全開實(shí)時陰影GPU直接吃不消尤其是多光源投影疊加的時候幀率斷崖式下跌。而且實(shí)時陰影在遠(yuǎn)處容易出現(xiàn)鋸齒和閃爍也就是俗稱的陰影痤瘡。我的解決方案是以烘焙為主實(shí)時為輔。整個展館的靜態(tài)光照——包括主光、環(huán)境光、展柜底光——全部烘焙到Lightmap里一次性烤好運(yùn)行時零開銷且效果細(xì)膩。只有少數(shù)需要動態(tài)變化的部分比如用戶走近展品時腳下的一圈高亮才用實(shí)時陰影而且給它單獨(dú)設(shè)置了很短的陰影距離。還有幾個具體參數(shù)值得記錄陰影貼圖分辨率不用盲目拉高主光用2048、補(bǔ)光用1024就夠了展館這種尺度再高也很難看出差別陰影級聯(lián)數(shù)用4級能覆蓋從近到遠(yuǎn)的過渡陰影距離壓到三十米以內(nèi)超出這個范圍的物件根本不投影——反正用戶看不到遠(yuǎn)景的陰影細(xì)節(jié)白白浪費(fèi)算力。6.2 LOD、批處理與遮擋剔除除開陰影模型數(shù)量也是性能大頭。展館里幾十件繡品加上建筑結(jié)構(gòu)如果不做優(yōu)化DrawCall能輕松上到幾百。我用了三板斧。第一是靜態(tài)合批。展館的墻壁、地板、裝飾這些不會移動的物件材質(zhì)相同或相近的全部合并成批次渲染DrawCall能降下一大截。第二是LOD分級。每件展品準(zhǔn)備高、中、低三檔模型根據(jù)相機(jī)距離自動切換。遠(yuǎn)看用低模湊近自動切高模用戶幾乎無感知但遠(yuǎn)處那幾十件展品的渲染壓力大幅下降。第三是遮擋剔除。展館有墻體很多展品是藏在墻后面的遮擋剔除能把它們提前剔除出渲染隊(duì)列。烘焙好遮擋數(shù)據(jù)之后相機(jī)看不到的物件就不會被提交渲染。優(yōu)化手段主要收益適用對象靜態(tài)合批降低DrawCall墻壁、地板、裝飾LOD分級降低面數(shù)與渲染壓力所有展品遮擋剔除剔除不可見物件被墻體遮擋的展品光照烘焙免除實(shí)時陰影開銷靜態(tài)場景光照6.3 分辨率與UI適配多端運(yùn)行意味著分辨率不固定UI適配必須提前考慮。我用的是Unity的Canvas Scaler參照分辨率設(shè)成1920×1080匹配模式用Match Width Or Height匹配值給0.5這樣在不同寬高比的屏幕上UI布局都不會變形。詳情面板這種需要精確排版的界面我進(jìn)一步用了錨點(diǎn)自適應(yīng)布局保證文字框能跟著屏幕拉伸。GPU方面有個要提的點(diǎn)包體里我把貼圖的壓縮格式按平臺區(qū)分。PC端用高質(zhì)量壓縮保證繡面細(xì)節(jié)移動和網(wǎng)頁端用更激進(jìn)的壓縮格式換取加載速度。同一張繡面紋理不同平臺的顯存占用能差出好幾倍這個優(yōu)化對移動端和網(wǎng)頁端尤為重要。7. 多端發(fā)布PC、網(wǎng)頁與頭顯端的適配取舍這個項(xiàng)目最終要做多端發(fā)布PC端給展廳大屏和深度體驗(yàn)用網(wǎng)頁端給線上傳播用頭顯端給沉浸式體驗(yàn)用。一套工程多端發(fā)布是所有Unity項(xiàng)目既愛又恨的地方——代碼能復(fù)用但性能和輸入方式差異巨大。7.1 WebGL打包與服務(wù)器部署網(wǎng)頁端是傳播的主力我用Unity的WebGL平臺打包。這里有個繞不開的環(huán)節(jié)服務(wù)器配置。Unity WebGL導(dǎo)出的產(chǎn)物包含.wasm、.data、.js等文件如果服務(wù)器沒配置對應(yīng)的MIME類型瀏覽器會拒絕加載頁面直接白屏。我在IIS上部署時需要在配置里補(bǔ)上這些擴(kuò)展名的映射。system.webServer staticContent remove fileExtension.wasm / mimeMap fileExtension.wasm mimeTypeapplication/wasm / remove fileExtension.data / mimeMap fileExtension.data mimeTypeapplication/octet-stream / remove fileExtension.unityweb / mimeMap fileExtension.unityweb mimeTypeapplication/octet-stream / /staticContent /system.webServer除了MIME類型還要開啟壓縮。Unity打包時可以選擇gzip或Brotli壓縮配合服務(wù)器端的壓縮模塊包體體積能降一半以上。另外包體的加載進(jìn)度一定要做可視化——網(wǎng)頁端初次加載動輒幾十兆用戶盯著白屏幾十秒會直接關(guān)掉加個和甌繡主題一致的加載動畫等待體驗(yàn)會好很多。7.2 頭顯端的適配要點(diǎn)頭顯端我評估過主流的幾款安卓系頭顯。VR模式下的核心約束是幀率必須穩(wěn)定在72以上一旦掉幀用戶會立刻產(chǎn)生眩暈感。這對渲染優(yōu)化提出了更高要求我的做法是在VR模式下強(qiáng)制啟用最低畫質(zhì)檔——關(guān)掉大部分實(shí)時陰影、降低LOD切換距離、減少后處理效果用表現(xiàn)力換幀率。輸入方式上VR用的是手柄射線交互和PC的鼠標(biāo)射線邏輯幾乎一樣只是把射線源從鼠標(biāo)位置改成了手柄朝向。這也是我一開始堅(jiān)持把交互邏輯抽象成射線拾取這個統(tǒng)一模型的好處換輸入設(shè)備時改動量很小。7.3 輕量端的現(xiàn)實(shí)取舍至于更輕量的傳播場景比如在社交平臺上做個小程序版本我的判斷是要大幅簡化不要想著把完整展館塞進(jìn)去。輕量端只保留幾個核心展品的環(huán)繞觀察就夠了漫游和復(fù)雜交互都砍掉。原因很實(shí)在輕量端的性能和包體限制太苛刻硬塞完整展館的結(jié)果就是又卡又慢還不如做個精致的單品展示體驗(yàn)。做多端適配最容易犯的錯誤就是試圖讓每個端都有完整體驗(yàn)結(jié)果哪個端都做不好。8. 開發(fā)中踩過的坑和一份經(jīng)驗(yàn)清單寫到這里該講的核心系統(tǒng)基本都覆蓋了。最后這部分我想把整個項(xiàng)目里踩過的坑單獨(dú)拎出來因?yàn)檫@些東西在官方文檔里基本找不到全是一次次調(diào)試熬出來的。8.1 材質(zhì)和光照相關(guān)的教訓(xùn)第一個坑是繡面反光過頭。剛開始調(diào)材質(zhì)的時候?yàn)榱送怀鼋z光效果把Smoothness拉得太高結(jié)果整個繡面在強(qiáng)光下變成一片白色的鏡面紋理全被淹沒。后來把高光區(qū)域收窄、底布區(qū)域壓暗讓高光只出現(xiàn)在繡線脊線上畫面才清爽起來。這個教訓(xùn)是絲光要點(diǎn)到為止不是越亮越好。第二個坑是烘焙光照和動態(tài)物件打架。有些展品我加了輕微的旋轉(zhuǎn)動畫結(jié)果發(fā)現(xiàn)它旋轉(zhuǎn)的時候投在展柜上的烘焙陰影紋絲不動顯得非常假。解決辦法是把這類動態(tài)展品單獨(dú)標(biāo)記為動態(tài)光照探針接收對象讓它的光照跟著變化雖然烘焙成本增加了但避免了穿幫。8.2 交互和性能相關(guān)的教訓(xùn)射線拾取的層級泄漏是個隱蔽的坑。有次發(fā)現(xiàn)點(diǎn)地面的磚塊也能彈出展品詳情排查了半天才發(fā)現(xiàn)是碰撞體層級沒設(shè)對地面誤入了展品層。后來我把所有可交互物件的層級統(tǒng)一管理用腳本在初始化時自動校驗(yàn)杜絕了這類問題。遠(yuǎn)距離物件的DrawCall浪費(fèi)也是常見問題。展館遠(yuǎn)端的小展品在用戶還看不清的時候就已經(jīng)全量渲染了白白吃性能。解決辦法就是前面說的LOD加遮擋剔除組合拳把近處的渲染預(yù)算省下來給真正需要看清的展品。8.3 給同類文化數(shù)字化項(xiàng)目的幾點(diǎn)建議第一文物和工藝美術(shù)類的模型細(xì)節(jié)假不了。用戶可能不懂PBR原理但他能一眼看出繡面假不假。寧可多花時間做針腳法線和材質(zhì)也不要在資產(chǎn)上偷工減料。第二交互要克制。展館不是游戲不需要花哨的特效和復(fù)雜的玩法。用戶進(jìn)來是看繡品的所有交互都應(yīng)該服務(wù)于看清、看懂這兩件事多余的動畫和音效只會分散注意力。第三數(shù)據(jù)一定要和表現(xiàn)分離。文化項(xiàng)目的內(nèi)容后期改動頻繁如果每改一段文字都要程序員參與項(xiàng)目交付后會非常痛苦。把數(shù)據(jù)抽成配置是讓項(xiàng)目能長期維護(hù)的關(guān)鍵。第四多端發(fā)布前先明確每個端的定位。PC端做深度、網(wǎng)頁端做傳播、頭顯端做沉浸各自有各自的優(yōu)化重點(diǎn)不要一套參數(shù)走到底。我個人在這個項(xiàng)目里最深的一點(diǎn)體會是虛擬展館做得再炫本質(zhì)還是個文化容器。技術(shù)只是把甌繡這門手藝端到用戶面前的手段真正讓用戶記住的還是繡面上那些細(xì)密的針腳和絲線的光澤。所以每次在性能優(yōu)化和表現(xiàn)力之間糾結(jié)的時候我都會問自己一句這個取舍會不會讓用戶看不清一根繡線如果會那就不砍。這套判斷標(biāo)準(zhǔn)幫我在無數(shù)個技術(shù)決策里找到了方向也希望能給正在做類似項(xiàng)目的你一點(diǎn)參考。