c# - 如何在 Unity3D 中计算两帧(轴向系统)之间的(正确)变换矩阵
问题描述
对于 Unity3D 中的一个项目,我正在尝试通过更改帧来转换世界上的所有对象。这意味着新框架的原点被旋转、平移和缩放以匹配旧框架的原点,然后此操作应用于所有其他对象(包括旧原点)。为此,我需要一个通用的 3 维(因此是 4x4)变换矩阵。
我看过使用 Unity 的内置 Matrix4x4.TRS() 方法,但这似乎没用,因为它只将平移、旋转和缩放应用于定义的点。我正在寻找的是框架的变化,其中新框架相对于原始框架具有不同的原点、旋转和比例。
为了可视化这个问题,我制作了一个小 GIF(我目前有一个 3D 工作版本,没有使用矩阵,也没有任何旋转): https ://gyazo.com/8a7ab04dfef2c96f53015084eefbdb01
每个球体的值:
Origin1 (Red Sphere)
Before:
Position (-10, 0, 0)
Rotation (0,0,0)
Scale (4,4,4)
After:
Position (10, 0, 0)
Rotation (0,0,0)
Scale (8,8,8)
-
Origin2 (Blue Sphere)
Before:
Position (-20, 0, 0)
Rotation (0,0,0)
Scale (2,2,2)
After:
Position (-10, 0, 0)
Rotation (0,0,0)
Scale (4,4,4)
-
World-Object (White Sphere)
Before:
Position (0, 0, 10)
Rotation (0,0,0)
Scale (2,2,2)
After:
Position (30, 0, 20)
Rotation (0,0,0)
Scale (4,4,4)
目前,我只是在两个原点之间获取向量,将其缩放到两个原点之间的差异,然后将其应用到原始(第一个)原点的新位置之上。当旋转应用于 2 个原点中的任何一个时,这当然不起作用。
// Position in original axes
Vector3 positionBefore = testPosition.TestPosition - origin.TestPosition;
// Position in new axes
Vector3 positionAfter = (positionBefore * scaleFactor) + origin.transform.position;
我正在寻找的是一个可以做到这一点的矩阵(并包括旋转,使得 Origin2 旋转到 Origin1 在转换之前的旋转,并且所有其他对象都移动到它们的正确位置)。
有没有办法在不对每个向量进行完整计算的情况下做到这一点(即转换向量前的位置)?它需要每帧应用于(非常)大量对象,因此需要(相当)优化。
编辑:缩放总是统一的。
解决方案
可能还有其他解决方案,但这是我会做的
将您的对象包装到以下层次结构中
WorldAnchorObject |- Red Sphere |- Blue Sphere |- White Sphere
确保
WorldAnchorObject
有position: 0,0,0 rotation: 0,0,0 localScale: 1,1,1
定位/旋转/缩放球体(现在将相对于
WorldAnchorObject
)现在剩下的就是变换
WorldAnchorObject
-> 它将移动、缩放和旋转其他任何东西,并保持相对变换不变。你如何移动世界锚是你的事。我猜您想始终将某个子对象居中并规范化。也许像
public void CenterOnObject(GameObject targetCenter) { var targetTransform = targetCenter.transform; // The localPosition and localRotation are relative to the parent var targetPos = transform.localPosition; var targetRot = targetTransform.localRotation; var targetScale = targetTransform.localScale; // First reset everything transform.position = Vector3.zero; transform.rotation = Quaternion.Idendity; transform.localScale = Vector3.one; // set yourself to inverted target position // After this action the target should be on 0,0,0 transform.position = targetPos * -1; // Scale yourself relative to 0,0,0 var newScale = Vector3.one * 1/targetScale.x; ScaleAround(gameObject, Vector3.zero, newScale); // Now everything should be scaled so that the target // has scale 1,1,1 and still is on position 0,0,0 // Now rotate around 0,0,0 so that the rotation of the target gets normalized transform.rotation = Quaternion.Inverse(targetRotation); } // This scales an object around a certain pivot and // takes care of the correct position translation as well private void ScaleAround(GameObject target, Vector3 pivot, Vector3 newScale) { Vector3 A = target.transform.localPosition; Vector3 B = pivot; Vector3 C = A - B; // diff from object pivot to desired pivot/origin float RS = newScale.x / target.transform.localScale.x; // relative scale factor // calc final position post-scale Vector3 FP = B + C * RS; // finally, actually perform the scale/translation target.transform.localScale = newScale; target.transform.localPosition = FP; }
现在你称它为传递其中一个孩子,例如
worldAnchorReference.CenterOnObject(RedSphere);
应该会导致您想要实现的目标。(在我的智能手机上破解这个,所以没有保证,但如果有问题,我可以在我再次使用 PC 时立即检查它。;))
推荐阅读
- c# - 如何在“真实”方法中使用参数调用模拟方法?
- powershell - StaffHub Powershell Cmdlet - 团队 ID 必须具有 Team_guid 模式
- windows - 批处理脚本在 Windows 10 Pro 上运行,但不在 Surface Pro 4 上运行
- c++ - 浮点减法给了我不准确的结果
- cuda - CUDA 对数:nvprof 以双精度检测单精度操作
- python - 如何在 Django 中从 HTML 标记按钮在后台触发 Python 脚本?
- c - 如何在C中同时进行输入和输出?
- c - 将字符包读入数组然后解析
- common-lisp - 将包名称添加到符号
- php - Fuel\Core\PhpErrorException [注意]:未定义变量:配置mysql db时出错