Home
JavaScript
【Three.js】カメラの距離を調節してWebGL座標をwindow座標(px)に合わせる

【Three.js】カメラの距離を調節してWebGL座標をwindow座標(px)に合わせる

【Three.js】カメラの距離を調節してWebGL座標をwindow座標(px)に合わせる

どうも!かけちまるです!

Three.jsでマウスイベントやスクロールイベントに合わせてアニメーションしたい時のためにカメラの距離を調節してWebGL座標をwindow座標(ピクセル)にあわせておくと良いです。

この記事では、

  • ・WebGL座標をwindow座標(ピクセル)にあわせる方法

がわかります。

【考え方】windowサイズがぴったり収まるカメラ距離を計算する

PerspectiveCameraを使うことを前提に解説します。
windowサイズがピッタリ収まる距離までカメラを下げることでメッシュのサイズや位置などをpx(ピクセル)で指定できるようになるのです。

下画像のように直角三角形を用いて、カメラの距離を求めます。

カメラ距離を計算

求める値は、カメラ距離です。
わかっている値はPerspectiveCameraで設定するfov(視野角)/2とブラウザの高さ/2です。

なので、三角関数のtanを使って求めます。
tanはラジアンを使うのでfov(視野角)/2をラジアンに変換します。

  • ラジアン = (fov ÷ 2) × (π ÷ 180)

度をラジアンに変換する公式はこちらがわかりやすそうです。
度をラジアンに変換する公式

それから、カメラ距離を求めます。

  • tan(ラジアン) = (ブラウザの高さ ÷ 2) ÷ カメラ距離
  • tan(ラジアン) × カメラ距離 = (ブラウザの高さ ÷ 2)
  • カメラ距離 = (ブラウザの高さ ÷ 2) ÷ tan(ラジアン)

三角関数についてはこちらがわかりやすそうです。
三角関数の公式

まとめると

  • ラジアン = (fov ÷ 2) × (π ÷ 180)
  • カメラ距離 = (ブラウザの高さ ÷ 2) ÷ tan(ラジアン)

JavaScriptにすると

JavaScript
開く&閉じるコピー
const fovRad = (fov / 2) * (Math.PI / 180); let distance = (window.innerHeight / 2) / Math.tan(fovRad);

で求めることができます。

windowサイズがぴったり収まるカメラ距離を実装する

立方体の準備

今回はカメラ距離に焦点を当てて解説するので次のコードで事前に立方体を作っておきましょう。

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; }
JavaScript
開く&閉じるコピー
import * 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(); // カメラを更新した際に呼び出す必要がある }

おわり

かけちまる
かけちまる
Webエンジニアをしています。
HTML/CSS/JavaScript/jQuery/PHPができます。
WEB制作を中心に日々学びになったこと、興味が沸いたことについて初心者の方でもわかりやすいようにアウトプットしていくブログです。

関連記事

Contact Form7の入力エラー表示をCSSのみでカスタマイズ

Contact Form7の入力エラー表示をCSSのみでカスタマイズ

【GSAP】タイムラインを使えば複雑なアニメーションを実装できる

【GSAP】タイムラインを使えば複雑なアニメーションを実装できる

【Macユーザー向け】ngrokでローカル環境のサイトを外部へ公開

【Macユーザー向け】ngrokでローカル環境のサイトを外部へ公開

Local by Flywheelで本番環境のWordPressをローカルに複製する

Local by Flywheelで本番環境のWordPressをローカルに複製する

【メンテナンス画面】LightStartの設定方法とデザインカスタマイズ

【メンテナンス画面】LightStartの設定方法とデザインカスタマイズ

【完全禁止は難しい】Webサイトの画像保存を禁止させる方法

【完全禁止は難しい】Webサイトの画像保存を禁止させる方法