【超入門】React Three Fiber × Drei × TypeScript!標準オブジェクトで作る3D表現の基本

  • URLをコピーしました!

3Dデザインは、今やゲーム開発だけでなく、ウェブサイトや次世代インターフェースの世界でも大きな注目を集めています。その魅力的な世界を「自分でも作りたい!」と思ったことはありませんか?

でも、「3Dって難しそう」と感じる人も多いはず。従来のThree.jsを直接使う方法では、複雑な設定やコードを書く必要があり、初心者にとっては少し敷居が高いのが現実です。

そんな中、React Three Fiber というライブラリが登場し、React のエコシステムの中で 簡単かつ直感的に 3D 表現を実現 できるようになりました。さらに、TypeScript を組み合わせれば、より安全で効率的な開発が可能になります。

しかし、React Three Fiber と TypeScript の組み合わせを初心者でも無理なく学べる解説は、まだまだ少ないのが現状です。

この記事では、React Three FiberとTypeScriptを組み合わせて、誰でも簡単に始められる3D開発の基礎を解説します。さらに、標準オブジェクト(ジオメトリやマテリアル)を使ったゲーム風の背景やアニメーションを作る実践例もご紹介!

「3D開発が初めての方」も安心して読める内容になっていますので、ぜひ一緒に始めてみましょう。

TypeScript x React Three Fiberのレッスンや作品は、今後もどんどん作成していきます!
YouTubeで告知致しますので、ぜひYouTubeのチャンネル登録をして通知をお待ちください!

📺 YouTubeを見る:こちらのリンクから視聴できます。

React Three Fiberで何ができるのか知りたい方は、下記を参考にしてみてください!
簡単にできる作品を用意しております!

目次

技術要素の紹介:このプロジェクトで使うツールとライブラリ

利用するツールやライブラリは、ご自身の使いやすいものに変更しても良いですが、このプロジェクトではこちらを前提に説明をしていきます。

また、環境構築は、「ViteでTypeScript x React Three Fiberの3D環境を構築」で解説しておりますので、そちらをご確認いただければと思います。

VSCode
  • Microsoftが提供する無料のコードエディタです。
  • VSCodeでなくても良いですが、多くの拡張機能がありますので、おすすめです。
  • ESLintやPrettierを入れるのもおすすめです。
Node.js
  • ChromeのV8 JavaScriptエンジン上に構築されたJavaScriptランタイムです。
  • ブラウザの外でJavaScriptコードを実行することができます。
  • こちらは既にインストールされている前提で説明しますので、「https://nodejs.org/ja」からダウンロードしておいてください。
    ※長期安定版であるLTSバージョンをダウンロードすることをお勧めします。
Vite
  • モダンなウェブプロジェクトのためのビルドツールです。 速く軽量という特徴があります。
  • 以前まで使われていた「CRA(create-react-app)」は、公式HPでも記載がなく、古い技術になってしまいました。
  • これからは、Reactでアプリを作る際は、Viteが最良の選択肢となるはずです。
React
  • UI(ユーザーインターフェース)を構築するためのJavaScriptライブラリです。Facebookが開発し、現在も多くのウェブアプリで使われています。
Three.js
  • 3Dグラフィックスを簡単に作成するためのJavaScriptライブラリです。WebGLの複雑な操作を抽象化し、直感的な3D開発を可能にします。
  • 3Dグラフィックスを簡単に作成でき、WebGLの直接操作よりも扱いやすいです。
React Three Fiber
  • Three.jsをReactで扱えるようにするためのライブラリです。Reactのコンポーネント構造とThree.jsの3Dエンジンを融合します。
  • Reactの開発スタイルでThree.jsを扱えるため、直感的かつ効率的な開発が可能になります。
React Three Drei
  • React Three Fiberのための便利なユーティリティコンポーネント集です。よく使われるThree.jsの機能を簡単に追加できます。
  • 複雑なThree.jsの機能を短いコードで実現できるため、学習コストを削減できます。

Meshes

3D Objectsを描画するためには、meshコンポーネントを利用します。
meshは、geometries(形状)とmaterials(材質)で構成されています。

positionやrotation、scale、castShadow(影を投げる)、receiveShadow(影を受ける)などのPropertiesが存在します。
また、onClickなどのイベントも定義することが可能です。詳しくは、three.jsのドキュメントを参照してください。

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh
        position={[0, 1, 0]}        // 位置
        rotation={[Math.PI / 4, 0, 0]} // 回転(ラジアン)
        scale={[2, 2, 2]}           // スケール(拡大)
        castShadow                 // 影を投げる
        receiveShadow              // 影を受け取る
      >
        <boxGeometry args={[1, 1, 1]} />
        <meshStandardMaterial color="red" />
      </mesh>
    </Canvas>
  );
}

