mcpField
T
threejs-impl-react-three-fiber

threejs-impl-react-three-fiber

github.com/impertio-studio開発23147 回インストール30 週間前に更新
リポジトリ

threejs-impl-react-three-fiber について

React Three Fiber 開発者向けの実装リファレンススキルです。Canvas の設定、フック、イベント、JSX から Three.js シーングラフへの変換を扱い、描画ループやリソース管理の指針も示します。

主な機能

  1. 01Canvas のレンダラーとカメラ設定
  2. 02useFrame、useThree などのフック
  3. 03JSX から Three.js シーンへの変換
  4. 04ポインターイベントとレイキャスト
  5. 05描画ループとリソースの最適化

活用シーン

  1. 01R3F Canvas と描画方式の設定
  2. 02フレーム単位のアニメーションやシーン処理
  3. 03メッシュ上のポインター操作の実装
  4. 04静的シーンの描画や重複生成の削減

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/three.js-claude-skill-package --skill threejs-impl-react-three-fiber

インストール後、Claude Code で/threejs-impl-react-three-fiberを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.gitkeep
references/
anti-patterns.md
examples.md
methods.md

詳細

threejs-impl-react-three-fiber

クイックリファレンス

Canvas コンポーネントの Props
PropTypeDefaultPurpose
glRenderer props | (canvas) => Renderer{}WebGL レンダラーの設定またはファクトリー
cameraCamera props | THREE.Camera{ fov: 75, near: 0.1, far: 1000, position: [0,0,5] }デフォルトのカメラ
sceneScene props | THREE.Scene{}シーンの設定
shadowsboolean | ShadowMapTypefalseシャドウマップを有効にする
raycasterRaycaster props{}レイキャスターの設定
frameloop"always" | "demand" | "never""always"レンダリングループの方式
resizeResizeOptions{ scroll: true, debounce: { scroll: 50, resize: 0 } }リサイズ時の動作
orthographicbooleanfalseOrthographicCamera を使用する
dprnumber | [min, max][1, 2]デバイスピクセル比
linearbooleanfalseリニア色空間
flatbooleanfalseトーンマッピングを無効にする
legacybooleanfalse色管理を無効にする
eventsEventManagerR3F defaultカスタムイベントマネージャー
eventSourceHTMLElement | React.RefObjectParent nodeイベントをキャプチャする要素
eventPrefixstring"offset"座標のプレフィックス
onCreated(state: RootState) => void--初期化後のコールバック
onPointerMissed(event: PointerEvent) => void--クリックがすべてのメッシュを外した場合
fallbackReact.ReactNode--初期化中の DOM フォールバック
Frameloop のモード
ModeBehavior
"always"requestAnimationFrame を使用して、毎フレーム必ずレンダリングします
"demand"invalidate() が呼び出されたときのみレンダリングします。静的なシーンで使用します
"never"自動ではレンダリングしません。呼び出し側が必ず advance(timestamp) を呼び出す必要があります
重要な警告

絶対に useFrame 内で Three.js オブジェクトを作成しないでください。毎フレームメモリが割り当てられ、GC の負荷が増加します。必ずコールバックの外でオブジェクトを作成するか、useMemo を使用してください。

絶対に useLoader を使用する際に <Suspense> を忘れないでください。Suspense 境界がないと、コンポーネントはサスペンドし、クラッシュします。

絶対に <primitive> を使って同じ Three.js オブジェクトのインスタンスをシーンツリーに複数回追加しないでください。Three.js オブジェクトが持てる親は 1 つだけです。

絶対に 1 つのプロパティだけが必要なときに、セレクターなしで useThree() を使用しないでください。状態オブジェクト全体を取得すると、毎フレーム再レンダリングが発生します。必ず useThree((s) => s.camera) を使用してください。

絶対に 命令型の scene.add() 呼び出しと、R3F の宣言型 JSX ツリーを混在させないでください。R3F はシーングラフを管理するため、命令型の変更によって同期がずれます。

アニメーションには必ず useFrame の delta を使用してください。時間ステップをハードコードすると、フレームレートによって速度にばらつきが生じます。

命令型で作成するジオメトリとマテリアルには必ず useMemo を使用してください。使用しないと、レンダリングのたびに新しいオブジェクトが割り当てられます。


JSX-to-Three.js のマッピング規則

R3F は決定的な規則に従って、JSX を Three.js のシーングラフ操作に変換します。

  1. 小文字の JSX = Three.js のクラス。 <mesh /> は new THREE.Mesh() を作成します。<meshStandardMaterial /> は new THREE.MeshStandardMaterial() を作成します。

  2. args = コンストラクター引数(配列)。 <sphereGeometry args={[1, 32, 32]} /> は new THREE.SphereGeometry(1, 32, 32) になります。args が変更されると、オブジェクトは破棄されて再作成されます。

  3. attach = 親プロパティへのバインド。 <meshStandardMaterial attach="material" /> は parent.material = this を設定します。ジオメトリは自動的に "geometry" に、マテリアルは "material" にアタッチされます。

  4. ネストされたパスにはハイフン区切りの attach を使用します。 attach="shadow-camera" は parent.shadow.camera = this を設定します。配列のインデックス指定には attach="material-0" を使用します。

  5. 関数形式の attach。 カスタムのバインド/解除には、attach={(parent, self) => { parent.add(self); return () => parent.remove(self); }} を使用します。

  6. .set() を受け付けるプロパティでは省略記法を使用できます。 position={[1, 2, 3]} は object.position.set(1, 2, 3) を呼び出します。color="hotpink" は object.color.set("hotpink") を呼び出します。

  7. スカラーの省略記法。 scale={2} は object.scale.setScalar(2) を呼び出します。

  8. ハイフン区切り表記でネストされたプロパティを指定できます。 rotation-x={Math.PI} は object.rotation.x = Math.PI を設定します。


