Three.jsをはじめよう。カメラワークとレスポンシブ対応【第2回】

前回、Three.jsの「3大要素(Scene, Camera, Renderer)」を学び、真っ暗な空間に緑色の立方体を浮かべることに成功しました。しかし、今のままでは立方体が勝手に回っているのを眺めているだけ…

実際のWEBサイトでは、ユーザーがマウスで商品を回してみたり、スマホで画面を傾けて覗き込んだりといった「インタラクション(相互作用)」が不可欠です。また、パソコンで見てもスマホで見ても、形が歪まずに綺麗に表示される「レスポンシブ対応」も、外せないポイントです。

第2回となる今回は、皆さんと一緒に、この「ただ眺めるだけの3D」を「自由に操作できる3D」へとアップグレードさせていきましょう。少しだけ数学的な話も出てきますが、大丈夫です。「お決まりのパターン」として分かりやすく噛み砕いて進めていきます。

マウスでグリグリ動かせる!「OrbitControls」

まずは、一番楽しい部分から始めましょう。カメラをマウスで自由に動かせるようにします。Three.jsには、そのための専用ツール「OrbitControls(オービット・コントロールズ)」が用意されています。

自分の視点を「軌道」に乗せる

OrbitControlsは、直訳すると「軌道制御」です。ある一点(デフォルトでは中心の0,0,0)を中心に、カメラが衛星のようにぐるぐると回り込む動きを簡単に実装できます。

このコントロール機能を使って「ユーザーに見せたい部分」に自然に視線を誘導する設計を行います。

実装コード(インポートと設定)

前回のコードに、以下の数行を追加するだけです。

javascript

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

// レンダラーとカメラを作った後に記述します
const controls = new OrbitControls(camera, renderer.domElement);

// 動きを滑らかにする(慣性を付ける)
controls.enableDamping = true;
controls.dampingFactor = 0.05;

ここで重要なのが enableDamping です。これを有効にすることで、マウスを離した後にピタッと動きが止まることなく「スーッ」と心地よく止まるようになり、一気に高級感のある操作感になります。

画面サイズに合わせて自動調整:レスポンシブ対応

次に、避けて通れないのが「レスポンシブ対応」です。今のままでは、ブラウザのウィンドウサイズを変更しても3D空間の大きさが変わらず、立方体が引き伸ばされたり、画面に余白ができたりしてしまいます。

なぜ「歪み」が起きるのか

3D空間の表示には「アスペクト比(縦横比)」が深く関わっています。カメラが映し出す範囲の計算式は以下のようになります。

Aspect Ratio = Width / Height

ブラウザの横幅(Width)や高さ(Height)が変わったのに、この比率を更新しないと、カメラが「古い比率」で無理やり映像を押し込もうとするため、物体が横に伸びたり縦に縮んだりしてしまうのです。

ウィンドウサイズ変更を検知する

JavaScriptの resize イベントを使って、画面の大きさが変わった瞬間にThree.jsの設定を書き換える処理を組み込みます。

JavaScript

window.addEventListener('resize', () => {
    // 1. ブラウザのサイズを取得
    const width = window.innerWidth;
    const height = window.innerHeight;

    // 2. レンダラーのサイズを更新
    renderer.setSize(width, height);

    // 3. カメラのアスペクト比を更新
    camera.aspect = width / height;

    // 4. カメラの投影行列を更新(これが必要!)
    camera.updateProjectionMatrix();
});

テクニック:ピクセル比(Device Pixel Ratio)

高精細なディスプレイ(Retinaディスプレイなど)では、実際のピクセル数とブラウザ上の数値が異なります。ここをケアしないと、3Dの輪郭が「ギザギザ」に見えてしまいます。

JavaScript

renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

プロは、パフォーマンスと美しさのバランスを考え、最大でも 2 程度に制限する工夫をしています。

数値を変えて試行錯誤:lil-GUIの導入

3D制作をしていると、「立方体の位置をあと0.1だけ右にしたい」「回転の速さを変えてみたい」といった調整が頻繁に発生します。そのたびにコードを書き換えて保存するのは効率が悪いですよね。

そこで登場するのが、画面上に操作パネルを表示させる「lil-GUI」というライブラリです。

直感的なデバッグ環境

これを使うと、ブラウザ上でスライダーを動かしながら、リアルタイムで物体の位置や色を変更できるようになります。

JavaScript

import { GUI } from 'lil-gui';

const gui = new GUI();
// 立方体のY軸の位置を調整するスライダー(-3から3の範囲)
gui.add(cube.position, 'y').min(-3).max(3).step(0.01).name('上下位置');

ディレクターやデザイナーと「もう少しキラキラさせたい」「動きをゆっくりに」といった対話をしながらブラウザ上で調整できるこの環境は、WEB制作の現場では必須のツールです。

今回の完成形コード

これまでの要素を統合した、第2回の「基本セット」を確認しましょう。

JavaScript

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// lil-guiの読み込み
import { GUI } from 'lil-gui';

// --- 初期設定 ---
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);

// --- 物体 ---
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// --- GUI設定 (lil-gui) ---
const gui = new GUI();
// 立方体のY軸の位置を調整するスライダー
// .add(対象オブジェクト, 'プロパティ名')
gui.add(cube.position, 'y').min(-3).max(3).step(0.01).name('上下位置 (Y)');

// おまけ:ワイヤーフレームの表示/非表示もGUIに追加すると便利です
gui.add(material, 'wireframe').name('枠線表示');

// --- コントロール ---
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;

// --- リサイズ対応 ---
window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});

// --- アニメーションループ ---
function animate() {
    requestAnimationFrame(animate);
    controls.update(); // 慣性を持たせるために必要
    renderer.render(scene, camera);
}
animate();

次のステップ:リアリティを追求する

いかがでしたか?マウスで立方体を好きな角度から見られるようになり、一気に「アプリケーション」らしい雰囲気が出てきたのではないでしょうか。

しかし、今の立方体はまだ「ワイヤーフレーム(線画)」や「単色」で、どこか無機質な印象です。実際の製品を3Dで見せるには、光り輝く金属の質感や、柔らかな布の質感などを表現する必要があります。

今回の学びのポイント

  • OrbitControls: ユーザーに「触る楽しみ」を提供する。
  • リサイズ処理: どんな画面でも歪ませない、崩さない。
  • lil-GUIの導入: 試行錯誤を見える形で。

3D体験がビジネスを変える

今回の「操作できる3D」の実装は、WEBサイトに訪れたユーザーを「受動的な読者」から「能動的な参加者」へと変える大きな一歩です。ユーザーが自らの手で視点を動かすという体験は、情報の理解度を深め、記憶に残りやすいサイトを実現します。

次回、第3回ではいよいよ「装飾編」に突入します。ただの箱を、まるで本物の石や金属のように見せるための「マテリアルとテクスチャ」について一緒に学んでいきましょう。

「自社の製品を、もっとリアルにWEB上で見せたい」というアイデアが湧いてきたら、いつでも私たちにご相談ください。エンジニアとデザイナーが、あなたの想像を形にするお手伝いをいたします。

それでは、また次回!