您當前的位置:首頁 > 攝影

記一次three.js專案總結

作者:由 attackonryan 發表于 攝影時間:2020-07-04

專案簡介:

完善3D模型,並在移動端展示,可利用陀螺儀環視,同時手機開啟攝像頭,且3D模型要覆蓋在攝像頭影片上。3D模型動畫結束展示直播間二維碼,出現兩個按鈕,一個可以儲存二維碼,一個可以重新觀看3D模型動畫。

3D模型的原始樣子

記一次three.js專案總結

3D模型的最終樣式(要求渲染出的樣子)

記一次three.js專案總結

選用框架:

three.js

webpack

開發過程:

配置webpack

使用webpack搭建專案環境,直接用自己寫的webpack-preset,配置檔案目錄結構如下

記一次three.js專案總結

三維專案因為用不到太多CSS和圖片(暫時是這樣的),所以webpack的很多外掛和loader其實用不到。

配置目錄結構

一開始初始化的時候覺得可以將scene,camera,renderer,lights子類的分到各個不同的資料夾裡,這樣目錄結構更清晰,程式碼也不會堆在一個檔案裡。

配好的目錄結構如下

記一次three.js專案總結

結構還算清晰,loaders和controls都只是為了方便獲取對應的類,其他都是例項後的物件

比如loaders/index。js

記一次three.js專案總結

只是為了方便獲取不同的Loader

記一次three.js專案總結

而其他檔案都是輸出一些例項後的物件,比如lights/index。js

記一次three.js專案總結

### 新增一個video標籤

template.html

裡建立

video

標籤,用來顯示相機影片。

記一次three.js專案總結

為了讓

three.js

生成的

canvas

能覆蓋在影片流上,我們要設定CSS

記一次three.js專案總結

因為

canvas

標籤是最後放入

body

標籤內的,所以不用設定

z-index

這裡設定了

video

標籤的

object-fit

屬性為

cover

,目的是為了讓影片能填滿整個手機螢幕,因為預設的影片顯示效果為佔滿整個螢幕寬度,但是高度卻遠遠達不到螢幕的高度,這樣會讓最終效果顯得很差,會有大量留白。(不知道是不是瀏覽器只支援橫屏展示相機影片,目前這個解決方案個人認為還不夠優雅,但確實還沒找到更好的辦法)。

開啟攝像頭

注意: Web呼叫攝像頭只支援在本地執行(http://localhost)或使用https!

呼叫攝像頭程式碼如下

export

function

initVideo

()

{

//medaDevices介面是新的規範,因此如果存在則優先呼叫

//navigator。getUserMedia方法已被廢棄,此處作為向後相容

//如果兩種方法都不存在,提示使用者升級瀏覽器版本

if

navigator

mediaDevices

{

navigator

mediaDevices

getUserMedia

({

video

{

width

window

innerWidth

height

window

innerHeight

//優先開啟後置攝像頭

facingMode

‘environment’

}

})

then

function

stream

{

let

video

=

document

querySelector

‘video’

//注意 這裡使用srcObject而不是src

video

srcObject

=

stream

video

onloadedmetadata

=

function

e

{

video

play

()

}

})

catch

function

err

{

alert

‘攝像頭開啟失敗’

})

}

else

{

navigator

getUserMedia

=

navigator

getUserMedia

||

navigator

webkitGetUserMedia

||

navigator

mozGetUserMedia

||

navigator

msGetUserMedia

if

navigator

getUserMedia

{

navigator

getUserMedia

({

video

{

width

window

innerWidth

height

window

innerHeight

facingMode

‘environment’

}

},

function

stream

{

let

video

=

document

querySelector

‘video’

video

srcObject

=

stream

video

onloadedmetadata

=

function

e

{

video

play

()

}

},

function

err

{

alert

‘攝像頭開啟失敗’

}

}

else

{

alert

‘您的瀏覽器暫不支援開啟攝像頭,您可嘗試升級您的瀏覽器版本以獲取最佳體驗’

}

}

}

建立three。js相關物件

scene,camera等相關物件的建立過程這裡就不細講了,比較基礎,大家可以去看three。js的官網教程學習。

要注意的地方有

renderer.alpha

屬性要設定為

true

,這是為了讓背景透明,從而能夠顯示攝像頭畫面。

夢(keng)開始的地方

一開始模型給的是FBX,二話不說直接使用FBXLoader,結果出來的模型只剩下非常少的一部分,如下圖,

第一眼根本看不出這是啥。

記一次three.js專案總結

一開始以為是FBX檔案的問題,但是反覆確認檔案沒有問題,我不得不檢查自己的配置。先從渲染出來的物件資料入手,看了一會果然發現有貓膩,有相當一部分的Mesh,quaternion和rotation屬性居然存在

NaN

記一次three.js專案總結

我斷定這就是模型渲染不出來的原因,因此我手動修正這些資料,仍然不行,設定完馬上又變NaN,我就開始懷疑是動畫軌道的問題,所以我去看了看

animationcCip.tracks

,,果然很多track的values資料都是NaN。

記一次three.js專案總結

我嘗試把存在NaN的軌道刪除,果然模型能顯示了

記一次three.js專案總結

但是顯示還是存在一定的問題,並且動畫效果不見了,現在留給我兩條路

1 手寫動畫,手動調整模型

2 換一個模型格式

我選擇後者,前者出來的模型應該不會太穩定。