export default App;

Geometry

Geometryとは

Geometryは、オブジェクトを構成する頂点、エッジ、および面の位置、サイズ、および接続性を定義しています。

  • 頂点(青):れらはオブジェクトの形状を定義する3D空間内の個々の点です。各頂点は、3D空間内の位置を決定する座標(x, y, z)を持っています。頂点はエッジで接続されて面を形成します
  • エッジ(緑):エッジは頂点をつなぐ線です。これらはオブジェクトの形状を定義し、直線でも曲線でもかまいません。各エッジは頂点を参照する一連のインデックスで構成されています。
  • 面(黄):面は頂点を接続することによって形成されるポリゴンです。面はオブジェクトの表面を定義し、三角形、四角形、またはその他のポリゴンであることがあります。各面も頂点を参照する一連のインデックスで構成されています。

Geometriesの種類

React Three Fiber(Three.js)では、既製ジオメトリが多くあります。
以下に一般的なものをいくつか紹介します。

boxGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <boxGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

sphereGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <sphereGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

planeGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <planeGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

circleGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <circleGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

cylinderGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <cylinderGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

capsuleGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <capsuleGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

coneGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <coneGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

dodecahedronGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <dodecahedronGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

extrudeGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <extrudeGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

icosahedronGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <icosahedronGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

latheGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <latheGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

octahedronGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <octahedronGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

polyhedronGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  const vertices = [
    0, 0, 1,  // 頂点1
    0, 1, 0,  // 頂点2
    1, 0, 0,  // 頂点3
    -1, 0, 0, // 頂点4
  ];

  const indices = [
    0, 1, 2, // 面1
    0, 1, 3, // 面2
    0, 2, 3, // 面3
    1, 2, 3, // 面4
  ];

  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <polyhedronGeometry args={[vertices, indices, 1, 0]} />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

tetrahedronGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <tetrahedronGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

torusGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

torusKnotGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

tubeGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <tubeGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

ringGeometry

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <ringGeometry />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

shapeGeometry

import { Shape, Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  const shape = new Shape();

  // ハート形状を定義
  shape.moveTo(0, 0.5);
  shape.bezierCurveTo(0.5, 1, 1, 0.5, 0, 0);
  shape.bezierCurveTo(-1, 0.5, -0.5, 1, 0, 0.5);

  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <shapeGeometry args={[shape]} />
        <meshNormalMaterial />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

edgesGeometry

import { Vector3, BoxGeometry } from "three";
import { Canvas } from "@react-three/fiber";
import { Edges, MeshDiscardMaterial, OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      {/* react-three-fiberのみ */}
      <lineSegments>
        <edgesGeometry args={[new BoxGeometry(1)]} />
        <lineBasicMaterial color="blue" />
      </lineSegments>
      {/* react-three-drai */}
      <mesh position={[2, 0, 0]}>
        <boxGeometry args={[1, 1, 1]} />
        <MeshDiscardMaterial />
        <Edges color="red" />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

export default App;

react-three-fiberのみでEdgesを使う場合は、argsにThree.jsを直接使う必要があります。
その一方で、react-three-dreiを使えば、Three.jsを直接使わずに、シンプルに書くことができますが、react-three-dreiのEdgesは、面も描画されてしまうため、MeshDiscardMaterialなどを利用して、面を透明にする必要があります。

wireframeGeometry

import { Vector3, BoxGeometry } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls, Wireframe } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>

      {/* react-three-fiber */}
      <lineSegments>
        <wireframeGeometry args={[new BoxGeometry(1)]} />
        <lineBasicMaterial color="blue" />
      </lineSegments>

      {/* react-three-drai */}
      <mesh position={[2, 0, 0]}>
        <boxGeometry args={[1, 1, 1]} />
        <Wireframe stroke="red" />
      </mesh>

      {/* react-three-fiberの別解 */}
      <mesh position={[4, 0, 0]}>
        <boxGeometry args={[1, 1, 1]} />
        <meshStandardMaterial color="green" wireframe />
      </mesh>

      <OrbitControls />
      <ambientLight />
    </Canvas>
  );
}

export default App;

Wireframeというreact-three-draiのコンポーネントは、面も描画します。
react-three-fiberの別解が一番使いやすいかもしれません。

Materials

Materialは、オブジェクトの表面が描画されたときにどのように見えるかという材質を決定する情報を定義します。
オブジェクトの色、テクスチャ、透明度、反射率などのPropertiesが存在します。

