LoginSignup
2
2

【React】Three.jsを使ってBlednerで作った3Dモデルを手軽に表示する

Posted at

はじめに

本記事ではReactでThree.jsを使用し3DモデルをWebページ上に表示します。
3DモデルはBlenderで作成した地球の3Dモデルを使用します。

完成イメージ

three_gif.gif

対象の方

  • ReactでThree.jsを使ってみたい方
  • Blenderで作成した3DモデルをReactを使って描画してみたい方
  • タイトルが気になった方

本記事のコードは対象の3Dモデル部分を変えていただければ、基本的にコピペでも動くと思います。

環境

  • vite:5.1
  • React:18.2
  • three:0.161

3Dモデルについて

本記事のタイトルの通り3DはBlenderで作成することを想定しています。
Blenderからgltf形式でエクスポートした3Dモデルを使用。
今回は以下のような惑星を使用しています。
何かしら3Dモデルを準備していてください。

image.png

プロジェクトの作成

今回は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は平面状のグリッドを生成し作成するヘルパークラスです。

image.png

// ヘルパー
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)
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)
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
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モデルが描画されていることを確認。

three_gif.gif

2
2
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
2
2