質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Three.js

Three.jsはWebGLをサポートしているJavaScriptの3D描画用ライブラリです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

0回答

1458閲覧

原点に固定したカメラの向きを、DeviceOrientationとMouseMoveを両方使用して変更したい

退会済みユーザー

退会済みユーザー

総合スコア0

Three.js

Three.jsはWebGLをサポートしているJavaScriptの3D描画用ライブラリです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2019/05/22 00:47

three.jsを使用して360度画像を見ることができるウェブページを作ろうとしています。
途中まではうまくいっていたのですが、スマートフォンの傾きと連動させてカメラの向きを変えようとする部分で行き詰ってしまいました。
そもそも行列やベクトルの概念に疎く(勉強中ではありますが)、three.jsのそれぞれのメソッド()の働きについてもいろいろ試してみてたまたまうまくいっているような状態です。

以下私が出来たことと出来なかったこと、実装コードを記載いたします。なにかヒントでも頂ければ幸いです。よろしくお願いいたします。

出来たこと

  1. XYZ空間の原点を中心として SphereGeometry を使用したメッシュを配置し、360度画像をメッシュの内側に描画
  2. XYZ空間の原点に PerspectiveCamera を配置し、レンダラーを通して Canvas にカメラに写っているメッシュを表示
  3. mousedown, mouseup, mousemoveなどのイベントから、マウスやタップの位置を利用してカメラの向きを変更
  4. deviceorientation のイベントから、スマホの向きを利用してカメラの向きを変更

出来なかったこと

  • 出来たこと3および4はそれぞれ単独で動作するが、連動して動作しない

(タップでカメラの向きを変更しても、指を話すとスマホの向きにカメラが戻ってしまう)
(カメラが0時の方向を向いた状態で、タップでカメラを3時の方向に向けても、指を離すと0時の方向に戻ってしまう)
(期待動作としては、上記の状態で指を離してもカメラの向きが3時のままで、そこを起点としてさらにスマホの向きでカメラの向きを変更できるようにしたい)

実装コード

一応全て載せましたが、コアの部分は add-gestures.js です。

index.html

html

1<!doctype html> 2<html> 3 <head> 4 <meta name="viewport" content="width=device-width,initial-scale=1"> 5 6 <link rel="stylesheet" href="pages/vr/style.css') ?> 7 </head> 8 9 <body> 10 <canvas id="vr-canvas"></canvas> 11 12 <script src="/js/lib/threejs/r104/build/three.min.js"></script> 13 14 <script src="/js/pages/vr/init-vr.js"></script> 15 <script src="/js/pages/vr/add-gestures.js"></script> 16 <script src="/js/pages/vr/add-sphere.js"></script> 17 </body> 18</html>

init-vr.js

javascript

1window.VRApp = window.VRApp || {}; 2 3const canvas = document.querySelector("#vr-canvas"); 4 5const renderer = (() => { 6 const webGLRenderer = new THREE.WebGLRenderer({ canvas }); 7 8 webGLRenderer.setPixelRatio(window.devicePixelRatio); 9 10 return webGLRenderer; 11})(); 12 13const scene = new THREE.Scene(); 14 15const camera = (() => { 16 const perspectiveCamera = new THREE.PerspectiveCamera(100, canvas.width / canvas.height, 0.01, 100); 17 18 perspectiveCamera.rotation.order = "ZYX"; 19 20 return perspectiveCamera; 21})(); 22 23const animate = () => { 24 requestAnimationFrame(animate); 25 26 renderer.render(scene, camera); 27}; 28 29animate(); 30 31window.VRApp.renderer = renderer; 32window.VRApp.scene = scene; 33window.VRApp.camera = camera;

add-gestures.js

javascript