Materialを確認するためには、ライト(光)が必要です。ライトに関しては、別の機会で解説しますので、今回はambientLightとdirectionalLightを下記のように入れてください。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshNormalMaterial />
      </mesh>

      <ambientLight intensity={0.5} /> // ライトを追加
      <directionalLight position={[0, 5, 5]} intensity={0.7} /> // ライトを追加

      <OrbitControls />
    </Canvas>
  );
}

export default App;

React Three Fiber(Three.js)のMaterialsの種類

React Three Fiber(Three.js)では、Materialも既製のもが多くあります。
以下に一般的なものをいくつか紹介します。

meshBasicMaterial

MeshBasicMaterialは、光源の影響を受けないシンプルなマテリアル。テクスチャや色をそのまま表示します。
ワイヤーフレーム、非リアルタイムレンダリング、背景などで利用されることが多いです。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshBasicMaterial color="red" />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

meshNormalMaterial

meshNormalMaterialは、頂点や面の法線を可視化する特殊なマテリアル。これも光の影響は受けません。
モデルの構造や法線が正しいか確認するデバッグ用途に利用されることが多いです。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshNormalMaterial />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

meshLambertMaterial

meshLambertMaterialは、拡散反射(ディフューズ)のみを持つ軽量マテリアル。拡散反射を計算し、マットな見た目を作ってくれます。軽量ですが、リアリティはないです。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshLambertMaterial color="red" />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

meshPhongMaterial

meshPhongMaterialは、ランバートの特性に加え、鏡面反射(スペキュラー)を持つマテリアル。鏡面反射の表現が可能になっていますが、これもリアリティはないですね。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshPhongMaterial color="red" shininess={100} />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

meshStandardMaterial

meshStandardMaterialは、PBR(物理ベースレンダリング)をサポートするマテリアル。リアルな光の反射や影を再現。メタルネスや粗さを設定可能で、そこそこリアルな質感を作ることができます。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshStandardMaterial color="red" metalness={1} roughness={0.5} />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

meshPhysicalMaterial

meshPhysicalMaterialは、meshStandardMaterial の拡張版。透過、反射率、クリアコートなど高度な物理特性を追加。
ガラスや水滴など、透過や屈折を表現することが可能です。より、リアルな表現が可能になりますが、計算量も多くなるので注意が必要です。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshPhysicalMaterial
          color="red"
          metalness={1} // 金属感を最大に
          roughness={0.5} // 表面の粗さ(滑らか)
          clearcoat={0.8} // 反射の強さ
          clearcoatRoughness={0.1} // 反射面の粗さ
        />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;
特徴meshLambertMaterialmeshPhongMaterialmeshStandardMaterialmeshPhysicalMaterial
シェーディングモデルランバートシェーディングフォンシェーディング物理ベースシェーディング(PBR)物理ベースシェーディング(PBR)
金属感なしなしmetalnessで金属感を表現metalnessで金属感を表現
粗さなしなしroughnessで表面の粗さを表現roughnessで表面の粗さを表現
反射なし鏡面反射(shininessで調整)シンプルな反射が可能clearcoatで反射層を追加、詳細な反射表現
透明感・屈折なしなしなしtransparencyrefractionRatioで表現
パフォーマンス高速やや遅い少し重い最も重い(計算量が多い)
使用目的シンプルなシーン、パフォーマンス重視光沢のある表面(プラスチック、金属)現実的な物理表現が必要なシーンリアルな反射、屈折、金属感、透明感が必要なシーン
特性簡単な拡散反射拡散反射と鏡面反射(ハイライト)物理的に正確な反射、拡散反射、金属感、粗さ物理的に正確な反射、透明感、屈折、金属感

meshToonMaterial

meshToonMaterialは、アニメ調の階段状のシェーディングを持つマテリアル。リアルな表現というよりは、カートゥーン風などを表現する際に利用することが多いです。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshToonMaterial color="red" />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

meshDepthMaterial

meshDepthMaterialは、深度に基づいて色を変える特殊なマテリアル。オブジェクトからカメラまでの距離を色で表現しています。こちらも光の影響は受けません。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshDepthMaterial />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

meshMatcapMaterial

meshMatcapMaterialは、光源を考慮せず、2Dテクスチャ(MatCap)を利用してリアルな陰影を再現するマテリアル。
画像に光の情報などが含まれているため、リアルな表現が可能になっている。

import { Vector3 } from "three";
import { Canvas, useLoader } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import { TextureLoader } from "three";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  // MatCapテクスチャをTextureLoaderで読み込む
  const matcapTexture = useLoader(
    TextureLoader,
    "https://threejs.org/examples/textures/matcaps/matcap-porcelain-white.jpg"
  );

  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <meshMatcapMaterial matcap={matcapTexture} />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

