1仕組みと動作原理
実装原理は3つのAPI規約と1つのVRAM回収規約に基づきます:フレームレート非依存(dtを掛ける)、素のrAFではなくsetAnimationLoopを使用、そしてGPUリソースのライフサイクルを第一級市民として扱うことです。
Use `clock.getDelta()` to make motion frame-rate independent (multiply rates by `dt`), and `setAnimationLoop` (works with WebXR and pauses on tab blur) instead of raw `requestAnimationFrame`.
GLTFの再生はAnimationMixerを使用し、要点は「deltaでmixerを更新すること」です。クロスフェードにはcrossFadeTo(線形ウェイトの場合はwarping=false)を用い、単発アクションにはLoopOnce + clampWhenFinished + finishedイベントを使用します。
Load a model, play clips through an `AnimationMixer`, and **update the mixer with delta time** every frame.
カメラの動きには固定係数ではなく指数減衰補間lerp(goal, 1 - exp(-lambda*dt))を使用することで、任意のフレームレートで均一な減衰感覚を保ちます。スクロールは目標位置の変更のみにとどめて減衰で平滑化し、曲線飛行はCatmullRomCurve3 + getPointAtで行います。
Smooth camera follow uses exponential interpolation toward a target. Frame-rate-correct damping:
大量のオブジェクトはInstancedMeshを使って1回のdraw callにまとめ、インスタンスごとのマトリクスをsetMatrixAtで設定します。フレーム毎の更新にはDynamicDrawUsageへの切り替えが必要であり、カラー変更はsetColorAtとneedsUpdateの設定で行います。
One draw call for thousands of identical meshes. Set per-instance matrices (and optional colors).
R3FブランチはuseFrame(state, delta)で駆動し、DPRの上限を<Canvas dpr={[1, 2]}>に設定します。モデルやアニメーションはuseGLTF、useAnimationsに委ね、スプリングやスクロールは必要に応じて@react-spring/three、drei ScrollControlsを導入します。
Drive animation with `useFrame`; it receives `state` and `delta`.
VRAMの破棄は本ドキュメントの中核です。通常のGCではGPUのbuffer/textureを解放できない仕組みを解説し、「何をdisposeすべきか、何が不要か」の判断基準を与え、マテリアル配列やすべてのテクスチャスロットを含む完全な走査処理を提供します。
GPU resources (geometries, materials, textures, render targets) live on the *GPU*, not the JS heap. The garbage collector frees the JS objects but **cannot free the WebGL buffers/textures they uploaded**.
R3Fにおけるリーク境界を明確に区別します。JSXで宣言的に作成されたリソースはアンマウント時に自動disposeされますが、命令型(useMemo/ref/loader内でnewされたもの)は自動解放されないためuseEffectのクリーンアップで手動disposeする必要があります。これをrenderer.info.memoryによる10回のマウント/アンマウントベースライン回帰で検証します。
Verify with `renderer.info.memory` (R3F: `useThree().gl.info.memory`): mount/unmount 10x; if `geometries`/`textures` does not return to baseline, something leaked.
2コア機能
3外部依存関係
| Kind | Dependency / Endpoint |
|---|---|
| cli | npx playwright screenshot(验证回路:截 file:// 页面冻结帧) |
| cli | 仓库根共享脚本 scripts/seek-shot.sh、scripts/contact-sheet.sh(不在 skill 目录内) |
| cli | ffmpeg / ffprobe(contact-sheet.sh / probe-mp4.sh) |
| package | three + three/addons(GLTFLoader 等),产物期经 importmap 从 CDN 取 |
| package | React Three Fiber 生态(@react-three/fiber、drei 的 useGLTF/ScrollControls、@react-spring/three,以及 postprocessing 的 UnrealBloom / DepthOfField) |
4推奨統制措置と判定根拠
- scripts/: ローカルスクリプトまたはコマンドの実行: (SKILL.mdの指示)npx playwright screenshot --wait-for-timeout=600 "file://$PWD/
- 网络端点: 外部ネットワークアクセスの宣言: skillディレクトリ内にネットワーク呼び出しコードはなし。実行時のネットワーク面は `npx playwright` のツール取得(npmレジストリ + Chromiumバイナリのダウンロード)のみ
- 本地工作目录: ファイルの読み書きと成果物出力: 成果物本体:単一のscene.html(エージェントが出力規約に従って作成)
5二重目の独立検証
- [ok] 「skill 目录内无脚本、无 3D 资产」是否成立 — 文件清单实证:目录内只有 SKILL.md、README.md(0 B) 与两份 markdown reference;无 .glb/.gltf/.hdr/贴图,也无 package.json 或构建配置。引用到的 scripts/*.sh 属仓库根资产。
- [ok] 凭证/环境变量读取是否存在 — 本仓全仓正则 `process.env|API_KEY|TOKEN|SECRET|Authorization` 零命中;credential_reads 为空成立。
- [ok] 是否有更高的触发条件被漏掉(红档特征) — `sandbox|automationcontrolled|no-verify|stealth|bypass|ignoreHTTPSErrors|dangerously` 全仓零命中;无任意代码执行面(交付物在浏览器沙箱内运行用户自己的场景代码)。
- [ok] 「显存 GC 释放不了」的解释与处置是否自洽 — 文档先讲机制(GC 释放 JS 对象但释放不了上传的 WebGL buffer/texture),再给「该 dispose / 不用 dispose」清单,再给 disposeObject 遍历(含材质数组与 for…in 贴图槽),三者互证;scene.remove 的误解也单独点破:'it frees **nothing** on the GPU'。
- [ok] R3F 自动 dispose 的边界声明是否有可验证手段 — 声明式自动 dispose vs imperative 泄漏的分界写得很具体,并要求 renderer.info.memory 挂载/卸载 10 次回归;references/resource-disposal.md 另附完整泄漏案例与 <DisposeOnUnmount> 方案,属可执行而非口号。
- [ok] 冻结 harness 与 mixer 的配合是否写对(易错点) — harness 段用 `mixer.setTime(elapsed)` 并注释 'setTime is absolute, not delta',与循环里 `mixer.update(dt)` 的增量语义区分开,说明作者意识到差异,声明可信。
6結論
de5e7ad51ab285c7…