Three.js 進階之旅:全景漫游-初階移動相機版

聲明:本文涉及圖文和模型素材僅用于個人學(xué)習、研究和欣賞,請勿二次修改、非法傳播、轉(zhuǎn)載、出版、商用、及進行其他獲利行為。

摘要
3D
?全景技術(shù)可以實現(xiàn)日常生活中的很多功能需求,比如地圖的街景全景模式、數(shù)字展廳、在線看房、社交媒體的全景圖預(yù)覽、短視頻直播平臺的全景直播等。Three.js
?實現(xiàn)全景功能也是十分方便的,當然了目前已經(jīng)有很多相關(guān)內(nèi)容的文章,我之前就寫過一篇《Three.js 實現(xiàn)3D全景偵探小游戲》。因此本文內(nèi)容及此專欄下一篇文章討論的重點不是如何實現(xiàn)?3D
?全景圖功能,而是如何一步步優(yōu)雅實現(xiàn)在多個3D全景中穿梭漫游,達到如在真實世界中前進后退的視覺效果。
全景漫游系列文章將分為上下兩篇,本篇內(nèi)容我們先介紹如何通過移動相機的方法來達到場景切換的目的。通過本文的學(xué)習,你將學(xué)到的知識點包括:在?Three.js
?中創(chuàng)建全景圖的幾種方式、在?3D
?全景圖中添加交互熱點、利用?Tween.js
?實現(xiàn)相機切換動畫、多個全景圖之間的切換等。

效果
本文最終將實現(xiàn)如下的效果,左右控制鼠標旋轉(zhuǎn)屏幕可以預(yù)覽室內(nèi)三維全景圖,同時全景圖內(nèi)有多個交互熱點,它們標識著三維場景內(nèi)的一些物體,比如沙發(fā)???
?、電視機???
?等,交互熱點會隨著場景的旋轉(zhuǎn)而旋轉(zhuǎn),點擊熱點???
?可以彈出交互反饋提示框。

點擊屏幕上有其他場景名稱的按鈕比如?客廳
、臥室
、書房
?時,可以從當前場景切換到目標場景全景圖,交互熱點也會同時切換。

打開以下鏈接,在線預(yù)覽效果,大屏訪問效果更佳。
?????
?在線預(yù)覽地址:https://dragonir.github.io/panorama-basic/
本專欄系列代碼托管在?Github
?倉庫【threejs-odessey】,后續(xù)所有目錄也都將在此倉庫中更新。
??
?代碼倉庫地址:git@github.com:dragonir/threejs-odessey.git

原理
我們先來簡單總結(jié)下在?Three.js
?中實現(xiàn)三維全景功能的有哪些方式:
球體
在球體內(nèi)添加?HDR
?全景照片可以實現(xiàn)三維全景功能,全景照片是一張用球形相機拍攝的圖片,如下圖所示:

??
?球體全景圖 Three.js 官方示例
立方體
在立方體內(nèi)添加全景圖貼圖的方式也可以實現(xiàn)三維全景圖功能,此時需要對?HDR
?全景照片進行裁切,分割成?6
?張來分別對應(yīng)立方體的?6
?個面。

??
?立方體全景圖 Three.js 官方示例
環(huán)境貼圖
使用環(huán)境貼圖也可以實現(xiàn)全景圖功能,像下面這樣加載全景圖片,然后將它賦值給?scene.background
?和?scene.environment
?即可:
??
?具體原理和實現(xiàn)方式就不詳細介紹了,可查看我往期的文章《Three.js 進階之旅:多媒體應(yīng)用-3D Iphone》,環(huán)境貼圖段落中有詳細實現(xiàn)介紹。
其他
除了使用?Three.js
?自己實現(xiàn)全景圖功能之外,也有一些其他功能完備的全景圖庫可以很方便的實現(xiàn)三維全景場景,比如下面幾個就比較不錯,其中后兩個是?GUI
?客戶端,可以在客戶端內(nèi)非常方便的在全景圖上添加交互熱點、實現(xiàn)多個場景的漫游路徑等,大家感興趣的話都可以試試。
panolens.js:https://github.com/pchen66/panolens.js
pannellum:https://github.com/mpetroff/pannellum
Photo-Sphere-Viewer:https://github.com/JeremyHeleine/Photo-Sphere-Viewer
krpano:https://krpano.com/home/
Pano2VR:https://ggnome.com/pano2vr/

工具
全景圖生成工具
使用球形全景相機拍攝。
使用?
Blender
?等建模軟件相機?360
?度旋轉(zhuǎn)渲染。
全景圖編輯工具
下面兩個網(wǎng)站提供豐富的三維全景背景照片及將?hdr
?圖片裁切成上述需要的?6
?張貼圖的能力,大家可以按自己需要下載和編輯。
??
?HDR全景背景照片下載網(wǎng)站:polyhaven

