如何围绕点缩放 Matrix4x4?
How to scale Matrix4x4 around point?
我正在 Unity 中编写自定义编辑器 window,我希望能够在其中滚动 in/out 和拖动视图。为此,我将 GUI.matrix
设置为 Matrix4x4.TRS(offset, Quaternion.identity, Vector3.one * scale)
,我可以控制 offset
和 scale
。这工作正常,除了滚动 in/out 时,它锚定 window 的左上角。 我希望它锚定在鼠标的位置。
如果这只需要在缩放时更改偏移量,那就太好了 - 我只是不确定这里的偏移量应该是多少。 Matrix4x4
s 超出了我的数学舒适区。
这是我目前处理缩放的方式:
if (Event.current.type == EventType.ScrollWheel)
{
_scale *= Math.Sign(Event.current.delta.y) == 1 ? 1.1f : 1f / 1.1f;
_offset += Math.Sign(Event.current.delta.y) * /*What do I put here?*/;
}
让我们尝试了解 GUI 矩阵的作用。它代表一个转换,它在世界 space(您的 GUI 对象所在的位置)中获取坐标并将它们转换为 GUI space(或多或少与您的 window 对齐)。由于我们没有旋转,我们可以很容易地解释用 TRS()
构造矩阵对 world-space 点 pWorld
:
的影响
pGUI = scale * pWorld + offset
现在您想将 scale
更改为 scaleNew
。这样做时,您希望在鼠标下保持相同的世界位置。
如果你的鼠标位置在GUI中给出space(比如来自Event.current.mousePosition
),那么我们首先需要找到对应的世界space点:
v3World = (1.0 / scaleOld) * (v3GUI - offsetOld)
并且我们要在鼠标下修复这个点,即:
v3GUI = scaleNew * v3World + offsetNew
v3GUI = scaleNew / scaleOld * (v3GUI - offsetOld) + offsetNew
我们可以解决这个问题以获得新的偏移量:
v3GUI = scaleNew / scaleOld * v3GUI - scaleNew / scaleOld * offsetOld + offsetNew
(1 - scaleNew / scaleOld) * v3GUI + scaleNew / scaleOld * offsetOld = offsetNew
就是这样。
顺便说一句,您也可以单独使用矩阵运算来完成此操作。这就是 GUIUtility.ScaleAroundPivot()
所做的。这是它的样子:
newMatrix = T(v3GUI) * S(newScale / oldScale) * T(-v3GUI) * oldMatrix
T
表示平移,S
表示缩放。翻译对 T(v3GUI)
和 T(-v3GUI)
将坐标系的临时原点移动到您的鼠标位置并从那里执行缩放。然后您可以直接从该矩阵中读取偏移量和比例。
我正在 Unity 中编写自定义编辑器 window,我希望能够在其中滚动 in/out 和拖动视图。为此,我将 GUI.matrix
设置为 Matrix4x4.TRS(offset, Quaternion.identity, Vector3.one * scale)
,我可以控制 offset
和 scale
。这工作正常,除了滚动 in/out 时,它锚定 window 的左上角。 我希望它锚定在鼠标的位置。
如果这只需要在缩放时更改偏移量,那就太好了 - 我只是不确定这里的偏移量应该是多少。 Matrix4x4
s 超出了我的数学舒适区。
这是我目前处理缩放的方式:
if (Event.current.type == EventType.ScrollWheel)
{
_scale *= Math.Sign(Event.current.delta.y) == 1 ? 1.1f : 1f / 1.1f;
_offset += Math.Sign(Event.current.delta.y) * /*What do I put here?*/;
}
让我们尝试了解 GUI 矩阵的作用。它代表一个转换,它在世界 space(您的 GUI 对象所在的位置)中获取坐标并将它们转换为 GUI space(或多或少与您的 window 对齐)。由于我们没有旋转,我们可以很容易地解释用 TRS()
构造矩阵对 world-space 点 pWorld
:
pGUI = scale * pWorld + offset
现在您想将 scale
更改为 scaleNew
。这样做时,您希望在鼠标下保持相同的世界位置。
如果你的鼠标位置在GUI中给出space(比如来自Event.current.mousePosition
),那么我们首先需要找到对应的世界space点:
v3World = (1.0 / scaleOld) * (v3GUI - offsetOld)
并且我们要在鼠标下修复这个点,即:
v3GUI = scaleNew * v3World + offsetNew
v3GUI = scaleNew / scaleOld * (v3GUI - offsetOld) + offsetNew
我们可以解决这个问题以获得新的偏移量:
v3GUI = scaleNew / scaleOld * v3GUI - scaleNew / scaleOld * offsetOld + offsetNew
(1 - scaleNew / scaleOld) * v3GUI + scaleNew / scaleOld * offsetOld = offsetNew
就是这样。
顺便说一句,您也可以单独使用矩阵运算来完成此操作。这就是 GUIUtility.ScaleAroundPivot()
所做的。这是它的样子:
newMatrix = T(v3GUI) * S(newScale / oldScale) * T(-v3GUI) * oldMatrix
T
表示平移,S
表示缩放。翻译对 T(v3GUI)
和 T(-v3GUI)
将坐标系的临时原点移动到您的鼠标位置并从那里执行缩放。然后您可以直接从该矩阵中读取偏移量和比例。