1window.VRApp = window.VRApp || {}; 2 3const State = { 4 Neutral: 0x0000, 5 RotateCamera: 0x0001, 6}; 7 8let state = State.Neutral; 9 10let windowOrientation = window.orientation || 0; 11let cameraRotationCache = window.VRApp.camera.rotation.clone(); 12 13let mousePositionCache = { 14 x: 0, 15 y: 0, 16 minYDiff: 0, 17 maxYDiff: 0, 18}; 19 20const setState = (newState) => { 21 if (State.hasOwnProperty(newState)) { 22 state = State[newState]; 23 } 24}; 25 26const checkState = (targetState) => { 27 if (State.hasOwnProperty(targetState)) { 28 return state === State[targetState]; 29 } 30 31 return false; 32}; 33 34const setQuaternion = (() => { 35 const zee = new THREE.Vector3(0, 0, 1); 36 const euler = new THREE.Euler(); 37 const q0 = new THREE.Quaternion(); 38 const q1 = new THREE.Quaternion(-1 * Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)); 39 40 return (alpha, beta, gamma, orientation) => { 41 euler.set(beta, alpha, -1 * gamma, "YXZ"); 42 43 window.VRApp.camera.quaternion.setFromEuler(euler); 44 window.VRApp.camera.quaternion.multiply(q1); 45 window.VRApp.camera.quaternion.multiply(q0.setFromAxisAngle(zee, -1 * orientation)); 46 }; 47})(); 48 49const onMouseDownHandler = (clientX, clientY) => { 50 setState("RotateCamera"); 51 52 cameraRotationCache = window.VRApp.camera.rotation.clone(); 53 54 mousePositionCache.x = clientX; 55 mousePositionCache.y = clientY; 56 mousePositionCache.minYDiff = -90 - (cameraRotationCache.x * (180 / Math.PI)) - (clientY * (Math.PI / 180)); 57 mousePositionCache.maxYDiff = 90 - (cameraRotationCache.x * (180 / Math.PI)) - (clientY * (Math.PI / 180)); 58}; 59 60const onMouseMoveHandler = (clientX, clientY) => { 61 if (checkState("RotateCamera")) { 62 window.VRApp.camera.rotation.order = "ZYX"; 63 64 let xDiff = clientX - mousePositionCache.x; 65 let yDiff = clientY - mousePositionCache.y; 66 67 if (yDiff < mousePositionCache.minYDiff) { 68 yDiff = mousePositionCache.minYDiff; 69 70 mousePositionCache.y = clientY - mousePositionCache.minYDiff; 71 } 72 73 if (yDiff > mousePositionCache.maxYDiff) { 74 yDiff = mousePositionCache.maxYDiff; 75 76 mousePositionCache.y = clientY - mousePositionCache.maxYDiff; 77 } 78 79 let newAngleX = cameraRotationCache.x + (yDiff * (Math.PI / 180)); 80 let newAngleY = cameraRotationCache.y + (xDiff * (Math.PI / 180)); 81 82 window.VRApp.camera.rotation.x = newAngleX; 83 window.VRApp.camera.rotation.y = newAngleY; 84 } 85}; 86 87const onMouseUpHandler = () => { 88 setState("Neutral"); 89 90 cameraRotationCache = window.VRApp.camera.rotation.clone(); 91 92 mousePositionCache.x = 0; 93 mousePositionCache.y = 0; 94 mousePositionCache.minYDiff = 0; 95 mousePositionCache.maxYDiff = 0; 96}; 97 98if ("onresize" in window) { 99 window.addEventListener("resize", (event) => { 100 const width = window.innerWidth; 101 const height = window.innerHeight; 102 103 window.VRApp.renderer.domElement.width = width; 104 window.VRApp.renderer.domElement.height = height; 105 106 window.VRApp.renderer.domElement.style.height = height + "px"; 107 108 window.VRApp.renderer.setSize(width, height); 109 110 window.VRApp.camera.aspect = width / height; 111 window.VRApp.camera.updateProjectionMatrix(); 112 }); 113} 114 115if ("onload" in window) { 116 window.addEventListener("load", (event) => { 117 const width = window.innerWidth; 118 const height = window.innerHeight; 119 120 window.VRApp.renderer.domElement.width = width; 121 window.VRApp.renderer.domElement.height = height; 122 123 window.VRApp.renderer.domElement.style.height = height + "px"; 124 125 window.VRApp.renderer.setSize(width, height); 126 127 window.VRApp.camera.aspect = width / height; 128 window.VRApp.camera.updateProjectionMatrix(); 129 }); 130} 131 132if ("onmousedown" in window.VRApp.renderer.domElement) { 133 window.VRApp.renderer.domElement.addEventListener("mousedown", (event) => { 134 onMouseDownHandler(event.clientX, event.clientY); 135 }); 136} 137 138if ("onmousemove" in window.VRApp.renderer.domElement) { 139 window.VRApp.renderer.domElement.addEventListener("mousemove", (event) => { 140 onMouseMoveHandler(event.clientX, event.clientY); 141 }); 142} 143 144if ("onmouseup" in window.VRApp.renderer.domElement) { 145 window.VRApp.renderer.domElement.addEventListener("mouseup", (event) => { 146 onMouseUpHandler(); 147 }); 148} 149 150if ("onmouseleave" in window.VRApp.renderer.domElement) { 151 window.VRApp.renderer.domElement.addEventListener("mouseleave", (event) => { 152 onMouseUpHandler(); 153 }); 154} 155 156if ("ontouchstart" in window.VRApp.renderer.domElement) { 157 window.VRApp.renderer.domElement.addEventListener("touchstart", (event) => { 158 event.preventDefault(); 159 160 if (event.touches.length === 1) { 161 const touch = event.touches[0]; 162 163 onMouseDownHandler(touch.clientX, touch.clientY); 164 } 165 }); 166} 167 168if ("ontouchmove" in window.VRApp.renderer.domElement) { 169 window.VRApp.renderer.domElement.addEventListener("touchmove", (event) => { 170 event.preventDefault(); 171 172 if (event.touches.length === 1) { 173 const touch = event.touches[0]; 174 175 onMouseMoveHandler(touch.clientX, touch.clientY); 176 } 177 }); 178} 179 180if ("ontouchend" in window.VRApp.renderer.domElement) { 181 window.VRApp.renderer.domElement.addEventListener("touchend", (event) => { 182 event.preventDefault(); 183 184 onMouseUpHandler(); 185 }); 186} 187 188if ("ontouchcancel" in window.VRApp.renderer.domElement) { 189 window.VRApp.renderer.domElement.addEventListener("touchcancel", (event) => { 190 event.preventDefault(); 191 192 onMouseUpHandler(); 193 }); 194} 195 196if ("onorientationchange" in window) { 197 window.addEventListener("orientationchange", (event) => { 198 windowOrientation = window.orientation || 0; 199 }); 200} 201 202if ("ondeviceorientation" in window) { 203 window.addEventListener("deviceorientation", (event) => { 204 if (checkState("Neutral")) { 205 let alpha = event.alpha * (Math.PI / 180); 206 let beta = event.beta * (Math.PI / 180); 207 let gamma = event.gamma * (Math.PI / 180); 208 let orientation = windowOrientation * (Math.PI / 180); 209 210 setQuaternion(alpha, beta, gamma, orientation); 211 } 212 }); 213}

add-sphere.js

javascript

1window.VRApp = window.VRApp || {}; 2 3const sphere = (() => { 4 const geometry = new THREE.SphereGeometry(100, 64, 64); 5 6 geometry.scale(1, 1, -1); 7 geometry.rotateY(Math.PI / 2); 8 9 const material = new THREE.MeshBasicMaterial({ 10 }); 11 12 const mesh = new THREE.Mesh(geometry, material); 13 14 return mesh; 15})(); 16 17const textureLoader = new THREE.TextureLoader(); 18const texture = textureLoader.load("/img/pages/vr/sample-360.jpg"); 19 20sphere.material.map = texture; 21 22window.VRApp.scene.add(sphere);

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問