iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

React-three-fiberを使ってみた!

こんにちは。

アドベントカレンダー14日目!

担当のタクシです。

普段はフロントエンド中心の業務をしています。

アドベントカレンダーを書くために、最近あまり触れていないThreejsについて調べていたのですが、

「React three js」

と検索したらReact-three-fiberというものが出てきました。

Reactでthreejsがオーバーヘッドなしに使える!らしいので試してみました!

(あまり体力が続かなかったのでThreeやReactに関する詳しい説明を省いてしまっています。。すみません。。)

React-three-fiber

React-three-fiberはReactコンポーネントのようにThreejsが書けるライブラリです。

私自身、Threejsを初めて使ったときに感じたことですが、

慣れていないと正直なかなか難しい、、

WebGLを比較的容易に扱える反面、複雑な3D表現をすればするほどコード量は増え、かつ命令的なプログラムは読み解くのが簡単ではありません。

Threejsで立方体を描画する場合以下のようになります。

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)

const renderer = new THREE.WebGLRenderer()
renderer.setSize(width, height)
document.querySelector('#canvas-container').appendChild(renderer.domElement)

const mesh = new THREE.Mesh()
mesh.geometry = new THREE.BoxGeometry()
mesh.material = new THREE.MeshStandardMaterial()

scene.add(mesh)

function animate() {
  requestAnimationFrame(animate)
  renderer.render(scene, camera)
}

animate()

React-three-fiberの場合、

<Canvas>
  <mesh>
    <boxGeometry />
    <meshStandardMaterial />
  </mesh>
</Canvas>

コード量が格段に減り、可読性が上がりました。

MeshやBoxGeometryなどのThreejsオブジェクトはReact-three-fiberではネイティブなJSX elementsとして扱われます。importなどは必要ありません。

Reactコンポーネントのようにdeclarative(宣言的)にThreejsが書けるのはなかなかワクワクです。

今回は実際のReactアプリで試してみようかと思います。 気になる方はぜひ試してみてください!

セットアップ

// ## 今回はviteを使います
npm create vite kawaii-app

//reactをframeworkとして選択
cd kawaii-app

// typescriptを選択した場合は@types/threeもインストールします
npm install three @types/three @react-three/fiber

// ローカルサーバー起動
npm run dev

まずはドキュメント通りに立方体を作るコードを入れていきます。 https://docs.pmnd.rs/react-three-fiber/getting-started/your-first-scene

立方体

コード

App.tsx

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

function App() {
  return (
    <div id="boxGeometry">
      <Canvas>
        <mesh>
          <boxGeometry />
          <meshStandardMaterial />
        </mesh>
      </Canvas>
    </div>
  );
}

export default App;

結果

この今のところ真っ黒な物体を描画するために必要なものは以下の4つです。

Canvas

CanvasオブジェクトにはThreejsにおける、Scene PerspectiveCamera WebGLRenderer などなどが含まれており、

デフォルトの状態でもchildrenとして囲われたReact-three-fiberの3Dオブジェクトを描画してくれます。propsでcameraやsceneの設定をすることができます。

https://docs.pmnd.rs/react-three-fiber/api/canvas

mesh

ThreejsにおけるMeshオブジェクトです。

meshはいわば、3Dオブジェクトの骨組みのようなものです。(と私は思っていますが。。)

https://threejs.org/docs/?q=mesh#api/en/objects/Mesh

boxGeometry

boxという名の通り、立方体の基礎となる物体を描画するためのオブジェクトです。

幅、高さ、奥行きを持ち、propsで数値を設定して形を変えることができます。

https://threejs.org/docs/?q=box#api/en/geometries/BoxGeometry

meshStandardMaterial

materialは英語で材料です。

geomeryなどと組み合わせることで、色の変化や表面の質感など様々な表現ができます。

https://threejs.org/docs/?q=meshStandar#api/en/materials/MeshStandardMaterial

立体でもないし、色もなくてかわいくないのでそこら辺をやってみます。

コード

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

function App() {
  return (
    <div id="boxGeometry">
      <Canvas>
        <directionalLight position={[0, 0, 5]} />
        <mesh rotation-x={4} rotation-y={1}>
          <boxGeometry />
          <meshStandardMaterial color="#60ceff" />
        </mesh>
      </Canvas>
    </div>
  );
}

export default App;

結果

meshのpropsにrotationを追加したので3D感が出ました。

コードを見ていると、directionalLight mesh meshStandardMaterial にpropsを渡していることがわかります。

これらのpropsはThreejsオブジェクトである、DirectionalLight Mesh MeshStandardMaterial のプロパティやコンストラクタと同じで、

<mesh rotation-x={4} rotation-y={1}>

は、Threejsの以下と同義になります。

const mesh = new THREE.Mesh()
mesh.rotation.x = 4
mesh.rotation.y = 1

先ほど真っ黒だったのは光が当たっていなかったからですね。

directionalLight を追加したことで色が見えるようになり、meshStandardMaterial にcolorを追加したことでその色が反映されるようになりました!

回る立方体

先ほどの立方体をちょっと回してみましょう。

コード

import { Canvas, useFrame } from "@react-three/fiber";
import { useRef } from "react";
import { Mesh } from "three";
import "./App.css";

const RotationBox = () => {
  const mesh = useRef<Mesh>(null!);
  useFrame(() => {
    mesh.current.rotation.x = mesh.current.rotation.x += 0.01;
    mesh.current.rotation.y = mesh.current.rotation.y += 0.01;
    mesh.current.rotation.z = mesh.current.rotation.z += 0.01;
  });
  return (
    <mesh ref={mesh}>
      <boxGeometry />
      <meshStandardMaterial color="#60ceff" />
    </mesh>
  );
};

function App() {
  return (
    <div id="kawaii" className="kawaii-container">
      <Canvas>
        <directionalLight position={[0, 0, 5]} />
        <RotationBox />
      </Canvas>
    </div>
  );
}

export default App;

結果(くるくるカワイイ)

アニメーションを付けるには、useFrame hookを使います。

useFrame

Canvas内にあるは1フレームごとに描画されます。

その際、useFrameを使用すれば1フレームごとに処理を実行することができるので、

アニメーションに使うことができます。

https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe

今回はuseFrame内でrotationのx y zをそれぞれ0.01ずつ増加させるようにしました。

この数字を変えることで回転スピードを変えることができます。

まとめ

ふむふむ。。

Reactで扱いやすくなってかなり良かったです!

Threejsの知識はやはり必要ですが、それでもstateなども使ってインタラクティブなウェブサイトなどを作る際幅が広がりそうです。

個人としてはどんどん学んでいきたいところです!

引き続き色々作っていってみようかと思います!(結構楽しかったので趣味になりそうです) 目指せマスターofマスター!

明日は我らがスピードスターみよちゃんでございます!! 楽しみですね!

参考

https://www.mlit.go.jp/plateau/learning/tpc12-2/ https://liginc.co.jp/587025 https://qiita.com/sho-19202325/items/b1d56c627856818f4bf0#useframe https://threejs.org/