1. 程式人生 > 實用技巧 >硬核看房利器——Web 全景的實現

硬核看房利器——Web 全景的實現

作者:凹凸曼 - EC

疫情期間,打破社交距離限制的互動模式被推向前臺,為不少行業的傳統交易提供了想象的空間。

疫情時期,房地產租售業受到的衝擊無疑是巨大的,由於人口流動的限制,需求量大幅減少,無法現場看房更加重了這一危機。但有危就有機,倒是意外推動了一項技術的推廣——VR 看房。作為 WebVR 的子集,Web 全景是多數 WebVR 需求的降級選擇,例如街景地圖,本文將帶大家實現一個簡單的 Web 全景。

這裡是文章的剩餘部分。在hexo模版裡可通過{% raw %}{{ post.more }}{% endraw %} 來引用。

貝殼x如視案例

什麼是 VR

VR(Virtual Reality)是利用電腦模擬產生一個三維空間的虛擬世界,提供使用者關於視覺等感官的模擬,讓使用者感覺彷彿身歷其境,可以及時、沒有限制地觀察三維空間內的事物。使用者進行位置移動時,電腦可以立即進行復雜的運算,將精確的三維世界視訊傳回產生臨場感。—— 維基百科

與基於現實場景進行增強效果的 AR(Augmented Reality)的區別在於,VR 的場景需要完全重建,類似於進入另一個世界。

虛擬現實的原理

人眼對世界的感知,是通過將三維世界投射至視網膜上,以二維影象建立的視覺體系。所以一張具備透視關係的影象,在特定的角度,可以使人感受到三維的空間關係,這就是人眼的深度知覺(depth perception)。VR 技術則建立在這個基礎之上。

廣泛意義上來說,只要符合模擬三維空間這一行為,就可以稱為 VR,手機、電腦、大熒幕、VR 眼鏡甚至於空氣,都可以成為 VR 的載體。

圖片來源:https://www.xensorylab.com/services

VR 的應用場景

網際網路社會的發展,本質上是促進了人與人之間資訊的流動。在資訊交換這件事上,通過的媒介從文字到繪畫、影象、聲音、影像再到虛擬現實,沉浸感逐漸增強,實現的成本也逐漸增加。但人們在沉浸感上的追求,永遠不嫌多,因為高沉浸感帶來的感官衝擊,是任何降級手段都難以取代的。這也是虛擬現實在網際網路發展路上成為必然趨勢的原因。

除了常見的遊戲(例如吃雞、塞爾達等)、影視、房地產領域的應用,VR 可發揮想象力的空間是巨大的,例如軍事中的軍事演習,體育界的沉浸式賽事直播,汽車產商可提供車輛線上虛擬配置直銷的服務,醫療界的恐懼症治療方案,教育界的新型教育模式等。

WebVR 有著輕便、使用門檻低的特點。最主要的挑戰還是在內容製作上——人力與裝置成本太高。

WebVR 目前能做到的

按照互動模式,WebVR 涉及到一個概念:dof,degree of freedom,可以理解為陀螺儀的自由度。VR 中的互動自由度分為兩種:3dof 與 6dof。3dof 指裝置互動方向包含3個轉動角度,6dof 指在 3dof 的基礎上增加了3個位置相關的自由度(上下、左右、前後)。

圖片來源:《聊一聊VR虛擬現實(二):VR眼鏡的分類》

3dof,可以看做是定點視角模式,按體驗感受來描述,就是視角主人站在一個固定的位置,對周圍的景象進行探索;以計算機的角度來描述,即 camera 的位置只能在極為侷限的特定軌道內移動,覆蓋範圍可能只有一個球面,全景的搭建內容僅需簡化至一張圖片即可。這一型別的常見應用場景有 VR 看房、街景地圖等。

6dof,可以看做移動視角模式,是較為接近現實體驗的虛擬現實,視角主人可以在場景的特定空間中進行隨意路線、隨機視角的移動而同樣能體驗到合理的透視感。相比定點視角,移動視角 VR 的實現原理則可能截然不同。如果按照定點視角的實現方法,移動視角需要的則是覆蓋任意位置的 360 視角的平面圖,資料量是難以想象的。因此這裡需要引入建模的概念,通過在計算機內建立一個 3D 的模型體系,根據 camera 所在的位置實時計算出當前視野中的影象渲染。一方面這對初始場景的建立有要求,另一方面在視角移動過程中的場景渲染的算力也有要求。因此這種型別的 VR 開發成本與體驗成本相比起定點視角型別的都較高。移動視角 VR 常見的應用場景有第一視角的 3D 遊戲,以及三維動畫/影視。

