Three.js筆記(八)相機(2)

自定義控件

回到PerspectiveCamera透視相機上來。注釋OrthographicCamera正交相機,取消注釋PerspectiveCamera透視相機,移動相機,使其面向立方體,刪掉tick中的對網(wǎng)格體旋轉(zhuǎn)的部分。

// 相機

const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 1, 1000)

// const aspectRatio = sizes.width / sizes.height

// const camera = new THREE.OrthographicCamera(- 1 * aspectRatio, 1 * aspectRatio, 1, - 1, 0.1, 100)

// camera.position.x = 2

// camera.position.y = 2

camera.position.z = 3

camera.lookAt(mesh.position)

scene.add(camera)

以用鼠標控制相機為例,首先需要知道鼠標坐標??梢允褂迷鶭avaScript的偵聽事件addEventListener,來偵聽mousemove事件。

坐標將通過回調(diào)函數(shù)返回,比如event.clientX和event.clientY

// 鼠標

window.addEventListener('mousemove', (event) =>

{

? ? console.log(event.clientX, event.clientY)

})

此處的返回值可以直接使用,但是調(diào)整下值的范圍會更好,比如調(diào)整為區(qū)間為1的值。

比如,對于x:

? ? 當光標位于畫布左側(cè),輸出-0.5

? ? 當光標位于畫布中間,輸出0

? ? 當光標位于畫布右側(cè),輸出0.5

當然也可以采用其它值。

仿照size變量,這里創(chuàng)建個cursor變量,變量默認屬性包含x和y,當觸發(fā)mousemove回調(diào)時,更新屬性的值

// 鼠標

const cursor = {

? ? x: 0,

? ? y: 0

}

window.addEventListener('mousemove', (event) =>

{

? ? cursor.x = event.clientX / sizes.width - 0.5

? ? cursor.y = event.clientY / sizes.height - 0.5

? ? console.log(cursor.x, cursor.y)

})

event.clientX除以sizes.width會返回一個介于0~1之間的值,通過減去一個0.5,讓值分布在0.5~-0.5

這樣,就完成了鼠標位置的存儲,并且可以通過tick更新位置。

const tick = () =>

{

? ? // ...

? ? // 更新相機

? ? camera.position.x = cursor.x

? ? camera.position.y = cursor.y

? ? // ...

}

運行后,會發(fā)現(xiàn)沿y軸運動方向不對,這是因為Three.js中positon.y向上運動時為正,在網(wǎng)頁中clientY向下運動時為正。

通過在cursor.y上添加負號,來糾正。

window.addEventListener('mousemove', (event) =>

{

? ? cursor.x = event.clientX / sizes.width - 0.5

? ? cursor.y = - (event.clientY / sizes.height - 0.5)

})

可以通過cursor.x和cursor.y乘上一個數(shù)字,來調(diào)整幅度大小。此時仍可通過lookAt()方法調(diào)整相機朝向。

const tick = () =>

{

? ? // ...

? ? // 更新相機

? ? camera.position.x = cursor.x * 5

? ? camera.position.y = cursor.y * 5

? ? camera.lookAt(mesh.position)

? ? // ...

}

更進一步,還可以通過使用Math.sin()和Math.cos()實現(xiàn)相機繞網(wǎng)格體旋轉(zhuǎn)。

同時使用cos和sin時,可以讓運動變成圓周運動。要運動成完成的圓,角度的大小必須是Π的2倍??梢酝ㄟ^使用Math.PI,使用Π的值。

要增加圓的半徑,可以簡單的將Math.sin()和Math.cos()結(jié)果乘上一個數(shù)。

const tick = () =>

{

? ? // ...

? ? // 更新相機

? ? camera.position.x = Math.sin(cursor.x * Math.PI * 2) * 2

? ? camera.position.z = Math.cos(cursor.x * Math.PI * 2) * 2

? ? camera.position.y = cursor.y * 3

? ? camera.lookAt(mesh.position)

? ? // ...

}

