2024-10-19
React Three FiberでThree.jsに入門する
react-three-fiber
Three.js
お仕事でReact Three Fiber(以降R3F
とする)を使うことになるかもしれないので、面白そうと思って買って積んでいた初めてのThree.js 第2版 ―WebGLのためのJavaScript 3Dライブラリ―を読みながらキャッチアップしていく。
まずは1章の初めての3DシーンのサンプルをR3F
で書き直して動かしてみる。
プロジェクトはViteで作成。
App.tsx
import './App.css';
import { Canvas } from '@react-three/fiber';
import { Stats } from '@react-three/drei';
import { SpotLight } from 'three';
import { useRef } from 'react';
import Cube from './components/Cube';
import Sphere from './components/Sphere';
import PerspectiveCamera from './components/ PerspectiveCamera';
function App() {
const spotLightRef = useRef<SpotLight>(null);
return (
<div id="canvas-container">
<Canvas
onCreated={(state) => {
state.gl.setClearColor('#EEEEEE');
state.gl.setSize(window.innerWidth, window.innerHeight);
}}
shadows
>
<Stats />
<PerspectiveCamera
args={[45, window.innerWidth / window.innerHeight, 0.1, 1000]}
position={[-30, 40, 30]}
castShadow
/>
<axesHelper args={[20]} />
<mesh position={[15, 0, 0]} rotation-x={-0.5 * Math.PI} receiveShadow>
<planeGeometry args={[60, 20]} />
<meshLambertMaterial color="#ffffff" />
</mesh>
<Cube position={[-4, 3, 0]} castShadow />
<Sphere position={[20, 4, 2]} castShadow />
<spotLight
ref={spotLightRef}
position={[-20, 30, -5]}
color="#ffffff"
intensity={2000}
castShadow
/>
{spotLightRef.current && (
<cameraHelper args={[spotLightRef.current.shadow.camera]} />
)}
</Canvas>
</div>
);
}
export default App;
アニメーション等でhooksを利用したいのでコンポーネントとして切り出す
PerspectiveCamera.tsx
import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
import { PerspectiveCameraProps, useFrame, useThree } from '@react-three/fiber';
import { useRef } from 'react';
import { PerspectiveCamera as ThreePerspectiveCamera } from 'three';
function PerspectiveCameraWithLookAt(props: PerspectiveCameraProps) {
const cameraRef = useRef<ThreePerspectiveCamera | null>(null);
const { scene } = useThree();
useFrame(() => {
if (cameraRef.current) {
cameraRef.current.lookAt(scene.position);
}
});
return (
<>
<PerspectiveCamera ref={cameraRef} makeDefault {...props} />
<OrbitControls target={[0, 0, 0]} makeDefault />
</>
);
}
export default PerspectiveCameraWithLookAt;
Cube.tsx
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';
import { Mesh } from 'three';
function Cube(props: JSX.IntrinsicElements['mesh']) {
const ref = useRef({} as Mesh);
useFrame(() => {
if (ref.current) {
ref.current.rotation.x += 0.02;
ref.current.rotation.y += 0.02;
ref.current.rotation.z += 0.02;
}
});
return (
<mesh ref={ref} {...props}>
<boxGeometry args={[4, 4, 4]} />
<meshLambertMaterial color="#ff0000" />
</mesh>
);
}
export default Cube;
Sphere.tsx
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';
import { Mesh } from 'three';
function Sphere(props: JSX.IntrinsicElements['mesh']) {
const ref = useRef({} as Mesh);
let step = 0;
useFrame(() => {
if (ref.current) {
step += 0.04;
ref.current.position.x = 20 + 10 * Math.cos(step);
ref.current.position.y = 2 + 10 * Math.abs(Math.sin(step));
}
});
return (
<mesh ref={ref} {...props}>
<sphereGeometry args={[4, 20, 20]} />
<meshLambertMaterial color="#7777ff" />
</mesh>
);
}
export default Sphere;
ざっくり理解
Canvas
でcameraやスタイルを設定。- Childrenには
Scene
に追加する3Dオブジェクトを宣言的に記述できるようになっている。 Mesh
はGeometry
とMaterial
で構成されるGeometry
はオブジェクトの形状Material
はオブジェクトがどのように見えるか(このサンプルでは色やワイヤーフレームを設定)
axesHelper
は座標ガイドでx,y,zの軸を描画してくれるR3F
のコンポーネントはimport不要- https://r3f.docs.pmnd.rs/getting-started/your-first-scene#adding-a-mesh-component
- 全てのThree.jsオブジェクトはネイティブなJSX要素として扱われる(JSXが拡張されている?)
補足
spotLight
は書籍ではintensity
プロパティ未設定のようだったが、設定しないと3Dオブジェクトが全て真っ暗になってしまうため2000を設定している