這裡是「Three.js學習日誌」的第18篇,這一篇主要是接續上一篇的簡易OrbitControl實作,並加入一點進階的內容。這系列的文章假設讀者看得懂javascript,並且有Canvas 2D Context的相關知識。
在上一回我們透過定義水平環形軌道&垂直軸的攝影機移動方式來實作了簡易版本的OrbitControls
。
但我猜應該多少有人注意到像這樣的作法,實際成品好像和Three.js
所提供的OrbitControls
不太一樣。
尤其是讓Torus mesh
沿著X
軸旋轉的時候,差異的狀況最為明顯。
原版OrbitControls |
自製OrbitControls |
---|---|
我們在上一回自製的版本除了水平旋轉的方向相反以外,垂直翻轉似乎轉不太動(?)
這個原因就是在於我們是用卡式座標(Cartesian)的概念去計算攝影機位置。
我們之前的計算方式實際是讓攝影機在一個環形牆面上移動。
如果想要達成像是原版OrbitControls
的特效的話,那我們得把計算座標的方式改成極座標運算才行。
不熟極座標運算或是已經還給高中老師的人可以看這邊~
在Three.js
中,官方其實是有提供極座標運算的方法的,也就是Spherical
。
Spherical
本身並沒有提供可以轉化成Vector3
的方法,但我們可以用Vector3
底下的setFromSpherical
來做轉化的動作。
為了達成我們的目標,我們主要需要改寫的部分是tick loop
的函數。
原本的
tick loop
長這樣
const tick = () => {
camera.position.x = Math.sin(cursor.x * Math.PI) * railRadius;
camera.position.z = Math.cos(cursor.x * Math.PI) * railRadius;
camera.position.y = cursor.y;
camera.lookAt(new Vector3(0, 0, 0));
renderer.render(scene, camera);
requestAnimationFrame(tick);
};
tick();
而我們需要改寫的方向就是把cursor
的X/Y數值改為映射到φ
和θ
上面。
因為我們希望極座標的起始位置會從90度開始,這樣才可以看到正面的Torus
,所以φ
要加上 Math.PI / 2
做為Offset。
除此之外,我們還希望讓水平旋轉的方向與原版OrbitControls
相同,所以θ
必須要乘以-1。
最後建立起來的Spherical
還要記得發動makeSafe
方法。
makeSafe
方法主要是用來把φ
的變化限制在0~PI
之間。因為大於等於PI
的φ
角可能會造成座標軸方向的突然變換。
const tick = () => {
const sp = new Spherical(
railRadius,
// 因為我們希望極座標的起始位置會從90度開始,這樣才可以看到正面的Torus,所以這邊要加上 Math.PI / 2
Math.PI * cursor.y + Math.PI / 2,
-Math.PI * cursor.x // 讓水平旋轉方向相反
).makeSafe();
const location = new Vector3().setFromSpherical(sp);
camera.position.x = location.x;
camera.position.y = location.y;
camera.position.z = location.z;
camera.lookAt(new Vector3(0, 0, 0));
renderer.render(scene, camera);
requestAnimationFrame(tick);
};
tick();
Perfect !
codepen連結:點我
在Three.js
原版所提供的OrbitControls
是有controls.enableDamping
這個Option的。
Damping
指的是阻尼的意思,它可以讓使用者在操作OrbitControls
的時候,產生一個等減速度的操作體感。
而我們這邊就是要來示範一下,要怎麼為我們自製的OrbitControls
加上Damping
。
老實說其實不難。
我們實做OrbitControls
的原理是透過換算每一個Frame
上cursor
的數值,而現在的cursor
數值則是會根據滑鼠當前的位置做更新,也就是說,如果要實現Damping
,那就是要在mousemove
傳遞數值給cursor
的過程中做手腳。
這邊我們使用GSAP
套件的.to
方法來達成這件事。
renderer.domElement.addEventListener("mousemove", (ev) => {
const rect = renderer.domElement.getBoundingClientRect();
gsap
.to(cursor, {
x: ((ev.clientX - rect.left) / rect.width - 0.5) * 2,
y: -((ev.clientY - rect.top) / rect.height - 0.5) * 2,
duration: 1, // 用Tween的方式刻意的讓傳遞數值的動作產生delay
paused: true
})
.play();
});
ba-da-bean~ ba-da-boon~
這邊光看圖片其實看不太出來,可以直接在codepen上試試~
codepen 連結:點我
我們這次學習了要怎麼使用Three.js
提供的極座標運算方法,並且成功實做了一個附帶Damping
效果的簡易OrbitControls
,如果有興趣的話其實可以接續著研究要怎麼實踐原版OrbitControls
的其餘機能XD。
明天我們預計要來講講與滑鼠懸停/點擊息息相關的Raycasting
,敬請期待!