iT邦幫忙

2022 iThome 鐵人賽

DAY 19
1
Modern Web

Three.js 學習日誌系列 第 19

Day18 - Three.js與滑鼠互動操作(二)

  • 分享至 

  • xImage
  •  

Day18 - Three.js與滑鼠互動操作(一)

這裡是「Three.js學習日誌」的第18篇,這一篇主要是接續上一篇的簡易OrbitControl實作,並加入一點進階的內容。這系列的文章假設讀者看得懂javascript,並且有Canvas 2D Context的相關知識。

來自製一個陽春版本的OrbitControl吧 - 進階篇

img

在上一回我們透過定義水平環形軌道&垂直軸的攝影機移動方式來實作了簡易版本的OrbitControls

但我猜應該多少有人注意到像這樣的作法,實際成品好像和Three.js所提供的OrbitControls不太一樣。

尤其是讓Torus mesh沿著X軸旋轉的時候,差異的狀況最為明顯。

原版OrbitControls 自製OrbitControls
img img

我們在上一回自製的版本除了水平旋轉的方向相反以外,垂直翻轉似乎轉不太動(?)

這個原因就是在於我們是用卡式座標(Cartesian)的概念去計算攝影機位置。

img

我們之前的計算方式實際是讓攝影機在一個環形牆面上移動。

如果想要達成像是原版OrbitControls的特效的話,那我們得把計算座標的方式改成極座標運算才行。

img

不熟極座標運算或是已經還給高中老師的人可以看這邊~

Three.js中,官方其實是有提供極座標運算的方法的,也就是Spherical

img

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();

img

Perfect !

codepen連結:點我

最後的最後再來玩個有趣的東西 - 實做Damping

Three.js原版所提供的OrbitControls是有controls.enableDamping這個Option的。

Damping指的是阻尼的意思,它可以讓使用者在操作OrbitControls的時候,產生一個等減速度的操作體感。

而我們這邊就是要來示範一下,要怎麼為我們自製的OrbitControls加上Damping

老實說其實不難。

我們實做OrbitControls的原理是透過換算每一個Framecursor的數值,而現在的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~

img

這邊光看圖片其實看不太出來,可以直接在codepen上試試~

codepen 連結:點我

小結

我們這次學習了要怎麼使用Three.js提供的極座標運算方法,並且成功實做了一個附帶Damping效果的簡易OrbitControls,如果有興趣的話其實可以接續著研究要怎麼實踐原版OrbitControls的其餘機能XD。

明天我們預計要來講講與滑鼠懸停/點擊息息相關的Raycasting,敬請期待!


上一篇
Day17 - Three.js與滑鼠互動操作(一)
下一篇
Day19 - Three.js與滑鼠互動操作(三)
系列文
Three.js 學習日誌31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言