如何查找绕原点旋转的3D网格中任意点的最近格点(Unity C#适用)
通用求解思路
这个问题的核心解法是坐标空间转换,不需要在世界空间做复杂的几何计算,具体步骤如下:
- 首先明确网格的变换参数:本题中网格仅绕原点旋转,我们只需要存储网格的旋转矩阵/四元数
R即可。 - 逆变换到网格局部空间:将世界空间下的待查询点
P乘R的逆矩阵,得到网格局部坐标系下的坐标P_local。由于旋转矩阵是正交矩阵,逆矩阵等于转置矩阵,计算开销极低。 - 局部空间找最近格点:网格在自身局部空间是对齐坐标轴、步长为1的规则网格,因此直接对
P_local的三个分量分别四舍五入,就能得到局部空间下的最近格点G_local。 - 正变换回世界空间:将
G_local乘原始旋转矩阵R,得到的结果就是世界空间下的最近格点坐标。
如果你的网格后续需要支持偏移、缩放,只需要在变换步骤中对应加减偏移、乘除缩放系数即可,逻辑完全通用。
Unity C# 实现
我们可以把逻辑封装为可复用的工具类,预计算逆旋转避免重复计算,单次查询时间复杂度为O(1),适合运行时高频调用:
using UnityEngine; public class RotatedGridSnapper { private Quaternion _gridRotation; private Quaternion _inverseGridRotation; /// <summary> /// 构造旋转网格吸附工具 /// </summary> /// <param name="gridRotation">网格的世界空间旋转</param> public RotatedGridSnapper(Quaternion gridRotation) { UpdateGridRotation(gridRotation); } /// <summary> /// 运行时更新网格的旋转参数 /// </summary> public void UpdateGridRotation(Quaternion newRotation) { _gridRotation = newRotation; _inverseGridRotation = Quaternion.Inverse(newRotation); } /// <summary> /// 查询世界空间点对应的最近格点坐标 /// </summary> /// <param name="worldPoint">待查询的世界空间点</param> /// <param name="gridStep">网格步长,默认1</param> /// <param name="gridOffset">网格原点的世界空间偏移,默认无偏移</param> /// <returns>世界空间下的最近格点坐标</returns> public Vector3 GetNearestGridPoint(Vector3 worldPoint, float gridStep = 1f, Vector3 gridOffset = default) { // 抵消网格全局偏移 Vector3 offsetPoint = worldPoint - gridOffset; // 转换到网格局部空间 Vector3 localPoint = _inverseGridRotation * offsetPoint; // 局部空间按步长吸附 Vector3 localGridPoint = new Vector3( Mathf.Round(localPoint.x / gridStep) * gridStep, Mathf.Round(localPoint.y / gridStep) * gridStep, Mathf.Round(localPoint.z / gridStep) * gridStep ); // 转换回世界空间并恢复偏移 return _gridRotation * localGridPoint + gridOffset; } }
使用示例
// 初始化:网格绕Y轴旋转45度,步长1,原点无偏移 Quaternion gridRotation = Quaternion.Euler(0, 45f, 0); RotatedGridSnapper snapper = new RotatedGridSnapper(gridRotation); // 查询示例 Vector3 testPoint = new Vector3(2.1f, 0.3f, 1.7f); Vector3 nearestPoint = snapper.GetNearestGridPoint(testPoint); Debug.Log($"最近格点坐标:{nearestPoint}");
注意事项
- 默认使用Unity的
Mathf.Round实现四舍五入,采用银行家舍入规则(即中间值舍入到最近的偶数),如果需要自定义舍入规则,替换Mathf.Round为自定义实现即可。 - 如果网格有非均匀缩放,只需要把缩放参数也存入工具类,逆变换时对应除以各轴缩放系数,正变换时乘回缩放系数即可。
内容的提问来源于stack exchange,提问作者starbi
相关产品推荐
相关产品推荐

