耿秋晚,張海波
(北京服裝學院信息中心,北京 100029)
目前服裝品牌利用智能科技打破營銷同質化成為了當下的趨勢,許多服裝品牌對虛擬現實營銷進行了嘗試[1]。虛擬現實開發技術在線上零售中可以再現購物中心、貨架、產品等,通過虛擬服裝店環境的搭建,可以為顧客營造良好的視覺氛圍。通過數字服裝延伸的虛擬陳列,可以拓寬線上展示形式、表達品牌理念,搶占消費者市場[2]。通過在服裝店中漫游功能的實現,可以增加趣味性,吸引消費者并引發其購買行為,最終達到推動服裝營銷和增加營收的目的。
早期虛擬現實在線上主要通過VRML、Java3D 等技術實現,但這些實現技術均存在需要安裝應用插件的共性問題[3],WebGL 技術解決了這個問題,它可以在所有兼容WebGL的瀏覽器中流暢地展示三維場景和模型[4]。Three.js是一個用于輔助WebGL 開發的框架和第三方庫,它簡化了Web GL 原生API中的底層細節,對3D 圖形編程中常用的對象以簡單和直觀的方式進行了封裝。Three.js功能強大,在開發過程中采用了很多圖形引擎中的高級技巧,可以極大地提高性能[5-6],有效降低開發難度和復雜度。基于Three.js實現在Web端無插件構建虛擬服裝店及漫游功能,對其中的關鍵技術進行介紹,如前期模型的數字化、基本場景的構建、碰撞檢測、人物漫游等。
1.1.1 服裝縫合模擬
服裝是由衣片相互縫合而成,虛擬服裝的生成也一樣需要在衣片之間設置一對一縫線[7]。三維服裝軟件Style3D 是一個國產3D 數字化設計和建模工具。在Style3D 中導入虛擬模特,將版片在2D 窗口進行顯示并對其擺放位置進行調整;然后在3D 窗口將版片合理放置在模特安排點上,對需要縫合的邊使用縫合工具進行虛擬縫合;經過“模擬”,服裝版片和縫紉線就會在模特身上模擬形成三維服裝。服裝的縫合模擬流程,如圖1所示。

圖1 服裝虛擬縫合模擬流程
1.1.2 服裝懸掛模擬
對上衣、連衣裙這2類服裝進行懸掛的操作流程比較簡單。在Style3D 中完成虛擬試穿之后,只需要將場景中的人體模特刪除;然后添加衣架并將衣架移動到服裝內部合適的位置,使衣架與服裝的肩縫處于同一水平,再進行模擬即可;模擬完成之后對服裝進行局部調整,直至達到理想的懸掛效果,如圖2所示。

圖2 上衣懸掛模擬過程
褲裝、半身裙這2類使用褲架進行懸掛的服裝模擬的操作流程相對比較復雜。使用上衣類型的衣架在進行模擬時,服裝會在重力的模擬作用下落在衣架上,但是如果使用褲架以衣架模擬的方法直接進行模擬,服裝會在重力的作用下直接掉落在地上。以褲裝為例,介紹使用褲架進行懸掛模擬的流程,見表1。

表1 褲裝懸掛模擬流程
1.2.1 服裝虛擬陳列展示方式
服裝陳列是一種視覺表現手法和宣傳手段,正掛陳列、側掛陳列、疊裝陳列和人模陳列是常見的4種陳列方式[8]。為使用戶在漫游逛店時可以看到服裝的全貌,充分展示服裝細節,采取正掛陳列、側掛陳列和人模陳列3種方式相結合,豐富陳列形式,增加視覺變化。在Style3D 中將懸掛模擬的服裝生成正掛陳列裝置和側掛陳列裝置,穿著模擬的服裝生成人模陳列裝置。服裝虛擬陳列展示的3種方式如圖3所示。

