どうも!かけちまるです!
Three.jsでマウスイベントやスクロールイベントに合わせてアニメーションしたい時のためにカメラの距離を調節してWebGL座標をwindow座標(ピクセル)にあわせておくと良いです。
この記事では、
がわかります。
PerspectiveCamera
を使うことを前提に解説します。
windowサイズがピッタリ収まる距離までカメラを下げることでメッシュのサイズや位置などをpx(ピクセル)で指定できるようになるのです。
下画像のように直角三角形を用いて、カメラの距離を求めます。
求める値は、カメラ距離です。
わかっている値はPerspectiveCamera
で設定するfov
(視野角)/2とブラウザの高さ/2です。
なので、三角関数のtan
を使って求めます。tan
はラジアンを使うのでfov
(視野角)/2をラジアンに変換します。
度をラジアンに変換する公式はこちらがわかりやすそうです。
度をラジアンに変換する公式
それから、カメラ距離を求めます。
三角関数についてはこちらがわかりやすそうです。
三角関数の公式
JavaScriptにすると
JavaScriptconst fovRad = (fov / 2) * (Math.PI / 180); let distance = (window.innerHeight / 2) / Math.tan(fovRad);
で求めることができます。
今回はカメラ距離に焦点を当てて解説するので次のコードで事前に立方体を作っておきましょう。
HTML<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" type="text/css" href="./css/style.css"> <script async src="https://unpkg.com/es-module-shims@1.5.8/dist/es-module-shims.js"></script> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.142.0/build/three.module.js" } } </script> <script src="./js/main.js" type="module"></script> </head> <body></body> </html>
CSS* { margin: 0; padding: 0; box-sizing: border-box; }
JavaScriptimport * as THREE from 'three'; /** * シーン・カメラ・レンダラーを用意する */ // シーンを用意 const scene = new THREE.Scene(); // カメラを用意 const camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.z = 5; // カメラをz軸方向に5ずらす(後ろにずらす) // レンダラーを用意 const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーをwindowのサイズと合わせる renderer.setPixelRatio(window.devicePixelRatio); // デバイスのピクセル比を設定 document.body.appendChild(renderer.domElement); // レンダラー(canvas)をbody内に挿入 /** * ジオメトリ+マテリアル=メッシュを用意する */ // ジオメトリを用意 const boxGeometry = new THREE.BoxGeometry(1, 1, 1); // (幅, 高さ, 奥行き) // マテリアルを用意 const lambertMaterial = new THREE.MeshLambertMaterial({ color: 0x3da8e6, // 色を設定 }); // ジオメトリ+マテリアル=メッシュ化 const boxMesh = new THREE.Mesh(boxGeometry, lambertMaterial); scene.add(boxMesh); // メッシュをsceneにaddする /** * ライトを追加する */ const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // (光の色, 光の強さ) directionalLight.position.set(-1, 2, 4); // (x, y, z) scene.add(directionalLight); // ライトをsceneにaddする function animate() { // シーンとカメラをレンダラーに読み込ませる(最後に実行するようになっていること) renderer.render(scene, camera); boxMesh.rotation.x += 0.01; boxMesh.rotation.y += 0.01; requestAnimationFrame(animate); // 引数内の関数をフレーム単位で読み込む } animate(); // ブラウザのリサイズに対応させる window.addEventListener('resize', onWindowResize); function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーのサイズをれサイズ対応 camera.aspect = window.innerWidth / window.innerHeight; // カメラのアスペクト比をリサイズ対応 camera.updateProjectionMatrix(); // カメラを更新した際に呼び出す必要がある }
上記コードの詳しい解説は以下の記事で行っています。
では、立方体の準備をもとに【考え方】windowサイズがぴったり収まるカメラ距離を計算するで解説した公式を使ってカメラ距離を求めていきます。
まず、公式でカメラ距離を求めます。(1~5行目)
それをカメラのz軸に当て込むだけです。(16行目)
ブラウザのリサイズにも対応するためにリサイズ処理の部分でも計算し直すようにしています。(34、35行目)
このままだと立方体がとても小さいので大きさも調整しておきましょう。(24行目)
JavaScript// 視野角をラジアンに変換 const fov = 50; const fovRad = (fov / 2) * (Math.PI / 180); // カメラ距離を求める let distance = (window.innerHeight / 2) / Math.tan(fovRad); // カメラを用意 const camera = new THREE.PerspectiveCamera( // 50, fov, window.innerWidth / window.innerHeight, 0.1, 1000 ); // camera.position.z = 5; // カメラをz軸方向に5ずらす(後ろにずらす) camera.position.z = distance; ・・・ /** * ジオメトリ+マテリアル=メッシュを用意する */ // ジオメトリを用意 const boxGeometry = new THREE.BoxGeometry(100, 100, 100); // (幅, 高さ, 奥行き) ・・・ // ブラウザのリサイズに対応させる window.addEventListener('resize', onWindowResize); function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーのサイズをれサイズ対応 camera.aspect = window.innerWidth / window.innerHeight; // カメラのアスペクト比をリサイズ対応 distance = (window.innerHeight / 2) / Math.tan(fovRad); camera.position.z = distance; camera.updateProjectionMatrix(); // カメラを更新した際に呼び出す必要がある }
おわり
フィードバックを送信
記事についてのフィードバックはTwitterかお問い合わせフォームから受け付けております。