Three.jsの続きです。前回は三角関数を使ってカメラの動きを制御しました。今回はさらにカメラの位置をインタラクティブに操作できるようにします。Three.jsの拡張ライブラリ「OrbitControls.js」を使います。それでは行きましょう!
【目次】
※参考:【Three.js】カメラ制御を実現している「三角関数」を理解する!(サイン、コサイン、タンジェント) - クモのようにコツコツと
※Three.jsを習得するためにやったことまとめ
qiita.com
前回作ったもの
前回、ics.Mediaの「Three.js入門」の「カメラの制御」を参考に進めた。
※参考:Three.jsのカメラの制御 - ICS MEDIA
作ったものこちら。
See the Pen three.js camera-1 by イイダリョウ (@i_ryo) on CodePen.
イイダ星と周りの星屑が一緒に回転しているので、イイダ星自体ではなくカメラが回っていることがわかる。
詳細は前回記事を参照
※参考:【Three.js】カメラ制御を実現している「三角関数」を理解する!(サイン、コサイン、タンジェント) - クモのようにコツコツと
この時は初めて*1出会った三角関数の理解がメインになった。
実は、記事にはまだ続きがある。カーソルの位置に合わせてカメラが回転する向きと速度が変わるバージョンがある。今回はまずここからトライ。
カーソルの位置に合わせてカメラが回転する向きと速度を変える
作ったもの
See the Pen three.js camera-2 by イイダリョウ (@i_ryo) on CodePen.
マウスを左右に動かすとカメラが回転する向きと速度が変わる。(スマホだと変わらないぽい)
JSコード全体
// ページの読み込みを待つ window.addEventListener('load', init); function init() { // サイズを指定 const width = 960; const height = 540; let rot = 0; let mouseX = 0; // マウス座標 // レンダラーを作成 const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#myCanvas') }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); // シーンを作成 const scene = new THREE.Scene(); // カメラを作成 const camera = new THREE.PerspectiveCamera(45, width / height); // ジオメトリを作成(球体) const geometry = new THREE.SphereGeometry(300, 30, 30); // 画像を読み込む const loader = new THREE.TextureLoader(); const texture = loader.load('https://s.cdpn.io/profiles/user/305282/512.jpg'); // マテリアルを作成 const material = new THREE.MeshStandardMaterial({map: texture}); //メッシュを作成 const mesh = new THREE.Mesh(geometry, material); //シーンにメッシュを適用 scene.add(mesh); // 平行光源 const directionalLight = new THREE.DirectionalLight(0xFFFFFF); directionalLight.position.set(1, 1, 1); // シーンに追加 scene.add(directionalLight); // 星屑を作成 (カメラの動きをわかりやすくするため) createStarField(); function createStarField() { // 形状データを作成 const geometry = new THREE.Geometry(); for (let i = 0; i < 1000; i++) { geometry.vertices.push( new THREE.Vector3( 3000 * (Math.random() - 0.5), 3000 * (Math.random() - 0.5), 3000 * (Math.random() - 0.5) ) ); } // マテリアルを作成 const material = new THREE.PointsMaterial({ size: 10, color: 0xffffff }); // 物体を作成 const mesh = new THREE.Points(geometry, material); scene.add(mesh); } // マウス座標はマウスが動いた時のみ取得できる document.addEventListener('mousemove', event => { mouseX = event.pageX; }); tick(); // 毎フレーム時に実行されるループイベントです function tick() { // マウスの位置に応じて角度を設定 // マウスのX座標がステージの幅の何%の位置にあるか調べてそれを360度で乗算する const targetRot = (mouseX / window.innerWidth) * 360; // イージングの公式を用いて滑らかにする // 値 += (目標値 - 現在の値) * 減速値 rot += (targetRot - rot) * 0.02; // ラジアンに変換する const radian = (rot * Math.PI) / 180; // 角度に応じてカメラの位置を設定 camera.position.x = 1000 * Math.sin(radian); camera.position.z = 1000 * Math.cos(radian); // 原点方向を見つめる camera.lookAt(new THREE.Vector3(0, 0, 0)); // 地球は常に回転させておく mesh.rotation.y += 0.005; // レンダリング renderer.render(scene, camera); requestAnimationFrame(tick); } }
変更点
変更点を部分ごとに見ていく。
まず、マウス座標の変数を設定
let mouseX = 0; // マウス座標
- 変数
mouseX
を設定。初期値は0
次にマウスムーブのイベントを設定
// マウス座標はマウスが動いた時のみ取得できる document.addEventListener('mousemove', event => { mouseX = event.pageX; });
mousemove
イベントを設定。引数名はevent
mouseX
の値をpageX
に
pageX
はJS組み込みのプロパティ
pageX変数には、マウスイベント発火時の水平位置のピクセル数値が格納されます。 この値はドキュメント全体からの相対位置になります。 このプロパティは、ページの水平スクロールを考慮します。
※参考:.pageX | JavaScript 日本語リファレンス | js STUDIO
マウスイベント時のページの中での水平位置を取得する。
最後、tick()
メソッドの中
function tick() { // マウスの位置に応じて角度を設定 // マウスのX座標がステージの幅の何%の位置にあるか調べてそれを360度で乗算する const targetRot = (mouseX / window.innerWidth) * 360; // イージングの公式を用いて滑らかにする // 値 += (目標値 - 現在の値) * 減速値 rot += (targetRot - rot) * 0.02; // 中略 // 地球は常に回転させておく mesh.rotation.y += 0.005; // 中略 } }
- 変数
targetRot
はmouseX
をinnerWidth
で割って360を掛ける rot
にtargetRot
からrot
を引いて、0.02を掛けるmesh
はy
方向に0.005ずつ回転する
innerWidth
はウィンドウ幅のサイズを取得する。
※参考:window.innerWidth - Web API | MDN
変数`targetRot` = マウス横位置 / ウィンドウ横幅 * 360
targetRot
とrot
の差分に0.02を掛けた数値をrot
に加算する。これでマウスの動きとカメラの回転が関連付く。
それとは別で、イイダ星mesh
は1フレーム0.005ずつ回転させる。
OrbitControls.jsを使ってカメラの動きを360度インタラクティブに動かす
次にカメラの動きを360度グリングリン動かす!これがics.Media「Three.js入門」の「入門編」のラストを飾る部分!
※参考:Three.jsのOrbitControlsで手軽にカメラを制御する - ICS MEDIA
作ったもの
See the Pen three.js camera-3 by イイダリョウ (@i_ryo) on CodePen.
マウスを動かすと星の向きが同期して360度回転する!(こちらはスマホでも動いた!)
JSコード(全体)
// ページの読み込みを待つ window.addEventListener('load', init); function init() { // サイズを指定 const width = 960; const height = 540; // レンダラーを作成 const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#myCanvas') }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); // シーンを作成 const scene = new THREE.Scene(); // カメラを作成 const camera = new THREE.PerspectiveCamera(45, width / height); // カメラの初期座標を設定 camera.position.set(0, 0, 1000); // カメラコントローラーを作成 const controls = new THREE.OrbitControls(camera); // 滑らかにカメラコントローラーを制御する controls.enableDamping = true; controls.dampingFactor = 0.2; // ジオメトリを作成(球体) const geometry = new THREE.SphereGeometry(300, 30, 30); // 画像を読み込む const loader = new THREE.TextureLoader(); const texture = loader.load('https://s.cdpn.io/profiles/user/305282/512.jpg'); // マテリアルを作成 const material = new THREE.MeshStandardMaterial({map: texture}); //メッシュを作成 const mesh = new THREE.Mesh(geometry, material); //シーンにメッシュを適用 scene.add(mesh); // 平行光源 const directionalLight = new THREE.DirectionalLight(0xFFFFFF); directionalLight.position.set(1, 1, 1); // シーンに追加 scene.add(directionalLight); // 星屑を作成 (カメラの動きをわかりやすくするため) createStarField(); function createStarField() { // 形状データを作成 const geometry = new THREE.Geometry(); for (let i = 0; i < 1000; i++) { geometry.vertices.push( new THREE.Vector3( 3000 * (Math.random() - 0.5), 3000 * (Math.random() - 0.5), 3000 * (Math.random() - 0.5) ) ); } // マテリアルを作成 const material = new THREE.PointsMaterial({ size: 10, color: 0xffffff }); // 物体を作成 const mesh = new THREE.Points(geometry, material); scene.add(mesh); } tick(); // 毎フレーム時に実行されるループイベントです function tick() { // 地球は常に回転させておく mesh.rotation.y += 0.005; // カメラコントローラーを更新 controls.update(); // レンダリング renderer.render(scene, camera); requestAnimationFrame(tick); } }
OrbitControls.jsを読み込む
この動きはTHREE.OrbitControls
というクラスで実現できる。
※参考:three.js docs
そのためにはThree.js本体とは別に拡張ライブラリの「OrbitControls.js」が必要。
※参考:three.js/OrbitControls.js at master · mrdoob/three.js · GitHub
このように読み込む。
<script src="js/controls/OrbitControls.js"></script>
CodePenで読み込むためにCDNを探した。jsDelivrでCDNを探す方法はこちらがわかりやすい!
※参考:jsDelivrなら超簡単に使いたいライブラリ(プラグイン)のCDNが見つかるかも!|shun|note
下記が見つかった。
※参考:three-orbitcontrols CDN by jsDelivr - A CDN for npm and GitHub
このファイルのリンクパスは下記
https://cdn.jsdelivr.net/npm/three-orbitcontrols@2.110.3/OrbitControls.js
しかしこのパスをリンクしてもエラーになって動かない。デベロッパーツール では下記のエラーコード。
ReferenceError: Can't find variable: require
冒頭にrequire('three')
があり、これはNode系のコードっぽい。
var THREE = require('three')
さらに調べるとこちらのQ&Aに別のパスがあり、
※参考:Is there a site to load THREE library? ( No hotlinking ) - Questions - three.js forum
「OrbitControls.js」単体ではなくThree.js本体のファイル一式*2の
※参考:three CDN by jsDelivr - A CDN for npm and GitHub
「examples」の中にOrbitControls.jsがあった!
three CDN by jsDelivr - A free, fast, and reliable Open Source CDN
こちらのURLをCodePenにリンクしたらエラーがなくなった!
https://cdn.jsdelivr.net/npm/three@0.101.1/examples/js/controls/OrbitControls.js
変更部分
まずカメラの設定を追加
// カメラを作成 const camera = new THREE.PerspectiveCamera(45, width / height); // カメラの初期座標を設定 camera.position.set(0, 0, 1000); // カメラコントローラーを作成 const controls = new THREE.OrbitControls(camera); // 滑らかにカメラコントローラーを制御する controls.enableDamping = true; controls.dampingFactor = 0.2;
camera
オブジェクトのposition
プロパティのset()
メソッドを設定。引数は左から0、0、1000に- 変数
controls
でTHREE.OrbitControls
インスタンスを作成。引数はcamera
に controls
オブジェクトのenableDamping
プロパティ の値をtrue
にcontrols
オブジェクトのdampingFactor
プロパティ の値を0.2に
そして最後のtick()
に追記
function tick() { // 中略 // カメラコントローラーを更新 controls.update(); // 中略 }
controls
オブジェクトのupdate()
メソッドを実行
おお、この1行だけであのグリングリンした動きが実現されるの??こりゃすごい!
.update () : Boolean
Update the controls. Must be called after any manual changes to the camera's transform, or in the update loop if .autoRotate or .enableDamping are set.
.update():ブール コントロールを更新します。 カメラの変換を手動で変更した後、または.autoRotateまたは.enableDampingが設定されている場合は更新ループで呼び出す必要があります。
※参考:three.js docs
最後に
ということで今回はカメラがマウスの動きに反応するインタラクティブな動きを実現できました。前回は三角関数の式を細かく書きましたがOrbitControls.jsを使うとグッと少ないコードでもっと複雑な動きが実現できました!
しかし、この動きがどうやって実現しているのかを想像する上でも、前回のような細かい処理を書いていく経験(車輪の再発明)も必要だなと感じました。車輪の再発明とライブラリの恩恵、両方を体験し、比較することでより有意義な使い方をしていきたいと思います。
ics.Mediaの「Three.js入門」、次回からは「基礎編」に入っていきます。それではまた!
※Three.jsを習得するためにやったことまとめ
qiita.com