圖3 服裝虛擬陳列展示方式
1.2.2 虛擬零售空間布局
好的零售空間布局能夠對店鋪的整體外觀進行展示,為用戶提供最佳體驗,實現用戶的購買行為最大化。零售商開發了幾種常見的布局方式,比如簡單網格布局、回環布局、島式布局、展會布局等[9]。采用如圖4所示的展會布局方式,像展會一樣把商品像藝術品那樣布置。在店鋪的邊界處擺放商品,中間區域保持空曠,以便用戶四處走動。由于商品前沒有任何遮擋物,這種布局使可視區域最大化[9]。在Blender中對服裝店進行建模,并將從Style3D 中導出的服裝陳列裝置導入Blender,按照此布局進行擺放。由于模型要在線上進行展示,為兼顧展示效果和速度,在Blender中對模型進行輕量化處理并導出。

圖4 展會布局方式
Three.js基本場景的構建需要3 個基本的元素,即場景、相機和渲染器。場景是一個容器,主要用于保存、跟蹤所要渲染的物體和使用的光源[10],任何物體都必須添加到場景中才有可能被繪制。相機決定在場景中能夠看到什么,選擇透視投影相機,它遵循近大遠小的透視規律,更貼近人對現實生活的感知[11]。渲染器會基于相機和場景提供的信息,調用底層圖形API執行場景繪制工作。
當場景或相機發生變化時,渲染器需要重新執行渲染方法對渲染圖像進行更新,即渲染循環。渲染循環通過.request AnimationFrame()方法來實現,它以渲染函數作為參數,請求再次執行渲染函數渲染下一幀,在理想的情況下渲染幀率可以達到60 FPS。
加載的服裝店三維模型是通過Blender導出的gltf格式的壓縮模型,必須經過DRACOLoader解壓縮轉換后才能使用[12](DRACOLoader,用加載出Draco庫壓縮的幾何體的加載器)。解壓縮時首先要在解析器中設置draco文件路徑,使用gltf加載器GLTFLoader,用load()方法解析gltfJSON 文件,載入外部資源并通過回調函數返回結果[5],設置gltf加載器的draco解碼器;用加載器加載模型,并將其添加到Three.js場景中。
碰撞檢測的目的是避免出現人物在場景中漫游時,與其他模型發生穿透的問題,并提高虛擬場景的真實性。采用八叉樹碰撞檢測,它屬于空間剖分的碰撞檢測。八叉樹是一種空間數據結構[13],它基于樹型結構能夠同時在多個尺度上進行空間細分,將一個圍繞整個空間的包圍體劃分成8個更小的立方體空間,檢測小立方體空間中是否存在碰撞點,如果沒有檢測到碰撞就不再對這個立方體進行劃分;如果檢測到碰撞點就對這個立方體進行再劃分;這樣一直劃分下去,通過這種方法可以對碰撞點進行快速聚焦,檢測到空間中物體碰撞發生的位置。
3.2.1 創建人物碰撞體和場景碰撞組
創建漫游人物的碰撞體,在代碼中創建一個膠囊體對象對人物進行模擬,通過膠囊體參數的設置可以模擬出人物的身高和寬度,如圖5所示。創建碰撞組對象THREE.Group(),加載服裝店模型后通過.add()方法將需要與漫游人物進行碰撞檢測的物體添加進來,然后將碰撞組添加到場景中。

