LoginSignup
20
6

React Three Fiber + TypeScript: 3次元空間で立方体を回してみる

Last updated at Posted at 2022-08-04

React Three Fiberは、Reactのコンポーネントでthree.jsの3次元空間を組み立てて、3Dオブジェクトが操作できるライブラリです。Reactの宣言的な記述で、WebGLによる3D表現が簡単にできます。採り上げるのは、3次元空間で立方体を回してみるというつぎのような作例です。カメラはマウスでインタラクティブに操作できます。

本稿の作例
https://codesandbox.io/s/react-three-fiber-typescript-basic-example-dnzllu?file=/src/App.tsx:579-605

マウスによるカメラ操作

  • ドラッグ: 立方体の中心から一定の距離で旋回する。
  • 右ボタンドラッグ: カメラの方向は保ったまま、フレーミングを水平・垂直に移動させる。
  • ホイール: カメラの方向に、映像を拡大・縮小させる。

ひな形プロジェクトの作成とインストール

まず、作業の準備です。プロジェクトのひな形は、Create React Appでつくります(プロジェクト名はreact-three-fiber-typescriptとしました)。テンプレート(--template)のオプションに加えたのはTypeScriptです。もっとも、型推論が働くので、明示的な型づけはほとんどしていません。

npx create-react-app react-three-fiber-typescript --template typescript

そして、three.js(three)とReact Three Fiber(@react-three/fiber)のインストールです。@types/threeも加えてください。

npm install three @react-three/fiber
npm install --save-dev @types/three

3次元空間に立方体を加える

素の(標準)JavaScriptでthree.jsによる3次元空間の舞台を整えるには、まずつぎの3つのオブジェクトがつくられなければなりません。

  • シーン(scene)
    • 3次元空間を表し、3Dオブジェクトやライトが加えられる。
  • カメラ(camera)
    • 3次元空間を映し、レンダラーに送って画面に投影する。
  • レンダラー(renderer)
    • カメラから受け取った画像を、画面の再描画のたびに更新する。

それが、React Three Fiberでは、Canvasコンポーネントひとつで済むのです。作例のつぎのコード(src/App.tsx)には、cameraプロパティでカメラの定めを加えました。けれど、デフォルトの設定はされているので、除いても3次元空間の準備はできているのです。Canvasの子として加える3Dオブジェクトの立方体(Cube)は、このあとコンポーネントとして定めます。

src/App.tsx
import React from 'react';
import { Canvas } from '@react-three/fiber';
import { Cube } from './Cube';

function App() {
	return (
		<div className="App">
			<Canvas
				camera={{
					fov: 45,
					near: 0.1,
					far: 1000,
					position: [0, 0, 5]
				}}
			>
				<Cube />
			</Canvas>
		</div>
	);
}

export default App;

cameraプロパティに定めた項目の内容はつぎのとおりです。こうした設定をするには、three.jsのドキュメントに当たらなければなりません。標準JavaScriptで使うthree.jsの基本については、「three.js + TypeScript: 3次元空間で立方体を回してみる」をご参照ください。

  • fov: 視野(field of view)またはカメラの用語で画角。写される光景の範囲を角度(度数)で示す。数字が大きいと広角で、写る範囲は広がるかわり、対象物は相対的に小さくなる。小さい角度は望遠に近づき、対象物が相対的に大きく写る。
  • aspect: アスペクト比で、描画する矩形領域の $ \frac{幅}{高さ} $ 。矩形領域の比と合わなければ、画像の水平・垂直比が歪む。
  • near: オブジェクトを描画するもっとも近い距離。これより近くのオブジェクトは画面に描かれない。
  • far: オブジェクトを描画するもっとも遠い距離。これより遠くのオブジェクトは画面に描かれない。

3Dオブジェクトを定めるのはmeshコンポーネントです。その中には形状を決めるジオメトリと、表面素材のマテリアルが加えられなければなりません。立方体はboxBufferGeometryでつくれます。マテリアルに選んだのはmeshPhongMaterialです。3Dオブジェクトはデフォルトでは、3次元空間の原点(0, 0, 0)を中心に配置されます。

つぎのコード例でboxBufferGeometryには、プロパティとしてargsに配列を与えました。これは、three.jsのBoxBufferGeometry()コンストラクタに渡す引数(arguments)の意味です。立方体の幅と高さ、および奥行きを定めます。なお、three.jsでは、距離や長さは1を基本とした数値です。カメラの設定でオブジェクトは大きくも小さくも描かれるので、単位は気にせず比率と捉えて構いません。

マテリアルに用いたのは、three.jsのMeshPhongMaterialです。表面は鏡面反射する光沢があります。colorプロパティには、CSSと同じかたちでカラーが定められ、16進数の場合は数値でも構いません。

src/Cube.tsx
import { FC } from 'react';