tick()

這只是一個自定義控制的例子,Three.js內(nèi)置了許多控件類,用來進行許多的相似操作。

內(nèi)建控件

如果在Three.js文檔中搜索控件,會看到很多現(xiàn)成的控件。

DeviceOrientationControls設備陀螺儀控件

DeviceOrientationControls設備陀螺儀控件,就是根據(jù)權(quán)限許可,依照設備方向相應的旋轉(zhuǎn)相機。如果設備合適,可以用來創(chuàng)建全景圖或者VR效果。

FlyControls飛行控件

FlyControls飛行控件允許像飛船一樣控制相機,此時可在三個軸上任意旋轉(zhuǎn)、并可前后移動。

FirstPersonControls第一人稱控件

FirstPersonControls第一人稱控件類似上一個,但具有一個固定向上的軸,即不能向前后左右翻轉(zhuǎn),雖然名字里有第一人稱,但是相機本身不含人。

PointerLockControls指針鎖定控件

PointerLockControls指針鎖定控件使用了JavaScript的pointer lock API。通過這個,隱藏了鼠標指針,并使其居中,但仍在事件回調(diào)中繼續(xù)發(fā)送移動。使用這個API,可以創(chuàng)建FPS游戲,但是,這個API只提供了相機的旋轉(zhuǎn),相機的移動和游戲中的物理效果仍舊需要自行處理。

OrbitControls環(huán)繞控件

OrbitControls環(huán)繞控件類似于上面的自定義控件的例子??梢酝ㄟ^左鍵繞某個點旋轉(zhuǎn),使用鼠標右鍵橫向平移,使用滾輪放大縮小。

TrackballControls軌跡球控件

TrackballControls軌跡球控件類似OrbitControls環(huán)繞控件,但是可以在豎直方向上翻轉(zhuǎn)。

TransformControls變換控件

TransformControls變換控件和相機無關(guān)??梢杂盟o對象添加一個控件,用來移動對象。

這里只講OrbitControls環(huán)繞控件

OrbitControls環(huán)繞控件

注釋掉tick中更新相機的部分。

實例化

首先,使用OrbitControls類實例化變量。

由于使用了Webpack,將會以以下形式引入

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

之后就可以直接實例化OrbitControls 類,但要在創(chuàng)建相機后執(zhí)行。

要使其正常工作,需要向其傳入相機和畫布,然后實例會自己處理鼠標事件。

// 控件

const controls = new OrbitControls(camera, canvas)

這樣就可以使用鼠標左鍵或者右鍵來移動相機,使用滾輪進行縮放。

比自定義控件方便得多,且?guī)в性S多控件。然后再往更深層次研究。

target目標

默認情況下,相機看向場景中心??梢酝ㄟ^target目標屬性修改它。

這個屬性是個Vector3,意思就是通過x、y、z三個屬性定義的。

如果希望OrbitControls環(huán)繞控件默認查看立方體上方,只需要增加y值。

controls.target.y = 2

這個用處不是很大,先注釋掉它。

Damping阻尼

Damping阻尼通過加速度和摩擦力公式來平滑動畫。

要啟用阻尼,請將controls的enableDamping屬性設置為true。

為了正常運行,還需要通過在每幀調(diào)用controls.update()。可以通過tick()實現(xiàn)。

// 控件

const controls = new OrbitControls(camera, canvas)

controls.enableDamping = true

// ...

const tick = () =>

{

? ? // ...

? ? // Update controls

? ? controls.update()

? ? // ...

}

這樣控件看起來更加流暢。

阻尼可以用在各種旋轉(zhuǎn)、移動、縮放、角度控制上,甚至按鍵綁定上。

何時使用內(nèi)建控件

雖然這些控件很方便,但是都有局限性。如果過于依賴它們,可能需要以意想不到的方式改變類的工作方式。

首先請確保所需的功能,然后檢查內(nèi)建控件的功能是否完全覆蓋所需的。

否則,需要自己建立控件。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容