5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【React/mapboxの地図上で3Dモデルを動かす】(1) 3DモデルをThree.jsを使ってブラウザに表示する

Last updated at Posted at 2022-11-08

はじめに

タイトルにある通り、ReactWebアプリに描画されたmapboxの地図上で3Dモデルを動かすことをゴールに記事にしていきます。
長いので4つの記事に分けて書きます。

  • (1) 3DモデルをThree.jsを使ってブラウザ表示する
  • (2) mapboxの地図上に3Dモデルを表示する
  • (3) mapboxの地図上で3Dモデルをアニメーションさせる
  • (4) mapboxの地形データ付きの地図上で地形に沿って3Dモデルをアニメーションさせる

今回は (1) 3DモデルをThree.jsを使ってブラウザ表示する です。

最終的な完成形

mapboxの地形付きの地図上で、3Dモデルが地形に沿って移動していきます。

完成形.gif

3Dモデルの準備

今回はこちらのドローンのモデルを使おうと思います。
drone.png

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を設定します。

index.html
<!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調整

ブラウザの余白を無くします。
直接関係は無いのでやらなくても大丈夫です。

style.css
+ * {
+   margin: 0;
+   padding: 0;
+ }

:root {
  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 24px;
  font-weight: 400;
  ...
  ...

Js初期セットアップ

main.js
+ import './style.css'

+ const canvas = document.getElementById("canvas");

DLした3Dモデル配置

/modelsというディレクトリを作ってそこに放り込んでおきます。

modelパス.png

試しに起動

yarn dev

http://localhost:5173

画面は真っ黒な状態。コンソールにエラーとか出てなければOK。
初期確認.png

Three.jsについて

概要

3Dコンテンツを作れるJavascriptライブラリです。
https://threejs.org/

簡単な3Dオブジェクトは勿論、3Dモデル用のファイルなどを読み込んで使うこともできます。

ブラウザでの描画の流れ

ものすごく大雑把な自分の中でのイメージです。
threejsのイメージ.png

  • scene・・・3Dオブジェクトを表示する場所。ここに3Dオブジェクトをどんどん配置していく。
  • camera・・・sceneをどのように見るか。
  • renderer・・・cameraで見ている状態をブラウザで描画できるようにする。ここではcanvasを指定する。

3Dモデルを表示させる実装

描画するための土台部分を作る

まずイメージ図のような、ブラウザに描画するための土台部分を作っていきます。

main.js
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です

土台.png

モデルを配置する

DLした3Dモデルを配置していきます。
Materialの中にファイルが色々と入っているのですが、今回はdrone_costum.fbxを使いたいと思います。

main.js
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();

モデルの配置は終わり、描画はできているのですがモデルが真っ黒です。
これは光源が設定されていないので光が無い状態だからです。

モデル追加.png

光源を追加

ライトを追加していきます。

main.js
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();

光源が追加され、モデルの外観が見えるようになりました。

ライト追加.png

浮遊感を出す(アニメーションを追加)

ついでに少しモデルを上下させてドローンに浮遊感を出してみようと思います。
アニメーションはanimateの中で表現していくことになります。

main.js
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();

ドローンに浮遊感を持たせることができました。

step1完成形.gif

まとめ

Three.jsについての基本的な描画の流れを追っていきました。
Three.jsライブラリのクラスや細かいプロパティなどは公式ドキュメントで詳細に説明されています。

次回はこれをmapboxの地図の世界へ持っていきます。

参考

Three.js 公式ドキュメント
お借りした3Dモデル

連載

(1) 3D モデルを Three.js を使ってブラウザ表示する
(2) mapbox の地図上に 3D モデルを表示する
(3) mapbox の地図上で 3D モデルをアニメーションさせる
(4) mapbox の地形データ付きの地図上で地形に沿って 3D モデルをアニメーションさせる

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?