export const Cube: FC = () => {
	return (
		<mesh>
			<boxBufferGeometry args={[1, 1, 1]} />
			<meshPhongMaterial color="aqua" />
		</mesh>
	);
};

アプリケーションを実行してみると、立方体が真っ黒に描かれるでしょう。明かりがないからです。デフォルトの背景色が白であることに、ごまかされてはいけません。3次元空間に光を与えないと、3Dオブジェクトの正しい姿は見えないのです。

平行光源を加える

まず光源として加えるのは、directionalLight(DirectionalLight)です。「平行光源」とも呼ばれ、太陽光のようにひとつの方向から、光が平行に差し込みます。光が差してくる方向を定めるのがpositionプロパティです。原点に向かって差し込むので、座標(1, 1, 1)は、原点(0, 0, 0)と立方体の初期位置の前面右上角の座標(0.5, 0.5, 0.5)を結んだ直線の方向になります(右斜め上手前)。intensityプロパティの数値は、1を100%とした光の明るさです。

src/App.tsx
function App() {
	return (
		<div className="App">
			<Canvas

			>

				<directionalLight position={[1, 1, 1]} intensity={0.8} />
			</Canvas>
		</div>
	);
}

これで、指定したカラー(aqua)の3Dオブジェクトが表示されたでしょう。もっとも、カメラに正面向きなので、立方体か正方形平面か区別がつきません。

立方体に回転のアニメーションを加える

立方体をアニメーションで回転してみましょう。そのとき用いるフックが、React Three FiberのuseFrameです。立方体のmeshコンポーネントにはrefプロパティを与えておきます。xおよびy軸周りの回転を加えるのはrotationプロパティです(単位ラジアン)。フックに定めたコールバックは、再描画のたびに呼び出されます。立方体が回っていることを確かめられるでしょう。

import { useFrame } from '@react-three/fiber';
// import { FC } from 'react';
import { FC, useRef } from 'react';
import type { Mesh } from 'three';

export const Cube: FC = () => {
	const cubeRef = useRef<Mesh>(null);
	useFrame(() => {
		const cube = cubeRef.current;
		if (!cube) return;
		cube.rotation.x += 0.01;
		cube.rotation.y += 0.01;
	});
	return (
		// <mesh>
		<mesh ref={cubeRef}>

		</mesh>
	);
};

環境光を加える

まだ、少し気になるかもしれません。光の当たっていない面が漆黒であることです。物理的に考えれば、仕方ありません。でも、現実と照らし合わせると違和感があるでしょう。そこで用いる光源がコンポーネントambientLightです。argsプロパティの配列は、AmbientLight()コンストラクタの引数で、光のカラーを定めます。明るさのintensityプロパティの値は控えめにしました。

src/App.tsx
function App() {
	return (
		<div className="App">
			<Canvas

			>

				<ambientLight args={[0xffffff]} intensity={0.2} />

			</Canvas>
		</div>
	);
}

立方体の陰になった面にも明るさが加わったでしょう。

OrbitControlsでカメラをマウス制御する

OrbitControlsを用いると、カメラが簡単にマウスで制御できます。@react-three/fiberとは別モジュールとして、インストールに加えてください。

npm install @react-three/drei

使い方は簡単で、CanvasOrbitControlsコンポーネントを加えるだけです。マウス制御の基本機能は、すでにデフォルトで備わっています。

src/App.tsx
import { OrbitControls } from '@react-three/drei';

function App() {
	return (
		<div className="App">
			<Canvas

			>

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

これで、冒頭にご紹介した作例はでき上がりです。ふたつのモジュールの記述全体を、つぎのコード001にまとめました。JSXを除いたJavaScriptコードはほとんどないのが驚きです。

コード001■3次元空間の立方体を回転させてカメラはマウス制御する

src/App.tsx
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { Cube } from './Cube';

function App() {
	return (
		<div className="App">
			<Canvas
				camera={{
					fov: 45,
					near: 0.1,
					far: 1000,
					position: [0, 0, 5]
				}}
			>
				<Cube />
				<ambientLight args={[0xffffff]} intensity={0.2} />
				<directionalLight position={[1, 1, 1]} intensity={0.8} />
				<OrbitControls />
			</Canvas>
		</div>
	);
}

export default App;
src/Cube.tsx
import { useFrame } from '@react-three/fiber';
import { FC, useRef } from 'react';
import type { Mesh } from 'three';

export const Cube: FC = () => {
	const cubeRef = useRef<Mesh>(null);
	useFrame(() => {
		const cube = cubeRef.current;
		if (!cube) return;
		cube.rotation.x += 0.01;
		cube.rotation.y += 0.01;
	});
	return (
		<mesh ref={cubeRef}>
			<boxBufferGeometry args={[1, 1, 1]} />
			<meshPhongMaterial color="aqua" />
		</mesh>
	);
};
20
6
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
20
6