フック

useFrame
useFrame((state: RootState, delta: number, xrFrame?: XRFrame) => void, priority?: number)

コールバックをレンダーループに登録します。毎フレーム実行されます。

State オブジェクトの主なプロパティ:

プロパティ型説明
glTHREE.WebGLRendererレンダラー
sceneTHREE.Sceneシーン
cameraTHREE.Cameraアクティブなカメラ
clockTHREE.Clockシステムクロック
pointerTHREE.Vector2正規化されたポインター(-1 から +1)
size{ width, height, top, left }キャンバスの寸法(px)
viewport{ width, height, factor, distance, aspect }カメラ相対のメトリクス
invalidate() => void要求時モードでのレンダーを要求します
advance(timestamp: number) => voidnever モードで 1 ティック進めます
performance{ current, min, max, regress() }適応型パフォーマンス
set(state) => voidState を直接変更します
get() => RootStateリアクティブではない方法で State を読み取ります

優先度システム: コールバックは優先度の昇順で実行されます。優先度が 0 より大きいコールバックが 1 つでもある場合、R3F は renderer.render() の自動実行を無効にします。最も優先度の高いサブスクライバーは、state.gl.render(state.scene, state.camera) を手動で呼び出す必要があります。負の優先度では自動レンダリングは無効になりません。

useThree
const state = useThree()                              // full state (re-renders often)
const camera = useThree((state) => state.camera)      // selector (re-renders only on change)

RootState(useFrame の State と同じオブジェクト)を返します。プロパティを 1 つだけ使用する場合は、必ずセレクターを使用してください。

useLoader
const result = useLoader(LoaderClass, url, extensions?, onProgress?)
const results = useLoader(LoaderClass, [url1, url2], extensions?)

Suspense ベースのアセット読み込みです。必ず <Suspense fallback={...}> でラップしてください。

  • アセットは URL でキャッシュされます。同じ URL を 2 回読み込むと、キャッシュされた結果が返されます。
  • useLoader.preload(LoaderClass, url) は、コンポーネントのマウント前にプリロードします。
  • GLTF の結果には { nodes, materials, scene, animations } が含まれます。
useGraph
const { nodes, materials } = useGraph(object3D)

Object3D の階層をたどり、名前をキーとするメモ化された { nodes, materials } コレクションを返します。


Primitives と extend()

Primitives

既存の Three.js オブジェクトを宣言的ツリーに挿入します。

<primitive object={existingMesh} position={[10, 0, 0]} />
  • 同じオブジェクトのインスタンスを複数回追加しないでください。Three.js オブジェクトが持てる親は 1 つだけです。
  • Primitives は自動的に dispose されません。ライフサイクルは呼び出し側が管理する必要があります。
extend()

カスタムの Three.js クラスを JSX 要素として登録します。

import { extend } from '@react-three/fiber'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

extend({ OrbitControls })
// Now usable as <orbitControls args={[camera, domElement]} />

JSX 要素の名前は、登録されたキーのキャメルケース版になります。


イベントシステム

R3F はレイキャスティングによってポインターイベントを実装します。イベントはシーングラフ内をバブリングします。

サポートされているイベント
イベントトリガー
onClickメッシュ上でポインターをクリック
onContextMenu右クリック/コンテキストメニュー
onDoubleClickダブルクリック
onPointerUpポインターを離す
onPointerDownポインターを押す
onPointerOverポインターがメッシュに入る(継続的に発火)
onPointerOutポインターがメッシュから出る
onPointerEnterポインターがメッシュに入る(一度だけ発火)
onPointerLeaveポインターがメッシュから出る(一度だけ発火)
onPointerMoveメッシュ上でポインターを動かす
onPointerMissedクリックがどのメッシュにも当たらない(Canvas レベル)
onWheelスクロールホイール
onUpdateオブジェクトが新しい props を受け取る
イベントオブジェクトのプロパティ
プロパティ型説明
objectTHREE.Object3D実際にヒットしたメッシュ
eventObjectTHREE.Object3Dイベントハンドラーを持つオブジェクト
pointTHREE.Vector3ワールド空間での交差点
distancenumberカメラから交差点までの距離
uvTHREE.Vector2交差点の UV 座標
faceTHREE.Face交差した面
rayTHREE.Ray交差判定に使われたレイ
cameraTHREE.Cameraアクティブなカメラ
intersectionsIntersection[]交差したすべてのオブジェクト
deltanumber下から上へのピクセル距離
sourceEventEvent元の DOM イベント
stopPropagation()function遮蔽されたオブジェクトへのイベント伝播を防止

パフォーマンスパターン

破棄
  • R3F はコンポーネントのアンマウント時に Three.js オブジェクトの dispose() を必ず呼び出し、GPU リソースを解放します。
  • 自動破棄を防ぐには、要素に dispose={null} を設定します。オブジェクトを複数のコンポーネント間で共有する場合に使用します。
静的シーン

継続的にアニメーションしないシーン(ダッシュボード、コンフィギュレーター)では、frameloop="demand" と invalidate() を使用します。これにより GPU の処理サイクルを節約できます。

ポータル
import { createPortal } from '@react-three/fiber'

createPortal(children, targetScene)

メインのシーングラフに影響を与えずに、子要素を別のシーン/レイヤーにレンダリングします。

命令型オブジェクトでの useMemo

命令型で作成したジオメトリとマテリアルは、必ず useMemo でラップしてください。

const geometry = useMemo(() => new THREE.TorusKnotGeometry(1, 0.3, 128, 32), [])

useMemo を使わないと、レンダリングのたびに新しいオブジェクトが作成されます。


リファレンスリンク

公式ソース

コメント