计算包括旋转在内的框的边界
Calculate bounds of a box including rotation
我正在为使用自定义碰撞器的游戏开发碰撞系统。我使用 this 为碰撞创建边界框。
我遇到了使用右值和前向值创建框的框的问题。否则它工作正常。任何人都知道如何在计算立方体的 8 个点时包括右向量和前向量。
下面是我计算积分的代码:
public static void GetBoundsPointsNoAlloc(BoxRegion bx, Matrix4x4 colliderBox4x4,Vector3[] points, Matrix4x4 mx) {
Transform tr = Utils.FromMatrix4x4(ref DebugCollisionTestSphere.trans,mx);
Vector3 v3Center = bx.center;
Vector3 v3ext = bx.GetExtents();
Vector3 right = bx.right;
Vector3 forw = bx.forward;
//Quaternion qua =Quaternion.LookRotation(bx.forward,Vector3.Cross (bx.forward, bx.right));
//tr.TransformDirection (bx.forward);
//tr.localRotation = qua;
tr.forward = bx.forward;
tr.right = bx.right;
//tr.rotation = qua;
points [0] = tr.TransformPoint (new Vector3 (v3Center.x - v3ext.x, v3Center.y + v3ext.y, v3Center.z - v3ext.z)); // Front top left corner
points [1] = tr.TransformPoint (new Vector3 (v3Center.x + v3ext.x, v3Center.y + v3ext.y, v3Center.z - v3ext.z)); // Front top right corner
points [2] = tr.TransformPoint (new Vector3 (v3Center.x - v3ext.x, v3Center.y - v3ext.y, v3Center.z - v3ext.z)); // Front bottom left corner
points [3] = tr.TransformPoint (new Vector3 (v3Center.x + v3ext.x, v3Center.y - v3ext.y, v3Center.z - v3ext.z)); // Front bottom right corner
points [4] = tr.TransformPoint (new Vector3 (v3Center.x - v3ext.x, v3Center.y + v3ext.y, v3Center.z + v3ext.z)); // Back top left corner
points [5] = tr.TransformPoint (new Vector3 (v3Center.x + v3ext.x, v3Center.y + v3ext.y, v3Center.z + v3ext.z)); // Back top right corner
points [6] = tr.TransformPoint (new Vector3 (v3Center.x - v3ext.x, v3Center.y - v3ext.y, v3Center.z + v3ext.z)); // Back bottom left corner
points [7] = tr.TransformPoint (new Vector3 (v3Center.x + v3ext.x, v3Center.y - v3ext.y, v3Center.z + v3ext.z)); // Back bottom right corner
}
当我应用四元数进行变换时,盒子正在改变它的位置
让我们根据 Unity3D 框架中本机可用的 类 重新定义此问题。如果你对数学理解得足够好,你可以把它应用到你正在使用的其他类。
如果您只需要 axis-aligned bounding box(或 AABB),那么您可以通过任何 Renderer
或 Collider
各自的 bounds
属性轻松获得它。这是一个在许多情况下都足够的快速解决方案。
其他一些边界框在本地 space 中表示——例如,Mesh
有一个 bounds
属性 在本地 space 中。无论出于何种原因,您有时会有一个尺寸向上、向右和向前的盒子,您可能需要将该盒子的点转换为世界 space.
假设我们的盒子看起来像这样:
size.x
.+------+
.' | .'|
+---+--+' | size.y (all around some point 'center')
| | | |
| ,+--+---+
|.' | .' size.z
+------+'
假设我们有一个以零为中心的单位立方体:
Vector3 center = Vector3.zero;
Vector3 size = Vector3.one;
框的范围是其大小的一半:
Vector3 extents = size * 0.5f;
这使得计算框周围的八个点变得足够容易:
Vector3[] points = new Vector3[8];
points[0] = center + new Vector3( extents.x, extents.y, extents.z);
points[1] = center + new Vector3( extents.x, extents.y, -extents.z);
points[2] = center + new Vector3( extents.x, -extents.y, extents.z);
points[3] = center + new Vector3( extents.x, -extents.y, -extents.z);
points[4] = center + new Vector3(-extents.x, extents.y, extents.z);
points[5] = center + new Vector3(-extents.x, extents.y, -extents.z);
points[6] = center + new Vector3(-extents.x, -extents.y, extents.z);
points[7] = center + new Vector3(-extents.x, -extents.y, -extents.z);
这些点都在本地space,还在。我们只需要一个 transform
组件将它们转换成世界 space:
for (int i=0; i<8; i++) {
points[i] = transform.TransformPoint(points[i]);
}
您需要将盒子的方向(存储为右和向前)连接到世界变换。像这样的东西应该可以工作:
Vector3 fwd=bx.forward.normalized(), rt=br.right.normalized(), up=fwd.Cross(rt);
Quaternion qbox; qbox.SetLookRotation(fwd,up);
tr.rotation = tr.rotation*qbox;
(由于 Unity 是讨厌的左撇子,您可能需要 fiddle 加上符号和连接顺序,或者在 SetLookRotation 中使用 rt 而不是 fwd)
更新:经过聊天讨论,这似乎是最终的工作版本。
Quaternion quat = Quaternion.identity;
quat.SetLookRotation(bx.forward, Vector3.Cross(bx.forward,bx.right));
points[0] = mx.MultiplyPoint3x4(quat * new Vector3(-bx.width/2,+bx.height/2,-bx.depth/2)+bx.center);
...
points[7] = mx.MultiplyPoint3x4(quat * new Vector3(+bx.width/2,-bx.height/2,+bx.depth/2)+bx.center);
我正在为使用自定义碰撞器的游戏开发碰撞系统。我使用 this 为碰撞创建边界框。
我遇到了使用右值和前向值创建框的框的问题。否则它工作正常。任何人都知道如何在计算立方体的 8 个点时包括右向量和前向量。
下面是我计算积分的代码:
public static void GetBoundsPointsNoAlloc(BoxRegion bx, Matrix4x4 colliderBox4x4,Vector3[] points, Matrix4x4 mx) {
Transform tr = Utils.FromMatrix4x4(ref DebugCollisionTestSphere.trans,mx);
Vector3 v3Center = bx.center;
Vector3 v3ext = bx.GetExtents();
Vector3 right = bx.right;
Vector3 forw = bx.forward;
//Quaternion qua =Quaternion.LookRotation(bx.forward,Vector3.Cross (bx.forward, bx.right));
//tr.TransformDirection (bx.forward);
//tr.localRotation = qua;
tr.forward = bx.forward;
tr.right = bx.right;
//tr.rotation = qua;
points [0] = tr.TransformPoint (new Vector3 (v3Center.x - v3ext.x, v3Center.y + v3ext.y, v3Center.z - v3ext.z)); // Front top left corner
points [1] = tr.TransformPoint (new Vector3 (v3Center.x + v3ext.x, v3Center.y + v3ext.y, v3Center.z - v3ext.z)); // Front top right corner
points [2] = tr.TransformPoint (new Vector3 (v3Center.x - v3ext.x, v3Center.y - v3ext.y, v3Center.z - v3ext.z)); // Front bottom left corner
points [3] = tr.TransformPoint (new Vector3 (v3Center.x + v3ext.x, v3Center.y - v3ext.y, v3Center.z - v3ext.z)); // Front bottom right corner
points [4] = tr.TransformPoint (new Vector3 (v3Center.x - v3ext.x, v3Center.y + v3ext.y, v3Center.z + v3ext.z)); // Back top left corner
points [5] = tr.TransformPoint (new Vector3 (v3Center.x + v3ext.x, v3Center.y + v3ext.y, v3Center.z + v3ext.z)); // Back top right corner
points [6] = tr.TransformPoint (new Vector3 (v3Center.x - v3ext.x, v3Center.y - v3ext.y, v3Center.z + v3ext.z)); // Back bottom left corner
points [7] = tr.TransformPoint (new Vector3 (v3Center.x + v3ext.x, v3Center.y - v3ext.y, v3Center.z + v3ext.z)); // Back bottom right corner
}
当我应用四元数进行变换时,盒子正在改变它的位置
让我们根据 Unity3D 框架中本机可用的 类 重新定义此问题。如果你对数学理解得足够好,你可以把它应用到你正在使用的其他类。
如果您只需要 axis-aligned bounding box(或 AABB),那么您可以通过任何 Renderer
或 Collider
各自的 bounds
属性轻松获得它。这是一个在许多情况下都足够的快速解决方案。
其他一些边界框在本地 space 中表示——例如,Mesh
有一个 bounds
属性 在本地 space 中。无论出于何种原因,您有时会有一个尺寸向上、向右和向前的盒子,您可能需要将该盒子的点转换为世界 space.
假设我们的盒子看起来像这样:
size.x
.+------+
.' | .'|
+---+--+' | size.y (all around some point 'center')
| | | |
| ,+--+---+
|.' | .' size.z
+------+'
假设我们有一个以零为中心的单位立方体:
Vector3 center = Vector3.zero;
Vector3 size = Vector3.one;
框的范围是其大小的一半:
Vector3 extents = size * 0.5f;
这使得计算框周围的八个点变得足够容易:
Vector3[] points = new Vector3[8];
points[0] = center + new Vector3( extents.x, extents.y, extents.z);
points[1] = center + new Vector3( extents.x, extents.y, -extents.z);
points[2] = center + new Vector3( extents.x, -extents.y, extents.z);
points[3] = center + new Vector3( extents.x, -extents.y, -extents.z);
points[4] = center + new Vector3(-extents.x, extents.y, extents.z);
points[5] = center + new Vector3(-extents.x, extents.y, -extents.z);
points[6] = center + new Vector3(-extents.x, -extents.y, extents.z);
points[7] = center + new Vector3(-extents.x, -extents.y, -extents.z);
这些点都在本地space,还在。我们只需要一个 transform
组件将它们转换成世界 space:
for (int i=0; i<8; i++) {
points[i] = transform.TransformPoint(points[i]);
}
您需要将盒子的方向(存储为右和向前)连接到世界变换。像这样的东西应该可以工作:
Vector3 fwd=bx.forward.normalized(), rt=br.right.normalized(), up=fwd.Cross(rt);
Quaternion qbox; qbox.SetLookRotation(fwd,up);
tr.rotation = tr.rotation*qbox;
(由于 Unity 是讨厌的左撇子,您可能需要 fiddle 加上符号和连接顺序,或者在 SetLookRotation 中使用 rt 而不是 fwd)
更新:经过聊天讨论,这似乎是最终的工作版本。
Quaternion quat = Quaternion.identity;
quat.SetLookRotation(bx.forward, Vector3.Cross(bx.forward,bx.right));
points[0] = mx.MultiplyPoint3x4(quat * new Vector3(-bx.width/2,+bx.height/2,-bx.depth/2)+bx.center);
...
points[7] = mx.MultiplyPoint3x4(quat * new Vector3(+bx.width/2,-bx.height/2,+bx.depth/2)+bx.center);