??
?HDR立方體材質(zhì)轉(zhuǎn)換工具:HDRI-to-CubeMap


實現(xiàn)
現(xiàn)在,我們使用第一種球體??
?全景圖的方式,來實現(xiàn)示例中介紹的內(nèi)容。
〇 場景初始化
創(chuàng)建全景圖前先做一些常規(guī)三維場景準備工作,由于三維全景圖功能并不會涉及到新的技術(shù)點,因此像下面這樣簡單實現(xiàn)就可以。
在文件頂部引入以下資源,其中?OrbitControls
?用于旋轉(zhuǎn)全景圖時的鏡頭鼠標控制;TWEEN
?用于創(chuàng)建流程的場景切換動畫,Animations
?是使用?TWEEN
?來控制攝像機和控制器切換的方法的封裝,可以快速實現(xiàn)鏡頭的絲滑切換;rooms
?是自定義的一個數(shù)組,用來保存多個全景圖的信息。
然后初始化渲染器、場景、相機、控制器、頁面縮放適配、頁面重繪動畫等。
① 創(chuàng)建一個球體
現(xiàn)在,像下面這樣,我們往場景中添加一個三維球體??
,作為第一個全景圖的載體。其中?THREE.SphereGeometry(radius, segmentsWidth, segmentsHeight, phiStart, phiLength, thetaStart, thetaLength)
?接收?7
?個參數(shù),我們使用前?3
?個參數(shù)半徑、經(jīng)度上的面數(shù)切片數(shù)、緯度上的切片數(shù)即可,數(shù)值可按自己的需求自行調(diào)整。

② 創(chuàng)建全景圖
現(xiàn)在我們對球體進行全景圖片貼圖,并將?side
?屬性設(shè)置為?THREE.DoubleSide
?或者?THREE.BackSide
?然后通過設(shè)置?geometry.scale(1, 1, -1)
?將球體內(nèi)外翻轉(zhuǎn),就能得到下面所示的效果。

此時,我們通過鼠標放大球體,進入到球體內(nèi)部,上下左右旋轉(zhuǎn)球體,就能觀察到全景效果了。

③ 創(chuàng)建其他場景的全景圖
對于數(shù)量較少,簡單的場景我們可以創(chuàng)建多個球體全景圖來實現(xiàn),這種方式雖然笨重,但是控制多個場景很方便,代碼也非常容易理解,下篇文章將通過另一種更優(yōu)雅的方式來實現(xiàn)多個全景圖場景,以適應(yīng)更加復(fù)雜的需求。
我們先對創(chuàng)建球體??
?全景圖的方法加以封裝,通過?createRoom
?方法批量創(chuàng)建多個全景圖場景,它接收的名稱?name
、位置?position
?以及 貼圖?map
?三個參數(shù)是通過上述引入的?rooms
?數(shù)值配置的。
我們按房間位置的和貼圖的配置,創(chuàng)建如下所示的三個房間客廳、臥室和書房。

④ 限制旋轉(zhuǎn)角度
根據(jù)自己的需求,我們可以對鏡頭控制器???
?做以下限制,比如開啟轉(zhuǎn)動慣性、禁止整個場景通過鼠標右鍵發(fā)生平移、設(shè)置縮放的最大級別防止暴露出球體、限制垂直方向旋轉(zhuǎn)等,以增強用戶體驗。

⑤ 實現(xiàn)多個場景穿梭漫游
本文中實現(xiàn)多個場景穿梭漫游的方法原理:主要是通過移動相機和控制器的中點位置來實現(xiàn)的,我們先用用于生成多個場景的?rooms
?數(shù)值在頁面上添加一些表示切換房間的按鈕,點擊按鈕時拿到需要跳轉(zhuǎn)的目標場景信息,然后通過?Animations.animateCamera
?方法將像機和控制器從當前位置平滑移動到目標位置。
其中?Animations.animateCamera
?方法是使用?TWEEN.js
?封裝的一個移動相機???
?和控制器???
?的方法,使用它可以實現(xiàn)絲滑的鏡頭補間動畫,不僅可以像本文中這樣來實現(xiàn)多個場景的切換,還可以實現(xiàn)像鏡頭從遠處拉近、點擊交互點后鏡頭聚焦放大到某個局部,鏡頭場景巡航等效果。完整代碼可以查看本篇文章的示例代碼:

⑥ 添加交互點
場景漫游穿梭的功能已經(jīng)實現(xiàn)了,現(xiàn)在我們來在全景場景中添加一些交互熱點??
,用于實現(xiàn)場景物體標注和鼠標點擊交互,比如我們在這個示例中,在客廳中添加了?電視機??
、沙發(fā)??
、冰箱??
?等交互點,我們可以現(xiàn)在創(chuàng)建場景的數(shù)組中添加這些交互點的信息?interactivePoints
,以方便批量創(chuàng)建,根據(jù)自己的需求我們可以添加一些可選的配置參數(shù),本文中的參數(shù)含義分別是:
key
:唯一標識符。value
:顯示名稱。description
:描述文案。cover
:配圖。position
:在三維空間中的位置。
然后在頁面上利用?rooms
?數(shù)組的?interactivePoints
?來批量創(chuàng)建交互點的?DOM
?節(jié)點:
用樣式表把交互點設(shè)置成自己喜歡的樣式???
?,需要注意的一點是,交互點???
?初始的樣式中設(shè)置了?transform: scale(0, 0)
, 即它的寬高都為?0
,是隱藏看不見的,這樣設(shè)置的目的是為了實現(xiàn)只有交互點出現(xiàn)在相機可視區(qū)域時才顯示在場景中,其他轉(zhuǎn)動到相機背面時應(yīng)該隱藏掉。當交互點被添加?.visible
?類時,交互點變?yōu)轱@示狀態(tài)。本示例中還使用交互點內(nèi)?.label::before
、.label::after
等偽元素和子元素添加了一些波紋擴散動畫及其其他文案信息等。
??
?隱藏顯示的交互也可以通過?display:none
、visibility:hidden
、及使用?js
?變量控制元素隱藏顯示等方式來實現(xiàn)。
創(chuàng)建完交互點???
?元素之后,我們還需要在頁面重繪方法?tick()
?中像下面這樣添加一個方法,來將交互點顯示在三維場景中,并根據(jù)與相機的關(guān)系來控制每個交互點的顯示與隱藏,原理是使用?THREE.Raycaster
?來檢測元素是否被遮擋:
??
?關(guān)于使用?Raycaster
?來檢測元素是否被遮擋的詳細介紹,可以看看我的這篇文章《Three.js 打造繽紛夏日3D夢中情島》。

⑦ 頁面優(yōu)化和加載進度管理
最后,因為創(chuàng)建多個三維全景圖場景需要加載很多張圖片,而且全景圖的圖片一般比較大,我們可以預(yù)先加載完所有圖片后再進行渲染,本文使用的是自己添加的一個預(yù)加載方法,也可以使用像?preload.js
?等其他庫來預(yù)加載圖片。除了加載進度顯示之外,現(xiàn)實開發(fā)場景中應(yīng)該還有很多個性化的需求,比如可以在點擊交互點的時候彈出一個詳細彈窗、點擊電視的時候開始播放一段視頻、點擊沙發(fā)的時候鏡頭聚焦放大到沙發(fā)、點擊開關(guān)的時候變?yōu)橐归g模式……這些交互的原理和本文中的交互點是差不多的???
。

??
?源碼地址:?https://github.com/dragonir/threejs-odessey
總結(jié)
本文中主要包含的知識點包括:
在?
Three.js
?中實現(xiàn)全景圖的原理和多種實現(xiàn)方式。與全景圖相關(guān)的生成工具、編輯工具的使用。
創(chuàng)建多個全景圖并實現(xiàn)多個場景間的漫游穿梭功能。
在三維全景圖中添加交互熱點。
本文到這里就結(jié)束了,本文中通過移動相機鏡頭和控制的方法來實現(xiàn)幾個全景圖之間漫游穿梭效果還是不錯的,但是它的缺點也是很明顯的,就是當全景場景數(shù)量特別多時,就需要創(chuàng)建非常多的球體,此時計算出每個場景的位置非常困難,并且會造成頁面性能耗損問題,因此需要進行優(yōu)化。下篇文章將會介紹另一種更加優(yōu)雅的方式來實現(xiàn)全景圖之間的漫游功能,過渡動畫也會更加流暢絲滑。
想了解其他前端知識或其他未在本文中詳細描述的Web 3D開發(fā)技術(shù)相關(guān)知識,可閱讀我往期的文章。如果有疑問可以在評論中留言,如果覺得文章對你有幫助,不要忘了一鍵三連哦 ??。
附錄
[1].??? Three.js 打造繽紛夏日3D夢中情島
[2].??? Three.js 實現(xiàn)炫酷的賽博朋克風格3D數(shù)字地球大屏
[3].??? Three.js 實現(xiàn)2022冬奧主題3D趣味頁面,含冰墩墩
[4].??? Three.js 實現(xiàn)3D開放世界小游戲:阿貍的多元宇宙
[5].??? 掘金1000粉!使用Three.js實現(xiàn)一個創(chuàng)意紀念頁面
…
【Three.js 進階之旅】系列專欄訪問 ??
更多往期【3D】專欄訪問 ??
更多往期【前端】專欄訪問 ??
參考
[1].?threejs.org