接下來我們從最為簡易的 Web 全景入手,試著實現一個 3dof 平面圖像全景場景。

開發原理

上面說到了 Web 全景的實現思路,細化下來,可以分成兩部分:一個覆蓋360度視野的三維場景供貼圖使用,以及貼圖。

按照我們對空間方位的認知,分為前後左右上下六個方位,可以想象為一個巨大的立方體,而你就站在立方體的中心位置。

我們繼續對這六個面進行切片處理,並加入適當的角度,儘量形成一個平滑的平面。當我們的切片足夠多時,這個立方體最終會變成一個球形,這是一個理想的情況。

如果只針對前後左右四面進行切片處理,直接拿掉上下方位的貼圖,也可以模擬出一個半覆蓋視野的全景場景。這種模式的最終形態會是一個沒有上下面的圓柱。

針對最基礎的立方體場景,我們需要準備的貼圖則分為6片,分別對應6個方位。貼圖需要做到接壤位置與周圍的四個貼片是影象相接的,這種型別的全景圖稱為「立方面片(Cube Faces)」,延伸出合成到同一張圖片上的「十字型」與「T 型」。而圓柱場景對應的全景圖則為「圓柱型」,球形場景對應的則為「矩形球面投影(Equirectangular)」。矩形球面投影圖可以勉強用於圓柱型場景。這幾種全景圖可以通過演算法互相轉換,最簡單的方法是使用 Pano2VR 軟體。

目前比較成熟的開發方案有:CSS 3D、WebGL(Threejs)。無論哪種方案,構建一個 Web 全景,都要經過四步:一,建立三維體系;二,搭建全景框架;三,貼圖;四,新增使用者互動。

建立三維場景

CSS 3D 版

CSS 3D 建立三維場景的關鍵點在於三維體系的設定。涉及到三個關鍵屬性:transform-styleperspectivetransform。將 transform-style 設定為 preserve-3d,意味著瀏覽器以這個容器為單位建立了一個三維空間體系,這個容器下的子元素的樣式屬性都將按照這個三維體系下的空間關係渲染。MDN 上的這個例子可以很好地體現二維體系與三維體系中元素空間關係的區別:https://developer.mozilla.org/en-US/docs/Web/CSS/transform-style

perspective 的值在三維體系中表示觀察點距離 z 軸 0 座標位置的距離,在視覺上的體現則為值越大,透視效果越弱。這麼說可能很難想象,可以參考大家在坐車的時候往窗外看,近景物體與遠景物體移動速度的差別,則為 perspective 值之間的差別,值越小意味著物體離你越近。預設值為 1000px,如果覺得透視效果不夠明顯,可以適當地減少 perspective 的值。

demo 來源:https://developer.mozilla.org/en-US/docs/Web/CSS/perspective

需要注意的是,這裡一共需要兩層 transform-style 的設定。包裹著所有切片的容器需要設定,整個場景的 3D 旋轉操作就是在這個容器上。為了讓這個容器的旋轉也產生 3D 的效果,需要在這個容器的外層再新增一個帶 transform-style 屬性的容器。

三維體系與透視值設定好了之後,就可以開始對子元素進行佈局了。與 3D 佈局相關的屬性基本集中在 transform 中,通過 translateXtranslateYtranslateZrotateXrotateYrotateZ 則可以滿足我們想要建立的三維場景。

以較為簡單的圓柱型場景為例,我們需要確定切片的數量,然後通過計算確定切片的旋轉角度與位移距離。因為我們有旋轉場景的需求,因此以 (0, 0, 0) 為場景中心點是較為好操作的。首先大家需要明確一個點,transform 中的各函式值先後順序不同所展示的樣式也是不同的。因此先設定 rotate 與先設定 translate,對應的函式值也不同。如果是先設定 rotate,則在位移上可以統一使用一個值,這裡我們使用 translateZ;如果先設定 translate,在位移的計算上則複雜了不少。因此我們採用先旋轉再位移的形式來佈局全景場景的切片。

