React-three-fiber(この記事では、r3fと呼称する。)とは、ReactでThree.jsを扱うためのラッパーライブラリで、Reactの宣言的な記述に合わせてWebGLシーンを構築することができます。
対象者
- r3fを使ったことがない方
- three.jsの知識をお持ちの方
- Reactで3Dのサイトを作りたい方
インストール
$ npm install three @react-three/fiber
また、TypeScript
をお使いの場合は下記に記載しているモジュールもインストールします。
$ npm install @types/three
Canvas
r3fを使う時に必ずラップする。
import React from 'react'
import { Canvas } from '@react-three/fiber'
const App = () => (
<Canvas>
...
</Canvas>
)
export default App;
Canvasコンポーネントではcamera、renderer、sceneを指定した事になります。
cameraの位置はデフォルトでは{ fov: 75, near: 0.1, far: 1000, position: [0, 0, 5] }
もし、camera位置を変更したい時には下記のようにpropsで指定します。
import React from 'react'
import { Canvas } from '@react-three/fiber'
const App = () => (
<Canvas camera={ { fov: カメラの錐台の垂直視野, near: 平面近くのカメラ錐台, far: カメラの錐台の遠方平面, position: [x座標, y座標, z座標] } }>
...
</Canvas>
)
export default App;
Mesh
いよいよ物体を表示するのですが、物体を表示させる為にはメッシュと呼ばれるものが必要でした。
r3fでは、meshコンポーネントがあります。<mesh />
一方で、メッシュを作成には形状(ジオメトリ) と 材質(マテリアル) が必要でした。
なので、最終的には下記のようなコードになるはずです。
import React from 'react'
import { Canvas } from '@react-three/fiber'
const App = () => (
<Canvas>
<mesh>
<Geometry />
<Material />
</mesh>
</Canvas>
)
export default App;
形状(ジオメトリ)
基本はthree.jsのインスタンスメソッドがコンポーネントになったと思ってください。
例えば、直方体を指定したい時には下記のインスタンスメソッドをメソッドを作成していました。
new THREE.BoxGeometry(1, 1, 1)
これが、r3fだと下記のようになります。
<boxGeometry args={[1, 1, 1]} />
このように、r3fではインスタンスメソッドがそのままコンポーネントになっています。
材質(マテリアル)
Materialの場合も同様に、three.jsのインスタンスメソッドがコンポーネントになっています。
例えば、MeshBasicMaterial
を使いたい場合、three.jsでは下記のように指定していました。
new THREE.MeshBasicMaterial({ color: "hotpink" })
これが、r3fだと下記のようになります。
<meshBasicMaterial color={"hotpink"} />
光源(ライト)
使用するMaterialによっては光源(ライト)を照らしてあげないといけない場合があります。 ここでは、光源(ライト)の照らし方を紹介します。
因みに余談なのですが、光源(ライト)を照らさなくても大丈夫なMaterialを紹介します。
MeshBasicMaterial
MeshNormalMaterial
ここでは、平行光源(DirectionalLight)の当て方を紹介します。
<directionalLight color={"white"} intensity={0.5} position={[0, 0, 0]} />
// color : 光の色
// intensity : 光の強さ
// position : 平行光源のポジショニング
アニメーション
JavaScriptでアニメーションをさせるには、時間経過で関数を呼び続ける必要があります。そのためには、requestAnimationFrame()
というグローバルメソッドを使用します。
r3fではuseFrame()
というメソッドが用意されています。
では、サンプルコードを交えてuseFrame()
を使っていきます。
import { Canvas, useFrame } from "@react-three/fiber";
import React, { useRef } from "react";
import { Mesh } from "three";
function App() {
const ref = useRef({} as Mesh);
useFrame(() => (ref.current.rotation.x += 0.01));
return (
<>
<Canvas
camera={{ fov: 70, near: 0.1, far: 2000 }}
style={{ width: "100vw", height: "100vh" }}
>
<color args={["#5bbee5"]} attach={"background"} />
<ambientLight intensity={0.5} />
<directionalLight position={[10, 10, 20]} />
<mesh ref={ref} position={[-2, 0, 0]}>
<boxGeometry args={[1, 1, 1]} />
<meshPhongMaterial color={"hotpink"} />
</mesh>
<mesh ref={ref} position={[0, 0, 0]}>
<boxGeometry args={[1, 1, 1]} />
<meshPhongMaterial color={"hotpink"} />
</mesh>
<mesh ref={ref} position={[2, 0, 0]}>
<boxGeometry args={[1, 1, 1]} />
<meshPhongMaterial color={"hotpink"} />
</mesh>
</Canvas>
</>
);
}
export default App;
3つのBoxGeometryが縦方向に少しずつ回転していくアニメーションを行なっていく処理を記述していきましたが、下記のようなエラーが出ました。
R3F: hooks can only be used within the Canvas component
useFrame()
は<Canvas />
の中でしか使えないよ〜と言ってきます。
なので、以下のようにメッシュを子コンポーネントで管理します。
import { Canvas } from "@react-three/fiber";
import Box from "./compornents/Box";
function App() {
return (
<>
<Canvas
camera={{ fov: 70, near: 0.1, far: 2000 }}
style={{ width: "100vw", height: "100vh" }}
>
<color args={["#5bbee5"]} attach={"background"} />
<ambientLight intensity={0.5} />
<directionalLight position={[10, 10, 20]} />
<Box position={[-2.4, 0, 0]} />
<Box position={[0, 0, 0]} />
<Box position={[2.4, 0, 0]} />
</Canvas>
</>
);
}
export default App;
import { useFrame } from "@react-three/fiber";
import React, { useRef } from "react";
import { Mesh } from "three";
const Box = (props: JSX.IntrinsicElements["mesh"]) => {
const ref = useRef({} as Mesh);
useFrame(() => (ref.current.rotation.x += 0.01));
return (
<mesh ref={ref} {...props}>
<boxGeometry args={[1, 1, 1]} />
<meshPhongMaterial color={"hotpink"} />
</mesh>
);
};
export default Box;
チュートリアルっぽく仕上げる
最後に公式にあるチュートリアルっぽく、BoxGeometryをクリックすると拡大・縮小したり、hoverしたら色が変わる処理を追加し今回のブログを締めようと思います。
拡大・縮小
まず最初にクリックしたら拡大・縮小する処理を実装します。実装するには拡大・縮小を管理するstateが必要になるので用意します。
const [clicked, setClicked] = useState<boolean>(false);
useStateの値がfalse
なら縮小・true
なら拡大してあげます。
また、大きさを変化させる時に用いられるプロパティはscale
です。
<mesh
onClick={() => setClicked(!clicked)}
scale={clicked ? 2 : 1}
>
hoverしたら色が変わる
クリックしたら拡大・縮小する処理と同様に色を管理するstateを用意します。
const [hovered, setHovered] = useState<boolean>(false);
useStateの値がfalse
ならhotpink・true
ならorangeに変わるという処理を記述していきます。
以下のプロパティを付けることでマウスがhoverした時の処理、マウスが離れた時の処理を実装することができます。
<mesh
onPointerOver={() => setHovered(true)} // マウスがhoverした時の処理
onPointerOut={() => setHovered(false)} // マウスが離れた時の処理
>
...
<meshPhongMaterial color={hovered ? "orange" : "hotpink"} />
</mesh>
おわりに
いかがだったでしょうか?僕自身も今r3fにハマっていてこれからどんどんキャッチアップしていきたいなと思っていて、次回まだ未定なのですが@react-three/drei
を用いて色々やる記事を書こうかな?と思っています。
今回使用した、@react-three/fiber
と@react-three/drei
を組み合わせる事により、@react-three/fiber
だけではできなかった3D表現ができるようになるのでお楽しみに。ということで最後まで目を通していただきありがとうございました。