一键导入
3d-camera-interaction
Three.js 中处理 3D 模型拖拽、缩放、边界检测的正确方法。解决鼠标移动与模型移动不同步、缩放后只能看到模型一部分等问题。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Three.js 中处理 3D 模型拖拽、缩放、边界检测的正确方法。解决鼠标移动与模型移动不同步、缩放后只能看到模型一部分等问题。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Work on N.E.K.O plugins end to end. Use when the user asks to create, modify, review, run, debug, inspect, document, validate, package, or reason about N.E.K.O plugins, plugin.toml, plugin SDK usage, plugin UI surfaces, plugin CLI tooling, plugin runtime behavior, or plugin system contracts.
i18n (internationalization) toolkit for projects using i18next. Provides three main functions: (1) i18n-check - Detect hardcoded Chinese text in HTML/JS files, (2) i18n-fix - Replace hardcoded text with i18n markers, (3) i18n-sync - Align translation keys across multiple languages (zh-CN, zh-TW, en, ja, ko, ru, es, pt). Use when working on internationalization tasks, detecting untranslated strings, or syncing locale files.
Gemini 模型通过 OpenAI 兼容 API 接入指南。包含:(1) 辅助 API 配置(summary/correction/emotion/vision),(2) extra_body 格式用于控制 thinking,(3) 响应格式处理(markdown 代码块)。当需要将 Gemini 作为辅助模型接入、配置 thinking 参数、或处理 Gemini API 返回格式时使用。
应对 WebGL 端 MMD 物理 (Ammo.js) 炸模、穿模、卡顿等极限边缘工况的终极调优指南与 Hack 技巧。
When masking httpx.AsyncClient with unittest.mock in Pytest, AsyncMock must be used instead of MagicMock for async methods like post/get to prevent TypeError when awaited.
Best practices for extracting data from modern React/Vue SSR pages (like Next.js or Nuxt.js) by targeting hydration state blocks (__NEXT_DATA__, __NUXT__) using regex and `jmespath`, avoiding brittle DOM selector scraping.
| name | 3d-camera-interaction |
| description | Three.js 中处理 3D 模型拖拽、缩放、边界检测的正确方法。解决鼠标移动与模型移动不同步、缩放后只能看到模型一部分等问题。 |
问题: 使用固定的 panSpeed = 0.01 进行平移计算
// ❌ 错误方式
const panSpeed = 0.01;
newPosition.add(right.multiplyScalar(deltaX * panSpeed));
为什么发生: 相机距离变化时,同样的世界空间距离在屏幕上的像素表现不同。距离近时像素多,距离远时像素少。
解决方案: 根据相机距离和 FOV 动态计算像素→世界空间的映射
// ✅ 正确方式:动态计算
const cameraDistance = camera.position.distanceTo(modelCenter);
const fov = camera.fov * (Math.PI / 180);
const screenHeight = renderer.domElement.clientHeight;
const screenWidth = renderer.domElement.clientWidth;
// 在相机距离处,视口的世界空间高度
const worldHeight = 2 * Math.tan(fov / 2) * cameraDistance;
const worldWidth = worldHeight * (screenWidth / screenHeight);
// 每像素对应的世界空间距离
const pixelToWorldX = worldWidth / screenWidth;
const pixelToWorldY = worldHeight / screenHeight;
// 应用:鼠标移动的像素 × 每像素对应的世界空间距离
newPosition.add(right.multiplyScalar(deltaX * pixelToWorldX));
newPosition.add(up.multiplyScalar(-deltaY * pixelToWorldY));
问题: 使用模型中心点的 NDC 坐标判断是否出界
// ❌ 错误方式:限制中心点位置
const ndc = position.clone().project(camera);
if (ndc.y > 0.2) clampedY = 0.2; // 限制顶部
为什么发生: 模型放大后,中心点在屏幕中心,但身体大部分已超出屏幕。限制中心点 = 限制只能看到身体中间部分。
解决方案: 计算模型在屏幕上的可见区域(像素),只在可见区域过小时才校正
// ✅ 正确方式:基于可见像素
const MIN_VISIBLE_PIXELS = 50;
// 1. 计算模型包围盒并投影到屏幕
const box = new THREE.Box3().setFromObject(vrm.scene);
const corners = [/* 8个顶点 */];
let modelMinX = Infinity, modelMaxX = -Infinity;
let modelMinY = Infinity, modelMaxY = -Infinity;
corners.forEach(corner => {
const projected = corner.clone().project(camera);
const screenX = (projected.x * 0.5 + 0.5) * screenWidth;
const screenY = (-projected.y * 0.5 + 0.5) * screenHeight;
// 更新边界...
});
// 2. 计算可见区域
const visibleWidth = Math.max(0, Math.min(screenWidth, modelMaxX) - Math.max(0, modelMinX));
const visibleHeight = Math.max(0, Math.min(screenHeight, modelMaxY) - Math.max(0, modelMinY));
const visiblePixels = visibleWidth * visibleHeight;
// 3. 只在可见区域太小时校正
if (visiblePixels < MIN_VISIBLE_PIXELS) {
// 将模型拉回可见区域
}
worldHeight = 2 × tan(fov/2) × cameraDistance
pixelToWorld = worldHeight / screenHeight
const ndc = worldPos.clone().project(camera);
const screenX = (ndc.x * 0.5 + 0.5) * screenWidth;
const screenY = (-ndc.y * 0.5 + 0.5) * screenHeight; // Y 轴反向