6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

THREE.DeviceOrientationControls のデバイス差を吸収する

Last updated at Posted at 2022-04-22

Three.js で Web VR/AR アプリを作るとき、スマホの向きに応じて空間内のカメラの向きを操作するのに THREE.DeviceOrientationControls が使われる。しかしこのクラスを使うのは一筋縄ではいかず、その課題と対処法についてまとめる。

THREE.DeviceOrientationControls のデバイス差問題

Three.js でスマホのジャイロセンサーと空間内のカメラを同期させる方法を調べると、だいたい THREE.DeviceOrientationControls を使用するやり方が出てくる。

しかしこのクラスは2021年10月にリリースされた r134 にて削除されてしまっている。

関連する Issue を読んでいくと、以下のことがわかる。

  • #15828
    • デバイスによって THREE.DeviceOrientationControls を使用した場合の初期方向が異なってしまう問題が発生している
    • → これはデバイスの実装に依存するもので、Three.js 側では対応しない
  • #22613
    • (Issue の内容としては同様)
    • → このクラスを削除することに (r134 で削除)
  • #22996
    • THREE.DeviceOrientationControls を復活させてほしいという Issue
    • → クラスを復活させることはしないが、ユーザが過去のコミットからクラスを復元して使用しても問題ない

クラスそのものは過去のものを復元して使用すればいいとして、デバイス差が発生する問題は解決されていない。

実際に自分で試した際にも、iPhone 11 (Safari) で初期状態で正面になるようにオブジェクトを配置したところ、それを Pixel 6 (Chrome) で見た際には左側に表示されてしまった。(初期状態でのカメラの向きが90°ズレている)

デバイス差を無理矢理に吸収する

初期状態でのジャイロセンサーの向きの値を取得して、それをオフセットとして設定して相殺してやればデバイス間の差分は吸収できるだろう、という雑な考え。

const controls = ... // DeviceOrientationControls オブジェクト

/**
 * スクリーンが垂直になるようにスマホを持っているときの、スクリーン裏面が向いている方角 (-180 〜 180) を計算する
 */
function calcDeviceDirection(e: DeviceOrientationEvent): number {
  const ry = ((e.gamma || 0) * Math.PI) / 180
  const rx = ((e.beta || 0) * Math.PI) / 180
  const rz = ((e.alpha || 0) * Math.PI) / 180
  const cy = Math.cos(ry)
  const sy = Math.sin(ry)
  const cx = Math.cos(rx)
  const sx = Math.sin(rx)
  const cz = Math.cos(rz)
  const sz = Math.sin(rz)
  const x = -(sy * cz + cy * sx * sz)
  const y = -(sy * sz - cy * sx * cz)
  const z = -(cy * cx)

  const angle = Math.atan2(-x, y) * (180.0 / Math.PI)
  return angle
}

window.addEventListener(
  'deviceorientation',
  (event: DeviceOrientationEvent) => {
    const deg = calcDeviceDirection(event)
    const rad = deg * (Math.PI / 180) // deg2rad (-π 〜 π)
    if (controls) controls.alphaOffset -= rad
  },
  { once: true }
)

一度だけ 'deviceorientation' イベントを受け取って DeviceOrientationControls のオフセットを更新している。

例えば この example コード でいえば、init 関数の末尾にこの処理を追加しておくだけでよい。これで各端末で同じ方向を向けるようになった。

参考

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?