先做一個小結:

FBXLoader

對FBX檔案格式要求較為嚴格,Loader外掛的相容性較差,謹慎使用。

模型格式更換

後來我得到了GTLF的檔案格式,使用

GLTFLoader

,正確配置好動畫後,這次基本所有的模型動畫都能顯示了。

對比一下開頭的模型,唯獨文字描邊位置非常不對勁。

記一次three.js專案總結

中間的文字描邊都不見了,大家可以找一找,它跑去哪了呢?

揭曉答案

記一次three.js專案總結

……

第一時間去查文字描邊的物件資料,

記一次three.js專案總結

scale屬性不正常,有了之前的經驗,第一時間懷疑動畫軌道有問題,查了查

記一次three.js專案總結

最後的正確資料應該為1而不是0。1,果然有問題,所以我不得不手動修改這些動畫資料。

包括quaternion屬性也存在相關問題,最終寫完了動畫修復程式碼

//修復描邊BUG

gltf

animations

0

]。

tracks

forEach

v

=>

{

let

len

=

v

values

length

if

v

values

len

-

1

<

0。2

&&

/(材質)\w*\。scale$/

test

v

name

))

{

for

let

i

=

0

i

<

6

i

++

{

v

values

len

-

1

-

i

=

1

}

}

else

if

+

v

values

len

-

1

!==

0。5

&&

/(材質)\w*\。quaternion$/

test

v

name

))

{

for

let

i

=

0

i

<

8

i

++

{

if

i

%

4

==

0

{

v

values

len

-

1

-

i

=

-

0。5

}

else

{

v

values

len

-

1

-

i

=

0。5

}

}

}

})

動畫顯示正常

記一次three.js專案總結

調整模型的樣式

為了達到要求的樣式,我選擇先手動給材質打上外發光顏色(

emissive

),部分程式碼如下圖

記一次three.js專案總結

效果

記一次three.js專案總結

接下來我希望底部有一種光照的效果,因此我使用three。js原始碼內的lensflare圖片作為顏色貼圖,再調整了一下顏色,程式碼如下

記一次three.js專案總結

效果(這裡打開了攝像頭)

記一次three.js專案總結

對比一下

記一次three.js專案總結

發現缺少一點明亮的感覺

使用bloom泛光效果

使用泛光效果可以將明亮部分更加逼真的渲染。

加入以下程式碼

//泛光特效

import

{

EffectComposer

}

from

‘three/examples/jsm/postprocessing/EffectComposer。js’

import

{

RenderPass

}

from

‘three/examples/jsm/postprocessing/RenderPass。js’

import

{

UnrealBloomPass

}

from

‘three/examples/jsm/postprocessing/UnrealBloomPass。js’

const

params

=

{

bloomStrength

0。8

bloomThreshold

0

bloomRadius

0

}

let

composer

let

renderScene

=

new

RenderPass

scene

camera

let

bloomPass

=

new

UnrealBloomPass

new

THREE

Vector2

window

innerWidth

window

innerHeight

),

1。5

0。4

0。85

bloomPass

threshold

=

params

bloomThreshold

bloomPass

strength

=

params

bloomStrength

bloomPass

radius

=

params

bloomRadius

composer

=

new

EffectComposer

renderer

composer

addPass

renderScene

composer

addPass

bloomPass

。。。

function

render

isMobile

=

false

{

//請求再次執行渲染函式render,渲染下一幀

requestAnimationFrame

render

。。。

//後期渲染

composer

render

()

。。。

}

記一次three.js專案總結

有點晃眼,是不是

環繞的文字和中間的文字過亮,但其他部分效果正好,所以我希望文字不被後期渲染。

研究了一下官網的例子,官網不讓某些物體被渲染的方法如下

記一次three.js專案總結

這種解決方案有缺點,因為這個方法會在render函數里不斷呼叫,十分影響效能。並且官網的例子是靜態的,如果將此解決辦法引入我們的專案,效果不一定會好。

因此我研究了

layer

,發現合理利用layer即可分層渲染。

首先設定文字物件的layers,將他們設定為1,與預設的0區分

記一次three.js專案總結

接著設定相機分層渲染,改寫render函式

記一次three.js專案總結

效果如下

記一次three.js專案總結

對比

記一次three.js專案總結

講道理還是差點特效,但是總比裸模型接近不少了。

二維碼、按鈕等部分的處理這裡就不講了,只是一些簡單的DOM操作。

總結

three。js相關的基礎概念很多,想要更高效的學習three。js最好先學習圖形學相關基礎。

不要害怕讀原始碼,原始碼可以教會你很多東西。

多看官方文件。

更新一些坑

陀螺儀控制可以使用three。js官方的DeviceOrientationControls,但是注意ios上safari需要使用者的主動操作才可以使用陀螺儀控制的核心API DeviceOrientationEvent,如果是頁面載入時指令碼自動使用這些API,陀螺儀是無法使用的。

解決方法

:一般會在頁面上新增一個按鈕,提醒使用者授權,在點選事件裡開啟這些功能即可。

使用了bloom泛光後期處理會導致三維場景背景不透明。

解決方法

:這個issue裡有具體的解決辦法。

WKWebkit核心(比如ios微信內嵌的瀏覽器)不支援WebRTC(也就沒法使用我的程式碼開啟攝像頭),這個問題的討論在這裡,大家有興趣可以看一看。

標簽: js  three  模型  攝像頭  video