React Three DreiのMaterialsの種類

React Three Dreiには、さらに高度なMaterialが提供されています。

MeshDiscardMaterial

MeshDiscardMaterialは、何もレンダリングしないマテリアル。影や子を表示したまま、シーンからオブジェクトを非表示にすることができます。

import { Canvas } from "@react-three/fiber";
import { MeshDiscardMaterial, OrbitControls } from "@react-three/drei";
import { Vector3 } from "three";

const INITIAL_CAMERA_POSITION = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas
      shadows
      camera={{ position: INITIAL_CAMERA_POSITION }}
      style={{ background: "lightblue" }}
    >
      <mesh castShadow receiveShadow>
        <torusKnotGeometry />
        <MeshDiscardMaterial />
      </mesh>

      {/* 地面(受ける影) */}
      <mesh
        receiveShadow
        position={[0, -1.8, 0]}
        rotation={[-Math.PI / 2, 0, 0]}
      >
        <planeGeometry args={[5, 5]} />
        <meshStandardMaterial color="lightgray" />
      </mesh>

      {/* 照明設定 */}
      <ambientLight intensity={0.3} />
      <directionalLight
        position={[5, 5, 5]}
        intensity={0.7}
        castShadow
        shadow-mapSize-width={1024}
        shadow-mapSize-height={1024}
      />

      {/* カメラ操作 */}
      <OrbitControls />
    </Canvas>
  );
}

export default App;

MeshDistortMaterial

MeshDistortMaterialは、Geometryを歪ませることができるマテリアル。色や質感だけでなく、動きが付くのが特徴です。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { MeshDistortMaterial, OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <MeshDistortMaterial distort={1} speed={10} />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

MeshReflectorMaterial

MeshReflectorMaterialは、メッシュに反射やぼかしを簡単に追加できるマテリアル。MeshStandardMaterialの拡張で、より簡単に鏡みたいな表現が可能になります。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import {
  MeshDistortMaterial,
  MeshReflectorMaterial,
  OrbitControls,
} from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <MeshDistortMaterial distort={1} speed={10} />
      </mesh>
      {/* 反射面(地面) */}
      <mesh rotation-x={-Math.PI / 2} position={[0, -3, 0]}>
        <planeGeometry args={[10, 10]} />
        <MeshReflectorMaterial
          blur={[200, 100]} // X, Y方向のぼかしを設定
          mixBlur={0.5} // ぼかしの強さを調整
          mixStrength={0.7} // 反射の強さ
          mixContrast={1} // 反射のコントラストを調整
          resolution={512} // 解像度、値を上げるほどきれいになるがパフォーマンスに影響
          mirror={1} // 環境から反射を取得
          depthScale={0.1} // 深さのスケール
          reflectorOffset={0.2} // 反射面のオフセット
        />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

MeshTransmissionMaterial

MeshTransmissionMaterialは、MeshPhysicalMaterialを改良したマテリアル。透過や屈折をより簡単に扱えるように設計されています。特に IOR(屈折率)を簡単に設定できる点が大きな特徴なのかなと思います。

IOR (Index of Refraction): 光が物質中を進む速さと空気中を進む速さの比率を表す数値を表しており、これが簡単に設定できるとガラスやダイヤモンド、水などの表現が非常に簡単になります。

  • 空気のIOR: 1.0
  • ガラスのIOR: 1.5
  • ダイヤモンドのIOR: 2.42
  • のIOR: 1.33
import { Canvas } from "@react-three/fiber";
import {
  Caustics,
  MeshTransmissionMaterial,
  OrbitControls,
} from "@react-three/drei";
import { Vector3 } from "three";

// 初期カメラ位置
const INITIAL_CAMERA_POSITION = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas shadows camera={{ position: INITIAL_CAMERA_POSITION, fov: 45 }}>
      <color attach="background" args={["#f0f0f0"]} />
      <Caustics
        color="#FF8F20"
        position={[0, -0.5, 0]}
        lightSource={[5, 5, -10]}
        worldRadius={0.01}
        ior={1.2}
        intensity={0.005}
        causticsOnly={false}
        backside={false}
      >
        <mesh castShadow receiveShadow position={[-2, 0.5, -1]} scale={0.5}>
          <sphereGeometry args={[1, 64, 64]} />
          <MeshTransmissionMaterial
            resolution={1024}
            distortion={0.25}
            color="#FF8F20"
            thickness={1}
            anisotropy={1}
          />
        </mesh>
      </Caustics>
      <mesh>
        <boxGeometry />
        <meshNormalMaterial />
      </mesh>

      {/* 照明設定 */}
      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      {/* カメラ操作 */}
      <OrbitControls />
    </Canvas>
  );
}

