React Three FiberでThree.jsに入門する
お仕事でR3Fと
まずはR3Fで
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;
アニメーション等で
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;
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;
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を 設定している
