Three.js:旋转后如何计算模型侵入容器的距离以限制位置?
解决旋转后模型超出容器的限制问题
嘿,这个问题我之前帮不少开发者解决过——旋转后模型的碰撞边界不再是轴对齐的,之前针对平移、缩放的AABB(轴对齐包围盒)限制逻辑自然就失效了。我来一步步给你拆解怎么搞定:
1. 先拿到旋转后模型的所有顶点坐标(容器坐标系下)
不管是2D还是3D场景,你需要先把模型的本地顶点(相对于模型自身原点的坐标)经过缩放→旋转→平移的变换,转换成容器坐标系下的实际位置。
举个2D场景的简单变换函数:
// 计算单个本地顶点变换后的容器坐标 function getTransformedVertex(localX, localY, posX, posY, scaleX, scaleY, rotation) { const cosθ = Math.cos(rotation); const sinθ = Math.sin(rotation); // 按缩放→旋转→平移的顺序变换(别搞反,顺序错了结果完全不对) const scaledX = localX * scaleX; const scaledY = localY * scaleY; const rotatedX = scaledX * cosθ - scaledY * sinθ; const rotatedY = scaledX * sinθ + scaledY * cosθ; return { x: rotatedX + posX, y: rotatedY + posY }; }
如果是3D场景,逻辑完全一致,只是用4x4变换矩阵来计算就行,核心是拿到所有顶点在容器里的真实位置。
2. 找出旋转后模型的实际占据范围
遍历所有变换后的顶点,找出它们在各个轴上的极值:
- 2D场景:找出
minX(最左点)、maxX(最右点)、minY(最上点)、maxY(最下点) - 3D场景:多加上
minZ和maxZ
这些极值就代表了旋转后模型在容器里的实际包围范围,替代之前的轴对齐包围盒。
3. 对比容器边界,计算修正偏移量
假设容器的边界是containerLeft、containerRight、containerTop、containerBottom(2D),逐个方向检查是否超出:
- 左边超出:如果
minX < containerLeft,需要把模型向右偏移containerLeft - minX - 右边超出:如果
maxX > containerRight,需要把模型向左偏移maxX - containerRight - 上边超出:如果
minY < containerTop,需要把模型向下偏移containerTop - minY - 下边超出:如果
maxY > containerBottom,需要把模型向上偏移maxY - containerBottom
3D场景同理,六个方向都要做对应的检查和偏移计算。
4. 应用偏移修正模型位置
把计算出来的偏移量直接加到模型的当前位置上就行——因为平移不会改变模型的旋转和缩放状态,所以修正后模型的边界会刚好贴合容器的限制范围。
优化小技巧
- 如果模型顶点数量极多(比如复杂3D网格),遍历所有顶点性能会有影响,这时候可以提前计算模型的方向包围盒(OBB),通过变换矩阵直接推导OBB的顶点,不用遍历所有网格顶点。
- 一定要注意坐标系的原点(比如容器是左上角为原点还是中心为原点),所有计算要统一坐标系,避免出现偏移错误。
- 如果模型是绕非自身中心的点旋转,变换顺序和顶点计算逻辑要做对应调整,别搞混旋转中心。
完整2D示例代码
// 模型本地顶点(以自身中心为原点) const modelLocalVertices = [ {x: -50, y: -50}, {x: 50, y: -50}, {x: 50, y: 50}, {x: -50, y: 50} ]; // 容器边界(左上角(0,0),宽400,高300) const container = { left: 0, right: 400, top: 0, bottom: 300 }; // 模型当前变换参数 let model = { x: 200, y: 150, // 位置 scaleX: 1, scaleY: 1, // 缩放 rotation: Math.PI/4 // 45度旋转 }; // 计算所有变换后的顶点 const transformedVertices = modelLocalVertices.map(v => getTransformedVertex(v.x, v.y, model.x, model.y, model.scaleX, model.scaleY, model.rotation) ); // 找出顶点极值 let minX = Infinity, maxX = -Infinity; let minY = Infinity, maxY = -Infinity; transformedVertices.forEach(v => { minX = Math.min(minX, v.x); maxX = Math.max(maxX, v.x); minY = Math.min(minY, v.y); maxY = Math.max(maxY, v.y); }); // 计算偏移量 let offsetX = 0, offsetY = 0; if (minX < container.left) offsetX = container.left - minX; if (maxX > container.right) offsetX = container.right - maxX; if (minY < container.top) offsetY = container.top - minY; if (maxY > container.bottom) offsetY = container.bottom - maxY; // 修正模型位置 model.x += offsetX; model.y += offsetY;
内容的提问来源于stack exchange,提问作者Oleh Riabychev
相关产品推荐
相关产品推荐