圖5 膠囊碰撞體
在Three.js中創建八叉樹空間對象,通過.from Graph Node()方法把碰撞組中所有的網格節點添加到八叉樹空間對象中,進行八叉樹的劃分,確保所有網格對象被分配到合適的節點中。在進行碰撞檢測時,可以避免對整個場景中的所有網格進行遍歷,從而有效降低碰撞檢測過程中的計算量,提高性能。
3.2.2 碰撞檢測
碰撞函數是判斷人物與物體發生的碰撞計算。在處理碰撞時涉及3個不同階段,分別是碰撞檢測、碰撞測定和碰撞響應[13]。
(1)碰撞檢測。
碰撞檢測即判斷碰撞有沒有發生。碰撞檢測是碰撞處理的第一階段,如果沒發生碰撞,就不需執行之后的階段。將人物的膠囊碰撞體作為參數,通過調用八叉樹自帶的.capsuleIntersect()方法,將膠囊碰撞體實例添加到八叉樹空間中進行碰撞檢測。
(2)碰撞測定。
碰撞測定即做出碰撞具體在什么地方出現。檢測會返回一個結果result,若result的值為false則沒有發生碰撞,若發生碰撞會返回一個碰撞的法向量和深度值。
(3)碰撞響應。
碰撞響應即做出碰撞產生的效果。場景中的碰撞檢測,最基本的就是人物站在地面上不穿透地面。當檢測到人物與地面發生碰撞時,對當前人物是否在地面上這個狀態值由false變為true,為避免發生地面穿模現象,重力加速度應該停止,并將人物垂直方向的速度變為0。由于發生碰撞人物會陷進地面一定的深度,還需要對人物的高度位置進行還原處理,避免發生地面穿模現象。將碰撞的法向量與深度相乘得到需要進行調整的距離向量,將它作為參數傳入膠囊碰撞體對象的.translate()方法中,即可還原人物位置。
漫游人物模型應是本身帶有變形動畫數據的模型。為了方便在Style3D 中直接導出obj格式的模特作為人物模型,在Mixamo網站選擇人物待機狀態、走路狀態、跑步狀態等動作將其賦予給人物;下載角色綁定使用與Blender 兼容的FBX 二進制格式;選擇With Out Skin選項下載模型,這個選項下載的模型只有骨骼沒有蒙皮,因此文件會很小。通過Mixamo生成的角色,如圖6所示。在Blender中將多個動作動畫合成在一個模型上,如圖7所示,將模型導出為gltf格式,用于在Three.js代碼中進行加載。加載時要通過GLTFLoader生成加載器對象,并通過.load方法加載路徑中的人物模型到場景中。

圖6 Mixamo網站制作動作

圖7 Blender中合成動作
第三人稱是用戶以旁觀者的視角觀察場景中的漫游人物與環境。這種視角通常處于用戶所控制的漫游人物的上方,所以第三人稱也被稱為“上帝視角”[14]。這種上帝視角往往通過相機來實現,通過相機綁定人物,跟隨人物的移動或旋轉來調整位置或角度并始終觀察場景。
4.2.1 相機綁定跟隨人物
在將人物與相機進行綁定時,為人物添加一個空的子物體,讓相機作為這個空對象的子物體,它們之間為父子綁定關系,如圖8所示。這樣設置,不但可以實現相機對人物的跟隨,而且當相機跟隨空對象一起旋轉時也不會影響人物。通過相機的位置參數設置相機與人物的位置關系,讓相機跟隨在人物后面并設置合適的高度,通過相機的.look At()方法傳入人物的位置讓相機看向人物。

圖8 綁定關系
4.2.2 相機自由視角控制
相機自由視角控制是指讓相機跟隨鼠標移動而變化,實現用戶操縱鼠標以第三人稱視角在場景中進行環視及俯視查看。在代碼中實現時,對鼠標事件進行監聽,根據鼠標在屏幕中的移動,左右旋轉人物或上下旋轉空對象。鼠標事件MouseEvent.movement X 提供當前和上一個mousemove事件之間鼠標在水平方向上的移動值,根據鼠標移動的方向進行計算。鼠標移動的距離越大,旋轉角度就越大。鼠標垂直移動實現俯仰查看時,需要設定一個旋轉臨界角度值,當超出這個角度時就固定在這個角度,避免出現旋轉到地面下或者上下翻轉的情況。最終實現的第三人稱效果如圖9所示。

圖9 第三人稱視角
通過鍵盤按鍵控制人物前后左右移動,鍵盤行為與其對應功能見表2。

