地震速報や宇宙の3Dビジュアライゼーションは、PCの大画面だけでなく「今まさに揺れを感じた瞬間にポケットから取り出したスマートフォン」の上で快適に動作しなければ、実用的な防災ツールとは言えません。
しかし、モバイルブラウザで3D球体を操作する際、多くのユーザーが直面するのが「指の動きと地球の回転が滑ってズレる」「北極付近でカメラが突然あらぬ方向へ反転する」「指を離すとピタッと止まって無機質に感じる」というストレスです。QuakeViewer3Dでは、まるで机の上の本物の地球儀を指先でスピンさせているかのような、吸い付くような操作感を実現しました。本記事では、その数学的アルゴリズムとPWA防災ダッシュボードのUI/UX設計を解説します。
1. なぜ標準のOrbitControlsではモバイル操作に違和感が出るのか?
Three.jsで広く使われている OrbitControls は、基本的にPCのマウス操作(左右ドラッグで方位角、上下ドラッグで仰角を変更する球面極座標系)を前提に設計されています。
この方式をスマートフォンのタッチスクリーンにそのまま適用すると、以下のような致命的な操作感の乖離が発生します:
- 接触点の滑り(Slip Artifact): ユーザーは「触れている日本列島を指で右に引っ張りたい」のに、極座標変換の仕様上、画面端をドラッグした際に指の移動量と地球表面の移動量が一致せず、指がテクスチャの上を滑る感覚が生じます。
- 極付近での特異点(Gimbal Singularity): 仰角が天頂(北極)または天底(南極)に近づくと、方位角の回転軸が縮退し、カメラが急激に180度反転してユーザーの空間識失調(空間見失い)を引き起こします。
2. アークボール(Arcball)投影とクォータニオンによる全方位球面回転
QuakeViewer3Dでは、この問題を解決するためにアークボール(Arcball / Trackball)回転アルゴリズムをゼロから自作しました。
画面上の2Dタッチ座標 $(x, y)$ を、画面背後にある仮想的な3D単位球面上の一点 $\vec{v} = (v_x, v_y, v_z)$ に射影します。指が前回のフレームの点 $\vec{v_1}$ から今回の点 $\vec{v_2}$ に移動したとき、その2つのベクトルの外積と内積から回転軸と回転角を算出し、四元数(Quaternion)として合成します:
// 2Dスクリーン座標を3D仮想球面上に射影する関数
function projectToSphere(x, y, width, height) {
const r = Math.min(width, height) * 0.45;
const px = (x - width / 2) / r;
const py = (height / 2 - y) / r;
const d2 = px * px + py * py;
if (d2 <= 1.0) {
return new THREE.Vector3(px, py, Math.sqrt(1.0 - d2)).normalize();
} else {
// 球の外側をドラッグした場合は双曲線でスムーズに減衰
return new THREE.Vector3(px, py, 1.0 / (2.0 * Math.sqrt(d2))).normalize();
}
}
// タッチ移動イベントでのクォータニオン回転合成
function onTouchMove(currPos, prevPos) {
const v1 = projectToSphere(prevPos.x, prevPos.y, viewWidth, viewHeight);
const v2 = projectToSphere(currPos.x, currPos.y, viewWidth, viewHeight);
const axis = new THREE.Vector3().crossVectors(v1, v2).normalize();
const angle = Math.acos(Math.min(1.0, v1.dot(v2))) * ROTATION_SENSITIVITY;
const deltaQuat = new THREE.Quaternion().setFromAxisAngle(axis, angle);
globeGroup.quaternion.premultiply(deltaQuat);
}
クォータニオン演算をベースとすることで、特異点やジンバルロックは原理的に存在しなくなり、どの方向へ指をスワイプしても、指が触れた地表が指先に吸い付くようにダイレクトに追従します。
3. 物理シミュレーションに基づく指数減衰慣性(Inertial Damping)
デジタル地球儀に生命を吹き込むのが「慣性スクロール(Inertia)」です。指を勢いよくフリックして離した際、瞬間的に地球儀が停止すると安っぽさを感じさせます。
QuakeViewer3Dでは、指が離れる直前3フレームの平均角速度ベクトル $\vec{\omega}$ を計算し、指を離した瞬間に慣性シミュレーションモードへ移行させます。毎フレーム、物理学における粘性摩擦抵抗を模した指数減衰係数($\gamma \approx 0.94$)を乗算して徐々に回転を落ち着かせます:
// アニメーションループ内での慣性回転ステップ
function updateInertia(deltaTime) {
if (!isUserTouching && angularVelocity.lengthSq() > 0.000001) {
const angle = angularVelocity.length() * deltaTime;
const axis = angularVelocity.clone().normalize();
const deltaQuat = new THREE.Quaternion().setFromAxisAngle(axis, angle);
globeGroup.quaternion.premultiply(deltaQuat);
// 粘性減衰(摩擦力)の適用
angularVelocity.multiplyScalar(Math.pow(0.92, deltaTime * 60));
}
}
この慣性計算により、ユーザーは地球儀を勢いよくスピンさせて地球の裏側へ一瞬で移動したり、ゆっくり指先で微調整したりといった、直感的で心地よいフィジカルな操作感を体験できます。
4. 片手操作に最適化したボトムシートHUDとPWA(Progressive Web App)設計
モバイル画面の専有面積は極めて貴重です。画面上部に大きな固定コントロールバーを配置すると、せっかくの3D地球儀が半分隠れてしまいます。
- グラスモーフィズム・ボトムシート: スマートフォンを片手で持った親指の可動域(Thumb Zone)に合わせ、主要なフィルター(深度選択、マグニチュード下限、日付レンジ)を画面下部のスライド式半透明ドック(
backdrop-filter: blur(16px))に集約しました。 - PWAフルスクリーン体験:
manifest.jsonと Service Workerを配備し、SafariやChromeから「ホーム画面に追加」するだけで、ブラウザのアドレスバーが完全に消去されたネイティブアプリ同等の全画面防災モニターとして即時起動します。 - オフラインキャッシュ耐性: 震源地のプレート境界線GeoJSONや基本地図テクスチャをCache APIに事前キャッシュすることで、通信環境が極度に悪化した被災現場でも直前の震源データと3D地形をオフライン表示できる堅牢性を確保しました。
5. まとめとフロントエンドUXの追求
Webブラウザ上で動作する3Dアプリだからといって、ネイティブアプリに操作感で劣ってよい理由はありません。
「アークボールクォータニオンによる幾何学的追従」「物理慣性シミュレーションによる滑らかな手触り」「親指に最適化したPWAインターフェース」の3点を徹底的に作り込むことで、QuakeViewer3Dは緊急時にもストレスなく頼れる防災プラットフォームとしての完成度を高めました。