はじめに
タイトルにある通り、ReactWebアプリに描画されたmapboxの地図上で3Dモデルを動かすことをゴールに記事にしていきます。
長いので4つの記事に分けて書きます。
- (1) 3DモデルをThree.jsを使ってブラウザ表示する
- (2) mapboxの地図上に3Dモデルを表示する
- (3) mapboxの地図上で3Dモデルをアニメーションさせる
- (4) mapboxの地形データ付きの地図上で地形に沿って3Dモデルをアニメーションさせる
今回は (1) 3DモデルをThree.jsを使ってブラウザ表示する です。
最終的な完成形
mapboxの地形付きの地図上で、3Dモデルが地形に沿って移動していきます。
3Dモデルの準備
https://free3d.com/3d-model/drone-costume-411845.html
環境構築
今回はThree.jsでモデルを表示することに集中するため、Reactと言っておきながらReactは使いません。
ピュアなJavascriptでまず3Dモデル表示の流れを行っていこうと思います。
vite
yarn create vite
✔ Select a framework: › Vanilla
✔ Select a variant: › JavaScript
package install
yarn add three
3D描画用のcanvasを設定
描画先のcanvas
を設定します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
- <div id="app"></div>
+ <canvas id="canvas"></canvas>
<script type="module" src="/main.js"></script>
</body>
</html>
css調整
ブラウザの余白を無くします。
直接関係は無いのでやらなくても大丈夫です。
+ * {
+ margin: 0;
+ padding: 0;
+ }
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
...
...
Js初期セットアップ
+ import './style.css'
+ const canvas = document.getElementById("canvas");
DLした3Dモデル配置
/models
というディレクトリを作ってそこに放り込んでおきます。
試しに起動
yarn dev
画面は真っ黒な状態。コンソールにエラーとか出てなければOK。
Three.jsについて
概要
3Dコンテンツを作れるJavascriptライブラリです。
https://threejs.org/
簡単な3Dオブジェクトは勿論、3Dモデル用のファイルなどを読み込んで使うこともできます。
ブラウザでの描画の流れ
- scene・・・3Dオブジェクトを表示する場所。ここに3Dオブジェクトをどんどん配置していく。
- camera・・・sceneをどのように見るか。
- renderer・・・cameraで見ている状態をブラウザで描画できるようにする。ここではcanvasを指定する。
3Dモデルを表示させる実装
描画するための土台部分を作る
まずイメージ図のような、ブラウザに描画するための土台部分を作っていきます。
import "./style.css";
import * as THREE from "three";
const canvas = document.getElementById("canvas");
const scene = new THREE.Scene();
// 背景をわかりやすいように白にしておく
scene.background = new THREE.Color(0xffffff);
const camera = new THREE.PerspectiveCamera(
35,
innerWidth / innerHeight,
0.1,
2000
);
camera.position.z = 80;
camera.position.y - 7.2;
// カメラでsceneを見れるように
scene.add(camera);
// 3Dの世界を投影する対象の設定
const renderer = new THREE.WebGLRenderer({ canvas });
// ブラウザの画面幅目一杯まで描画するサイズ設定
renderer.setSize(innerWidth, innerHeight);
// 3Dの世界をブラウザに描画する
renderer.render(scene, camera);
真っ白な画面が表示されていればひとまず土台部分はOKです
モデルを配置する
DLした3Dモデルを配置していきます。
Materialの中にファイルが色々と入っているのですが、今回はdrone_costum.fbx
を使いたいと思います。
import "./style.css";
import * as THREE from "three";
+ import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
const canvas = document.getElementById("canvas");
const scene = new THREE.Scene();
// 背景をわかりやすいように白にしておく
scene.background = new THREE.Color(0xffffff);
const camera = new THREE.PerspectiveCamera(
35,
innerWidth / innerHeight,
0.1,
2000
);
camera.position.z = 80;
camera.position.y - 7.2;
// カメラでsceneを見れるように
scene.add(camera);
+ // 3Dモデルの読み込みと配置
+ const fbxLoader = new FBXLoader();
+ // テクスチャの読み込み先の設定
+ fbxLoader.setResourcePath("./models/Drone_Costum/Teturizer/");
+ fbxLoader.load("./models/Drone_Costum/Material/drone_costum.fbx", (obj) => {
+ // 3Dモデルが大きすぎたので縮小
+ obj.scale.set(0.025, 0.025, 0.025);
+ // 3Dモデルをsceneに配置
+ scene.add(obj);
+ });
// 3Dの世界を投影する対象の設定
const renderer = new THREE.WebGLRenderer({ canvas });
// ブラウザの画面幅目一杯まで描画するサイズ設定
renderer.setSize(innerWidth, innerHeight);
- // 3Dの世界をブラウザに描画する
- renderer.render(scene, camera);
+ // モデルは読み込み処理により遅延してsceneにセットされるため、再描画が必要
+ // なので、renderを繰り返し行うように設定する
+ const animate = () => {
+ // 3Dの世界をブラウザに描画する
+ renderer.render(scene, camera);
+ window.requestAnimationFrame(animate);
+ };
+ animate();
モデルの配置は終わり、描画はできているのですがモデルが真っ黒です。
これは光源が設定されていないので光が無い状態だからです。
光源を追加
ライトを追加していきます。
import "./style.css";
import * as THREE from "three";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
const canvas = document.getElementById("canvas");
const scene = new THREE.Scene();
// 背景をわかりやすいように白にしておく
scene.background = new THREE.Color(0xffffff);
const camera = new THREE.PerspectiveCamera(
35,
innerWidth / innerHeight,
0.1,
2000
);
camera.position.z = 80;
camera.position.y - 7.2;
// カメラでsceneを見れるように
scene.add(camera);
+ // ライトの設定 今回は2種類のライトを追加します
+ const ambientLight = new THREE.AmbientLight();
+ ambientLight.color.set(0xffffff);
+ ambientLight.intensity = 0.5;
+ scene.add(ambientLight)
+
+ const directionalLight = new THREE.DirectionalLight(0xffffff,0.4);
+ directionalLight.position.set(1,0.55,5);
+ scene.add(directionalLight);
// 3Dモデルの読み込みと配置
const fbxLoader = new FBXLoader();
// テクスチャの読み込み先の設定
fbxLoader.setResourcePath("./models/Drone_Costum/Teturizer/");
fbxLoader.load("./models/Drone_Costum/Material/drone_costum.fbx", (obj) => {
// 3Dモデルが大きすぎたので縮小
obj.scale.set(0.025, 0.025, 0.025);
// 3Dモデルをsceneに配置
scene.add(obj);
});
// 3Dの世界を投影する対象の設定
const renderer = new THREE.WebGLRenderer({ canvas });
// ブラウザの画面幅目一杯まで描画するサイズ設定
renderer.setSize(innerWidth, innerHeight);
// モデルは読み込み処理により遅延してsceneにセットされるため、再描画が必要
// なので、renderを繰り返し行うように設定する
const animate = () => {
// 3Dの世界をブラウザに描画する
renderer.render(scene, camera);
window.requestAnimationFrame(animate);
};
animate();
光源が追加され、モデルの外観が見えるようになりました。
浮遊感を出す(アニメーションを追加)
ついでに少しモデルを上下させてドローンに浮遊感を出してみようと思います。
アニメーションはanimate
の中で表現していくことになります。
import "./style.css";
import * as THREE from "three";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
const canvas = document.getElementById("canvas");
const scene = new THREE.Scene();
// 背景をわかりやすいように白にしておく
scene.background = new THREE.Color(0xffffff);
const camera = new THREE.PerspectiveCamera(
35,
innerWidth / innerHeight,
0.1,
2000
);
camera.position.z = 80;
camera.position.y - 7.2;
// カメラでsceneを見れるように
scene.add(camera);
// ライトの設定 今回は2種類のライトを追加します
const ambientLight = new THREE.AmbientLight();
ambientLight.color.set(0xffffff);
ambientLight.intensity = 0.5;
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.4);
directionalLight.position.set(1, 0.55, 5);
scene.add(directionalLight);
// 3Dモデルの読み込みと配置
+ let drone;
const fbxLoader = new FBXLoader();
// テクスチャの読み込み先の設定
fbxLoader.setResourcePath("./models/Drone_Costum/Teturizer/");
fbxLoader.load("./models/Drone_Costum/Material/drone_costum.fbx", (obj) => {
// 3Dモデルが大きすぎたので縮小
obj.scale.set(0.025, 0.025, 0.025);
// 3Dモデルをsceneに配置
scene.add(obj);
+ drone = obj;
});
// 3Dの世界を投影する対象の設定
const renderer = new THREE.WebGLRenderer({ canvas });
// ブラウザの画面幅目一杯まで描画するサイズ設定
renderer.setSize(innerWidth, innerHeight);
// モデルは読み込み処理により遅延してsceneにセットされるため、再描画が必要
// なので、renderを繰り返し行うように設定する
- const animate = () => {
+ const animate = (frame) => {
+ // 3Dモデルの読み込みが終わっていたら
+ if (drone) {
+ // 上下に3Dモデルを動かす
+ let elevation = Math.sin(frame * 0.01) * 0.5;
+ drone.position.y = elevation;
+ }
// 3Dの世界をブラウザに描画する
renderer.render(scene, camera);
window.requestAnimationFrame(animate);
};
animate();
ドローンに浮遊感を持たせることができました。
まとめ
Three.jsについての基本的な描画の流れを追っていきました。
Three.jsライブラリのクラスや細かいプロパティなどは公式ドキュメントで詳細に説明されています。
次回はこれをmapboxの地図の世界へ持っていきます。
参考
連載
(1) 3D モデルを Three.js を使ってブラウザ表示する
(2) mapbox の地図上に 3D モデルを表示する
(3) mapbox の地図上で 3D モデルをアニメーションさせる
(4) mapbox の地形データ付きの地図上で地形に沿って 3D モデルをアニメーションさせる