表2 鍵盤綁定
根據鍵盤狀態更新人物移動的速度,根據按下的時間累加狀態,在更新函數中對控制人物移動的函數進行調用,實現人物的移動。設計按下“W”鍵時實現人物的前進,因此需對人物的前進方向向量進行設置,獲取人物前進的方向,計算人物速度時需給人物的速度加上當前前進的方向通過.multiplyScalar()方法與時間相乘,就可以實現按鍵的時間長短控制人物移動的速度(按得越久速度越快)。設計按下“S”鍵時實現人物后退,因此需在設置速度時加一個負號。
當按鍵抬起后,人物應該由移動變為停止。添加阻尼的參數設置,在鍵盤狀態為未按下的條件下,給速度添加一個反方向的力,通過.addScaled Vector()方法將本身的速度與反方向相乘,將得到的乘積加到速度向量上,實現人物移動逐漸停止的效果。
已知人物向上的方向與前進的方向,而側方方向垂直于這2個方向形成的平面,因此可以運用向量叉積運算求出人物左右的方向,如圖10所示。向上方向為(x1,y1,z1),前進方向為(x2,y2,z2),叉積運算求得側方方向公式,如式(1)所示。代碼實現時,正前方方向向量通過.cross()方法傳入正上方方向,進行叉積運算得到側方方向向量。人物移動的原理與前后移動一樣,當按下“A”“D”鍵時,可以實現人物的左右移動。

圖10 叉積運算求側方向示意圖
在不添加動畫的情況下,人物只能以靜止僵硬的姿態在場景中移動,因此需要為人物添加動畫,并根據不同的狀態對動畫進行切換,讓人物在場景中完成走或跑的動畫。
4.4.1 動作處理
為加載后的漫游人物模型創建一個動作混合器對象,然后對模型自帶的動畫數據進行for循環處理,根據模型中名為animations的成員對象的長度進行循環,animations的長度即模型包含動作的數量,通過animations[i].name可獲取每一個動作的名稱,通過mixer.clip Action方法可裁剪對應的動作[10]。這樣就可以將動作名稱和動畫一一對應。
為人物設置一個初始的激活動作,加載漫游人物后讓人物在沒有移動時播放靜止狀態下的“待機”動畫。要讓程序正常播放動畫,還需要在渲染函數中以mixer.update(delta)的形式告訴混合器本次渲染和上次渲染的時間差,調用update函數對每一幀動作進行更新。混合器將根據時間差來判斷模型的頂點從上一個關鍵幀向下一個變形目標移動的距離[10]。
4.4.2 動作切換
為實現動作流暢切換,首先將上一個動作通過fadeOut方法慢慢淡出。動作切換時以.reset().set Effective TimeScale(1).set EffectiveWeight(1).fadeIn(0.3).play()鏈式調用的方式將動作進行重置并設置時間影響因子、權重影響因子、動作漸入等,調用play方法執行播放動畫。THREE.Animation Mixer提供了可監聽的事件,調用混合器的addEvent Listener函數指定事件監聽函數,當一個動畫徹底播放完成時,監聽函數會收到“finished”事件通告。監聽到動畫結束后,執行將動作再次恢復到默認的人物靜止休閑狀態。當人物在運動狀態下,會有一個水平的速度(x或z),這時需要根據人物的運動速度大小判斷人物的動作(走或跑)。判斷流程如圖11所示。最終實現人物前進的動畫效果如圖12所示。

圖11 動畫切換判斷流程

圖12 人物前進動畫
討論了基于Three.js構建虛擬漫游服裝店的關鍵技術和流程,實踐表明:經過數字化處理的服裝店三維模型可以在Web端實現可視化顯示及場景漫游。分析了服裝店場景中服裝數字化不同的模擬方式,基于虛擬陳列展示和零售空間布局的理論對服裝店場景中的陳列展示進行數字化;介紹了Three.js中場景構建的流程,結果頁面中實現以良好的預覽效果對服裝店進行顯示;基于八叉樹原理的碰撞檢測可以避免穿透現象,實現人物在場景中的漫游和交互,用戶可以方便地在網頁中進行虛擬漫游逛店。基于現有成果,可進一步開展對服裝三維交互等功能的研究,提供更加豐富的用戶購物體驗。