從 y 軸視角看切片,是一個正多邊形。每一切片間的間隔角度為360度除以切片數量,而切片的寬度則需要通過三角函式來計算。

下面的程式碼段中的計算,radius 值固定,因此根據邊數的不同,切片的高度將發生變化。也可以固定 d 值或是切片高度來進行相應值的計算。



圓柱型場景使用的貼圖為首尾相接的『圓柱型』或『矩形球面投影』型別的,我們要做的就是將這張圖平均、無縫分佈到每一片切片上。這裡使用 background-image 背景圖進行貼圖的話,就需要針對 background-position 進行計算。


const position = parseInt(-width * (planeNum - i - 1), 10)

由於移動端在進行等比縮放時,會出現小數點的尺寸與定位值,因此很有可能在切片之間產生空隙,影響全景效果。這裡可以通過適當擴大切片寬度,切片之間互相交叉擺放,來避免這種情況的發生。這個時候的背景圖定位需要進行重新計算。

貼圖完成之後,整個圓柱型全景的場景也就搭好了。

整體 demo 地址:https://codesandbox.io/s/css-3d-panorama-oqswp?file=/src/Pano.js

ThreeJS 版

在開發原理部分,我們說到,立方體的每個面進行無限的切片處理,最終會形成理想的球形。理論上,球形形成的全景場景是最為接近人眼的真實感受的,視線抵達貼圖的距離均等。而選擇 ThreeJS(下文簡稱阿三) 也是看上了其完整的三維體系以及強大的幾何體繪製能力。

而目前在計算機中模擬弧面,只能通過切分的平面進行,切分的平面越多弧面就越平滑。因此阿三中的所有 3D 模型都是一個多面體。雖然切片的演算法全權由阿三處理,但切片的分佈需要做進一步的瞭解,以幫助我們瞭解球形貼圖的相關細節。

圖片來源:Creating a WebGL Earth with three.js

球體的切片方式與地球經緯度的劃分方式一致,在南北緯90度的位置會出現若干個三角平面匯聚的情況,因此貼圖在此處會出現放射狀紋理。

阿三的三維體系中有幾個基本物件:場景(Scene)、鏡頭(Camera)、渲染器(WebGLRenderer)。整個體系的建立這三個物件缺一不可。

場景承載著所有渲染元素,通過 add 方法新增元素。

鏡頭充當著“眼”的功能,放置了鏡頭才能“看到”場景中的元素,同時通過設定 position 引數調整鏡頭的擺放位置,通過 lookAt 方法調整鏡頭聚焦的位置。阿三提供了兩種模式的攝像機——正交投影攝像機(OrthographicCamera)與透視投影攝像機(PerspectiveCamera)。區別在於針對不同距離的元素是否有做透視處理,如果需要透視處理,選擇透視投影攝像機即可,這也是比較常用的攝像機。


camera.position.set(camerax, cameray, cameraz)
camera.lookAt(scene.position)

透視攝像機需要針對我們傳統意義上的視野進行一些引數設定,一共4個引數,決定了攝像機的最終視野。

PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number ) fov — Camera frustum vertical field of view. aspect — Camera frustum aspect ratio. near — Camera frustum near plane. far — Camera frustum far plane.

接著就是將整個場景渲染至網頁上。

我們可以先獲取場景容器,再將阿三畫布插入容器中。


document.body.appendChild(renderer.domElement)

或是直接將已有畫布物件傳入渲染器物件中。


new THREE.WebGLRenderer({ canvas: canvasDom })

然後通過渲染器的 render 方法渲染場景與攝像機。


renderer.render(scene, camera)

場景 demo 地址:https://codesandbox.io/s/threejs-panorama-0u763?file=/src/PanoScene.js

到這一步為止,整個三維場景其實已經建立好了,接下來我們往裡放元素。在這個場景裡我們需要的是球體元素,阿三中對應的物件為 SphereGeometry。貼圖對應的是材質(Material)物件,阿三提供了若干種材質物件,我們這裡僅需要用到最基礎的 MeshBasicMaterial(網格基礎材質)即可。將球體物件與材質物件結合到一起,就可以構成一個完整的 Mesh(網格)物件,這就是我們需要的全景球體。

