threejs-r3f について
Three.js と React Three Fiber で 3D コンテンツを作る開発者向けのクリエイティブコーディングスキルです。シーン構築、アセット読み込み、アニメーション、パフォーマンス最適化を扱い、React の有無に応じた選択も示します。
主な機能
- 01Three.js と R3F のシーン構築
- 02Drei コンポーネントとアセット読み込み
- 03アニメーションループとフックの使い方
- 04モデルとテクスチャの最適化
- 05GPU リソースの解放方法
活用シーン
- 01React プロジェクトでの 3D シーン構築
- 02GLTF モデルの読み込みと表示
- 03静的または大規模な 3D シーンの最適化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ athevon/ genjutsu --skill threejs-r3fインストール後、Claude Code で/threejs-r3fを入力して読み込まれたか確認します。
詳細
バージョン依存の情報です。 以下のすべての API 名、SDK の要件、ブラウザー対応に関する記述は、 2026-09-08 に一次資料に基づいて検証されています。何をいつ検証したかは
_jutsu/VERSIONS.mdを参照してください。この日付が古い場合は、バージョン番号に基づいて作業する前に再確認してください。
Three.js と React Three Fiber
Web 上の 3D。Three.js はエンジン、R3F は React のレンダラーです。 ここでは簡潔なルールを紹介します。詳細は
references/を参照してください。バージョン:
threer185(0.185.x)・@react-three/fiber9.x・@react-three/drei10.x・@react-three/postprocessing3.x。 R3F 9 は React 19 専用です。宣言されているピア依存関係の範囲はreact/react-dom>=19 <19.3です(drei 10 は^19を要求します)。React 18 のプロジェクトでは、R3F 8 と drei 9 を使用してください。足場を作る前に、インストール済みの React のメジャーバージョンを確認してください。 レンダラー:WebGLRendererは引き続き R3F のデフォルトです。WebGPURendererと TSL は正式にリリースされており(フラグなしで利用可能)、WebGL 2 に自動フォールバックします。ただし、Three.js のドキュメントでは「WebGLRendererの新しい代替」とされており、デフォルトではありません。非同期のglコールバックでオプトインします:import * as THREE from 'three/webgpu' <Canvas gl={async (props) => { const r = new THREE.WebGPURenderer(props as any); await r.init(); return r }} />
用途に応じた選択
| 必要なもの | ツール | 理由 |
|---|---|---|
| フル 3D シーン(モデル、ライト、物理演算) | R3F + drei | 宣言的で、React と親和性が高く、エコシステムも充実 |
| バニラな 3D(React なし) | Three.js を直接使用 | 軽量で、React のオーバーヘッドがありません |
| UI 上のシンプルな 3D 変換 | CSS transform3d | GPU で合成され、WebGL コンテキストが不要です |
| 2D パーティクル/ジェネレーティブ表現 | Canvas 2D | API がシンプルで、GPU のオーバーヘッドも小さくなります |
| シーングラフを使わないシェーダーのみのビジュアル | Raw WebGL / ShaderMaterial | 最大限の制御が可能で、抽象化を最小限に抑えられます |
シーンのセットアップパターン
import { Canvas } from '@react-three/fiber'
import { Environment, OrbitControls } from '@react-three/drei'
import { Suspense } from 'react'
<Canvas camera={{ position: [0, 2, 5], fov: 45 }} dpr={[1, 2]} gl={{ antialias: true }}>
<Suspense fallback={null}>
<Environment preset="studio" />
<OrbitControls makeDefault />
<Scene />
</Suspense>
</Canvas>
ルール:
- シーンのコンテンツは必ず
<Suspense>でラップしてください。ローダー(GLTF、テクスチャ、HDRI)で必要になります - ピクセル比を制限するため、
dpr={[1, 2]}を設定してください(GPU に過度な負荷をかけずに Retina に対応できます) - Canvas の親コンポーネントは最小限にしてください。再レンダリングがシーンに伝播します
R3F フック
| フック | 目的 | 注意点 |
|---|---|---|
useFrame((state, delta) => {}) | フレームごとの処理(アニメーション、物理演算) | 内部で setState を呼び出さないでください |
useThree() | gl、scene、camera、size、viewport、pointer へのアクセス | 必要なものだけを分割代入してください |
useLoader(TextureLoader, url) | 任意の Three.js リソースを読み込む | 親を Suspense でラップしてください |
useGraph(scene) | 読み込んだシーンからノード/マテリアルを抽出 | useGLTF の後に便利です |
useFrame のヒント
useFrame((state, delta) => {
// Use delta for framerate-independent animation
meshRef.current.rotation.y += delta * 0.5
// Access clock for time-based effects
material.uniforms.uTime.value = state.clock.elapsedTime
})
Drei の基本コンポーネント
| コンポーネント | 用途 |
|---|---|
Environment | HDRI ライティング(プリセット:studio、sunset、city、forest、dawn) |
Float | アイドル時の浮遊アニメーション(speed、rotationIntensity、floatIntensity) |
Text3D | 押し出し加工された 3D テキスト(Facetype.js の JSON フォントが必要です) |
useGLTF | .glb/.gltf モデルを読み込む({ nodes, materials, scene } を返します) |
useGLTF.preload(url) | コンポーネントのマウント前にモデルをプリロード |
MeshTransmissionMaterial | ガラス/水晶/液体の屈折効果 |
PresentationControls | 製品紹介でドラッグして回転 |
Center | メッシュのグループを自動的に中央揃え |
Detailed | LOD — カメラとの距離に応じてジオメトリを切り替え |
useTexture | Suspense に対応したテクスチャの読み込み |
Instances | 繰り返し使うメッシュを宣言的にインスタンス化 |
ポストプロセッシング
import { EffectComposer, Bloom, ChromaticAberration } from '@react-three/postprocessing'
import { BlendFunction } from 'postprocessing'
<EffectComposer>
<Bloom
luminanceThreshold={1}
luminanceSmoothing={0.4}
intensity={0.6}
/>
<ChromaticAberration
blendFunction={BlendFunction.NORMAL}
offset={[0.002, 0.002]}
/>
</EffectComposer>
ルール:
- Bloom はデフォルトで選択的に適用されます。発光させるには、マテリアルの色/emissive を 1.0 より大きくしてください
luminanceThreshold={1}を設定すると、明示的に発光させない限り何も光りません- EffectComposer 内の順序が重要です
- エフェクトは単一のパスに統合されます(パフォーマンスを考慮した設計です)
パフォーマンスパターン
| パターン | 使用場面 |
|---|---|
<Instances> / InstancedMesh | 同一メッシュが100個以上ある場合(パーティクル、木々、群衆) |
<Detailed distances={[0, 50, 100]}> | LOD:距離に応じて高/低詳細モデルを切り替えます |
<primitive> の dispose={null} | 共有ジオメトリの再利用時に自動破棄を防ぎます |
useGLTF + Draco | .glb モデルを圧縮します(サイズを70~90%削減) |
useTexture + KTX2 | GPU テクスチャを圧縮します(VRAM を1/4に削減) |
Canvas の frameloop="demand" | 変更がある場合にのみレンダリングします(静的なシーン向け) |
useThree の invalidate() | demand モードでレンダリングを開始します |
<AdaptiveDpr pixelated /> + <AdaptiveEvents />(drei) | カメラの移動中は解像度とレイキャストの精度を下げ、停止後に元に戻します |
<Bvh>(drei) | 高速なレイキャスト/ホバー選択が必要な高負荷シーン向け |
目標指標: ドローコール100未満、三角形数100万未満、ミドルレンジ GPU で60fps。
監視には stats-gl または r3f-perf を使用します。
禁止事項
1. useFrame 内で絶対に setState しない
毎秒60回の React 全体の再レンダリングが発生します。ref を直接変更してください。
// BAD
useFrame(() => {
setRotation(prev => prev + 0.01) // React re-render every frame
})
// GOOD
useFrame((_, delta) => {
meshRef.current.rotation.y += delta * 0.5 // Direct mutation, zero re-renders
})
2. レンダーループ内で絶対にメモリを確保しない
フレームごとに new Vector3() を呼び出すと、GC の急増によってカクつきます。
// BAD
useFrame((state) => {
const target = new THREE.Vector3(0, Math.sin(state.clock.elapsedTime), 0)
meshRef.current.position.copy(target)
})
// GOOD
const _target = useMemo(() => new THREE.Vector3(), [])
useFrame((state) => {
_target.set(0, Math.sin(state.clock.elapsedTime), 0)
meshRef.current.position.copy(_target)
})
3. dispose を絶対に忘れない(メモリリーク)
Three.js のテクスチャ、ジオメトリ、マテリアルは GPU 上に保持されます。React コンポーネントをアンマウントしても、これらは解放されません。
// BAD -- texture stays in VRAM after unmount
const texture = useLoader(TextureLoader, '/big-texture.jpg')
// GOOD -- R3F auto-disposes when using JSX primitives
// For manual resources, dispose in cleanup:
useEffect(() => {
return () => {
texture.dispose()
geometry.dispose()
material.dispose()
}
}, [])
4. Canvas の親コンポーネントを絶対に再レンダリングしない
親コンポーネントの状態が変化すると、Canvas 全体が再マウントされ、画面のちらつき、状態の消失、アセットの再読み込みが発生します。
// BAD
function App() {
const [uiState, setUiState] = useState(false) // re-renders remount Canvas
return (
<>
<button onClick={() => setUiState(!uiState)}>Toggle</button>
<Canvas><Scene config={uiState} /></Canvas>
</>
)
}
// GOOD -- isolate Canvas in its own component
function App() {
return (
<>
<UI />
<SceneCanvas />
</>
)
}
5. Suspense なしで絶対にアセットを読み込まない
ローダー(useGLTF、useTexture、useLoader)は Promise をスローします。Suspense がないと、クラッシュが発生します。
// BAD
<Canvas>
<Model /> {/* useGLTF inside -- will throw */}
</Canvas>
// GOOD
<Canvas>
<Suspense fallback={<Loader />}>
<Model />
</Suspense>
</Canvas>
クイックリファレンス:サブリソースの読み込み
| 必要な情報 | 参照先 |
|---|---|
| シーンの基本構成、照明セットアップ、コントロール | references/scene-setup.md |
| カスタムシェーダー、GLSL パターン、uniform | references/shaders.md |
| アニメーションの原則、イージング、タイミング | ../motion-principles/SKILL.md |
| GSAP と Three.js の連携 | ../gsap/SKILL.md |