export default App;

MeshWobbleMaterial

MeshWobbleMaterialは、ジオメトリを揺らしたり波打ったりさせることができるマテリアル。これも動きを付けることが可能なマテリアルとなっています。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { MeshWobbleMaterial, OrbitControls } from "@react-three/drei";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh>
        <torusKnotGeometry />
        <MeshWobbleMaterial factor={1} speed={10} />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 5, 5]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

SoftShadows

SoftShadowsは、こちらはマテリアルではないのですが、よりリアルな影を付けることが簡単になるコンポーネントです。影の濃淡が簡単に表現できるようになります。

import { Canvas } from "@react-three/fiber";
import { SoftShadows, OrbitControls } from "@react-three/drei";
import { Vector3 } from "three";

const INITIAL_CAMERA_POSITION = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas
      shadows
      camera={{ position: INITIAL_CAMERA_POSITION }}
      style={{ background: "lightblue" }}
    >
      {/* SoftShadowsを有効にする */}
      <SoftShadows />

      <mesh castShadow receiveShadow>
        <torusKnotGeometry />
        <meshStandardMaterial color="red" />
      </mesh>

      {/* 地面(受ける影) */}
      <mesh
        receiveShadow
        position={[0, -1.8, 0]}
        rotation={[-Math.PI / 2, 0, 0]}
      >
        <planeGeometry args={[5, 5]} />
        <meshStandardMaterial color="lightgray" />
      </mesh>

      {/* 照明設定 */}
      <ambientLight intensity={0.3} />
      <directionalLight
        position={[5, 5, 5]}
        intensity={0.7}
        castShadow
        shadow-mapSize-width={1024}
        shadow-mapSize-height={1024}
      />

      {/* カメラ操作 */}
      <OrbitControls />
    </Canvas>
  );
}

export default App;

ちょっとわかりずらいですが、先ほどのMeshDiscardMaterialの影と比較すると、分かりやすいかもしれません。

Materialのフロントサイド・バックサイドについて

マテリアルには、どちらの面をレンダリングするかを定義するsideがあります。
デフォルトでは、FrontSideとなっておりますが、BackSideを設定することで裏側をレンダリングすることも可能です。

import { Vector3 } from "three";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import * as THREE from "three";

// 定数
const INITIAL_CAMERA_POSITION: Vector3 = new Vector3(3, 3, 3);

function App(): JSX.Element {
  return (
    <Canvas camera={{ position: INITIAL_CAMERA_POSITION }}>
      <mesh position={[0, 0, 0]}>
        <boxGeometry args={[1, 1, 1]} />
        <meshStandardMaterial
          color="red"
          side={THREE.FrontSide} // Optional as it's the default
        />
      </mesh>

      <mesh position={[2, 0, 0]}>
        <boxGeometry args={[1, 1, 1]} />
        <meshStandardMaterial
          color="red"
          side={THREE.BackSide} // Optional as it's the default
        />
      </mesh>

      <ambientLight intensity={0.5} />
      <directionalLight position={[0, 3, 3]} intensity={0.7} />

      <OrbitControls />
    </Canvas>
  );
}

export default App;

標準ジオメトリやマテリアルを利用して、3Dコンテンツの作成

React Three FiberとReact Three Dreiの標準ジオメトリやマテリアルを活用して、シンプルかつ面白いインタラクティブな3Dコンテンツを作ってみましょう。

ログイン画面(ゲーム風)

ポケポケのパックを選ぶ画面を再現し、少しカスタマイズしてみました!!
👇作成するのは、こんな感じの 3D 背景 です👇

📺 YouTubeでデモを見る:こちらのリンクから視聴できます。

参考になった方は、是非チャンネル登録をお願いします!

TypeScript x React Three Fiberのレッスンや作品は、今後もどんどん作成していきます!
YouTubeで告知致しますので、ぜひYouTubeのチャンネル登録をして通知をお待ちください!

📺 YouTubeを見る:こちらのリンクから視聴できます。

React Three Fiberで何ができるのか知りたい方は、下記を参考にしてみてください!
簡単にできる作品を用意しております!

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

情報セキュリティを勉強するために始めたブログです。
新人のため、広い心を持って見ていただけると嬉しく思います。
楽しくプログラミングを勉強するために、「Teech Lab.」もありますので、ソフトウェア開発にも興味があればぜひ覗いて見てください!

目次