通過控制材質的 opacity 屬性,可以實現元素的顯隱,這一點可以應用於多全景場景之間的切換效果。


sphereMaterial.transparent = true
sphereMaterial.opacity = 0

一個基於阿三的球體全景場景就搭好了。

基於 WebGL 的其他框架

除了阿三,市面上還存在一種框架,將三維場景的元素封裝為 HTML 標籤,提升程式碼可讀性。類似 A-Frame(http://aframe.io/)與 krpano(https://krpano.com/home/)均為這種思路。區別在於 krpano 更專注於全景場景,封裝了包括控制面板在內的全景相關套件,類似於全景元件,並提供了視覺化桌面端軟體,簡單替換圖片、場景縮圖等資訊之後即可使用。而 A-Frame 是一個基於阿三的二次封裝框架,因此阿三能做的,它也能做,它所做的就是把阿三的各種物件封裝成了一個個 HTML 標籤,做到了物件與邏輯的程式碼分離。這一類的方案原理與阿三搭建場景的類似,感興趣的可以自行嘗試,在這裡就不多做介紹。

瀏覽全景

到這裡為止,我們完成了全景場景的搭建,但目前我們只能看到全景場景的一角,沒有滿足 VR 概念中“可觀察”這一條件。現在我們需要給場景增加一些可瀏覽的操作邏輯。瀏覽全景的效果從主視角看來,就是站在原地旋轉360度。在圓柱模式的全景場景中,上下方位的旋轉角度會受到邊界的限制;而如果是球體模式,則可以做到三個方向的360度旋轉。在 CSS 3D 的方案中,我們通過旋轉整個場景容器,來實現全景場景的瀏覽,而在阿三的方案中,我們需要通過調整攝像機的位置來實現(我們將攝像機的聚焦點固定在球體中心)。但無論是哪種方案,都需要得到一個使用者互動與場景/攝像機旋轉對映的值。

在 web 環境,一般的人機互動都是通過滑鼠、鍵盤與手勢進行,因此首先我們針對這些內容進行監聽。以手勢為例:

包含三個階段:互動起始,互動過程,互動終止。 最快速的方法,就是將互動位移差值與場景/攝像機旋轉值進行對應比例的轉換。

嘗試了這種方式之後,會很明顯感受到瀏覽過程的生硬感。這是因為這樣的動態方式違背了動畫十二原則中的漸快與漸慢(Slow in and slow out)原則,在場景動畫起始與結束時急起急停,缺乏過渡。因此考慮針對互動終止的位置增加一個緩出動畫,就好像轉圈的時候有個慣性似的,這也意味著我們需要對結尾的動作進行方向與速度上的判斷。


// 當前滑動速度
const vx = (originalEndX - startX) / duration
// 預估當前滑動終點
const endX = vx * 200

這裡我們結合 gsap 的時間軸,利用其動畫計算可以基於當前狀態值的特性,對整體動效進行平滑過渡處理,因為對於互動動作監聽的時間間隔夠短,在結束之前的數值緩動效果可以忽略不計,但起始與終止的緩動卻可以保留,堪稱完美。

完整緩動演算法 demo:https://codesandbox.io/s/move-ani-compare-xvi26?file=/index.js

有了這個演算法,我們就可以針對每一種方案進行場景的互動處理了。

CSS 3D 場景

在 CSS 3D 場景中,要實現全景場景的瀏覽,需要做的是旋轉整個三維體系容器,也就是使用 transform 中的 rotate 函式。針對互動數值變化與 rotate 數值做一個置換。

完整 demo:https://codesandbox.io/s/css-3d-panorama-with-mouse-event-jp2jl?file=/src/Pano.js

ThreeJS 場景

在阿三場景中,我們需要改變的是攝像機聚焦點的位置,又或者將攝像機聚焦點固定在球體中心,移動攝像機的位置。攝像機移動的範圍就在以球體中心為球心的球面上。

具體的滑鼠位移與球體經緯度的轉化公式可檢視完整 demo:https://codesandbox.io/s/threejs-panorama-dy1lt?file=/src/event.js

這樣,一個完整的全景體驗的 web 全景就開發完成了。

主要挑戰

當我們把全景的框架搭建好之後,理論上只要替換圖片,則可以實現無數場景的全景體驗。但稍微上網一搜,大家會發現,這類全景圖想要找到既高清又符合首尾相接標準的,真的很難,要麼帶水印,要麼不夠清晰,要麼就收費。如果你想自己拍,又會發現拍攝這一類照片所需要的裝置要求還蠻高,非專業機構沒什麼必要購置。因此此類全景場景最大的挑戰就在於全景圖的獲取,純體力活,需要消耗大量人力與時間。而像貝殼找房這類受疫情衝擊的行業,通過推動 VR 看房,結合便利、使用門檻低的裝置,既能消化閒置的人力,同時還維繫了業務的運轉。

同時大家也能看出,文章開頭的 VR 看房視訊,並不是單純的 Web 全景就能實現,其中還包含了測距、路徑切換等。而這一套服務,是由如視(https://realsee.com/)服務商提供的,他們售賣的方案中包含了自研硬體掃描器與線上三維重建技術等軟體的打包,解決了內容創作門檻這個痛點,同時通過 C 端裝置降級方案,由保有量較少的 VR 眼鏡轉向了體驗穩定的 PC 與手機,這或許將成為近一段時期 VR 消費端的通用推廣方式。

VR 的未來

一項新興技術的成熟曲線往往要經歷新技術誕生、期望膨脹、泡沫化、穩步爬升、實質生產這5個階段,目前 VR 正處於啟蒙期階段,內容創作成本尚未降低,裝置保有量尚未形成規模。

圖片來源:《聊聊 VR 虛擬現實(一):VR 的發展史》

在 2016 年高盛釋出的《VR 與 AR 報告:下一個通用計算平臺》中,對於 2025 年 VR/AR 9大應用領域規模的預期,只有視訊遊戲、事件直播和視訊娛樂3大領域將完全由消費者推動,佔整體 VR/AR 營收預期的 60%,剩餘 40% 由企業和公共部門推動。

正如前文提到的,從文字到圖片到聲音再到視訊,是目前所廣泛使用的資訊傳播途徑,未來的模式一定是更加降低門檻、減少失真,虛擬化將成為必然的趨勢,加上 AI 的持續推進,以及 5G 的未來,VR 的想象空間還有很大。

參考文件

淺談 WebVR:https://aotu.io/notes/2016/08/24/2016-8-24-webvr/ A-Frame WebVR 試玩報告:https://aotu.io/notes/2016/10/08/aframe/ CSS 3D Panorama - 淘寶造物節技術剖析:https://aotu.io/notes/2016/08/24/2016-8-24-css-3d-panorama/ krpano:https://krpano.com/home/ 使用 ThreeJS 在瀏覽器中展示全景圖:https://aotu.io/notes/2016/01/02/3D-panorama/ Pano2VR:https://ggnome.com/pano2vr/ VR 看房是如何實現的?:https://cloud.tencent.com/developer/ask/142336 十分鐘打造 3D 物理世界:https://aotu.io/notes/2018/10/18/cannonjs/ 高盛 VR 與 AR 報告:下一個通用計算平臺:https://tech.qq.com/a/20160202/011274.htm Google AR&VR:https://arvr.google.com/ The Best VR Headsets for 2020:https://www.pcmag.com/picks/the-best-vr-headsets 貝殼x如視案例:https://realsee.com/website/public/demo/fwmm/ryQkazl8xKdesBhKhVTkqqxTVLX6Rn9Km.html 聊聊VR虛擬現實(一):VR的發展史:https://zhuanlan.zhihu.com/p/26592125 VR眼鏡的分類:https://zhuanlan.zhihu.com/p/31649178 也說5G與雲VR:https://zhuanlan.zhihu.com/p/83623971 5G專網為“江南皮革廠”帶來了什麼?:https://mp.weixin.qq.com/s/BBwI2_Tnp_5cO2ngr4eh1w

歡迎關注凹凸實驗室部落格:aotu.io

或者關注凹凸實驗室公眾號(AOTULabs),不定時推送文章: