はじめに
本記事ではReactでThree.jsを使用し3DモデルをWebページ上に表示します。
3DモデルはBlenderで作成した地球の3Dモデルを使用します。
完成イメージ
対象の方
- ReactでThree.jsを使ってみたい方
- Blenderで作成した3DモデルをReactを使って描画してみたい方
- タイトルが気になった方
本記事のコードは対象の3Dモデル部分を変えていただければ、基本的にコピペでも動くと思います。
環境
- vite:5.1
- React:18.2
- three:0.161
3Dモデルについて
本記事のタイトルの通り3DはBlenderで作成することを想定しています。
Blenderからgltf形式でエクスポートした3Dモデルを使用。
今回は以下のような惑星を使用しています。
何かしら3Dモデルを準備していてください。
プロジェクトの作成
今回はViteを利用しプロジェクトを作成する。
以下のコマンドでプロジェクトを作成。
npm create vite@latest
作成されたプロジェクトにおいて、
public
直下のassets
フォルダにBlednerで作成した3Dモデルを格納する。
例: public
└assets
└3Dモデル
補足
src
直下のassets
フォルダに3Dモデルを格納すると参照できないため注意
パッケージのインストール
npm i three
npm i --save-dev @types/three
ソース
◆シーンの作成
空のシーンを新しく作成します。
このシーンに光源、ライト、3Dモデル等を追加していきます。
// シーン
const scene = new THREE.Scene();
◆canvasのサイズを取得
画面のサイズを取得し画面全体にcanvasを表示させるよう設定します。
// 画面のサイズを取得
const sizes = {
width: window.innerWidth,
height: window.innerHeight,
};
◆光源の配置
Three.jsの空間に光源を追加します。
光源に関連した項目(色・強さ・照射距離・減衰率)設定し座標を設定を宣言している。
今回はPointLight
を使用し光源を配置している。
// 光源の設定
const color = 0xFFFFFF; // 白色
const intensity = 300; // 強さ
const distance = 300; // 照射距離
const decay = 2; // 減衰率
// 光源の座標を作成
const vertices = [
new THREE.Vector3(-3, -3, -3),
new THREE.Vector3(3, -3, -3),
new THREE.Vector3(3, 3, -3),
new THREE.Vector3(-3, 3, -3),
new THREE.Vector3(-3, -3, 3),
new THREE.Vector3(3, -3, 3),
new THREE.Vector3(3, 3, 3),
new THREE.Vector3(-3, 3, 3)
];
// 光源を配置
for (const vertex of vertices) {
const light = new THREE.PointLight(color, intensity, distance, decay);
light.position.copy(vertex);
scene.add(light);
}
補足
今回はPointLight
を使用したが、Thre.jsには他にも光源の種類がいくつかあります。
気になる方はドキュメントをご確認ください。
◆カメラの配置
カメラとコントロール追加します。
OrbitControls
を使用してカメラを制御(マウスやタッチ操作)するためのコントロールを作成しています。
// カメラ
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100);
camera.position.set(0, 1, 5);
scene.add(camera);
const controls = new OrbitControls(camera, canvasRef.current!);
controls.enableDamping = true
注意:OrbitControlsの引数について
※OrbitControls
の2つめの引数にbody
を指定してしまうと、
canvas外の部分をクリックしてもOrbitControls
の操作が反応してしまうので注意してください。
◆ヘルパーの表示
空間内での座標が分かりにくいので基準となる平面を作成します。
GridHelper
は平面状のグリッドを生成し作成するヘルパークラスです。
// ヘルパー
const gridHelper = new THREE.GridHelper( 20, 10 );
gridHelper.position.set(0,-1,0);
scene.add( gridHelper );
◆レンダリング設定
WebGLRenderer
で3Dシーンをレンダリングします。
デフォルトではシーンの背景色が真っ黒なので色も変更しておきます。
// レンダラー
const renderer = new THREE.WebGLRenderer({
canvas: canvasRef.current!,
});
renderer.setSize(sizes.width, sizes.height);
// 背景色を水色に設定
renderer.setClearColor('#001021');
ここまでの全体のコード(renderCanvas.tsx)
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/Addons.js';
// シーンの作成
const createScene = (canvasRef: React.RefObject<HTMLCanvasElement>) => {
// 画面のサイズを取得
const sizes = {
width: window.innerWidth,
height: window.innerHeight,
};
// シーン
const scene = new THREE.Scene();
// ライト
const color = 0xFFFFFF;
const intensity = 300;
const distance = 300;
const decay = 2;
// 光源の座標を作成
const vertices = [
new THREE.Vector3(-3, -3, -3),
new THREE.Vector3(3, -3, -3),
new THREE.Vector3(3, 3, -3),
new THREE.Vector3(-3, 3, -3),
new THREE.Vector3(-3, -3, 3),
new THREE.Vector3(3, -3, 3),
new THREE.Vector3(3, 3, 3),
new THREE.Vector3(-3, 3, 3)
];
// 光源の配置
for (const vertex of vertices) {
const light = new THREE.PointLight(color, intensity, distance, decay);
light.position.copy(vertex);
scene.add(light);
}
// カメラ
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100);
camera.position.set(0, 1, 5);
scene.add(camera);
const controls = new OrbitControls(camera, canvasRef.current!);
controls.enableDamping = true
// ヘルパー
const gridHelper = new THREE.GridHelper( 20, 10 );
gridHelper.position.set(0,-1,0);
scene.add( gridHelper );
// レンダラー
const renderer = new THREE.WebGLRenderer({
canvas: canvasRef.current!,
});
renderer.setSize(sizes.width, sizes.height);
// 背景色を水色に設定
renderer.setClearColor('#001021');
return { scene, camera, renderer };
};
export default createScene;
◆3Dモデルの読み込み
以下のコードでassets
フォルダに格納した3Dモデルを読み込んでいる。
読み込み後には3Dモデルの位置を設定しシーンに追加・更新を行う。
また読み込んだ3DモデルはThree.jsの空間内でY軸
方向にゆっくり回り続けるように設定してある。
let model: THREE.Object3D;
const loader = new GLTFLoader();
// モデルの読み込み
loader.load('assets/earth2.glb', (gltf) => {
model = gltf.scene;
// 3Dモデルの位置
model.position.set(0,-1,0);
// 3Dモデルをシーンに追加
scene.add(model);
// シーンの更新
const animate = () => {
requestAnimationFrame(animate);
// モデルの回転
if (model) {
model.rotation.y += 0.001;
}
renderer.render(scene, camera);
};
animate();
});
3Dモデルの表示に関する全体のコード(App.tsx)
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import createScene from './renderCanvas';
const App = () => {
// canvasを扱う、DOMを扱うから初期値null
const canvasRef = useRef<HTMLCanvasElement | null>(null);
useEffect(() => {
const { scene, camera, renderer } = createScene(canvasRef);
// 3Dモデルの読み込み
let model: THREE.Object3D;
const loader = new GLTFLoader();
loader.load('assets/earth2.glb', (gltf) => {
model = gltf.scene;
model.position.set(0,-1,0);
scene.add(model);
// カメラの位置を設定
camera.position.set(0, 1, 5);
// シーンの更新
const animate = () => {
requestAnimationFrame(animate);
// モデルの回転
if (model) {
model.rotation.y += 0.001;
}
renderer.render(scene, camera);
};
animate();
});
}, []);
return <canvas ref={canvasRef} className='canvasContaier'/>;
}
export default App
main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
実行結果
npm run dev
で起動しWebページ上の描画すると、
以下の画像用のように問題なくBlnderで作成した3Dモデルが描画されていることを確認。