gpt4 book ai didi

opengl - 相对于指定空间正确转换节点?

转载 作者:行者123 更新时间:2023-12-03 12:57:38 26 4
gpt4 key购买 nike

我目前正在使用分层场景图中的节点,并且在相对于特定转换空间(例如父节点)正确平移/旋转节点方面遇到困难。

如何在场景图中相对于父节点正确地转换/旋转节点?

问题

考虑以下场景节点的父/子结构的水分子图(无连接线),其中 O xygen原子为父节点,2个 H ydrogen原子为子节点。

water molecule representing parent/children node relationship

翻译问题

如果捕获父代 O 木质素原子并平移结构,则您希望 H 氢属 child 跟随并保持与父代的相对位置相同。如果您改为抓取一个子 H 原子并进行翻译,则只会影响该子对象。通常这是当前的工作方式。翻译 O 原子后, H 原子会自动移动,如层次图所示。

但是,在平移 parent 时, child 最终还会积累额外的翻译,这实际上导致 child 在相同方向上“平移两次”并远离 parent ,而不是保持相同的相对距离。

轮换问题

如果捕获父节点 O 节点并对其进行旋转,则您希望子节点 H 节点也旋转,但在轨道上旋转,因为旋转是由父节点执行的。这按预期工作。

但是,如果您捕获一个子 H 节点并告诉它相对于其父节点旋转,我希望只有该子节点最终会以相同的方式围绕其父节点旋转,但这不会发生。取而代之的是, child 在其当前位置以更快的速度(例如,相对于其自身局部空间的旋转速度的两倍)在自己的轴上旋转。

我真的希望这个描述足够公平,但是请让我知道是否正确,我将根据需要进行澄清。

数学

我正在使用4x4列主矩阵(即Matrix4)和列 vector (即Vector3Vector4)。

下面的错误逻辑是我最接近正确行为的逻辑。请注意,我选择使用类似Java的语法,并使用运算符重载来简化此处的数学运算。当我以为自己想出了办法时,曾尝试过不同的方法,但实际上没有。

当前翻译逻辑

translate(Vector3 tv /* translation vector */, TransformSpace relativeTo):
switch (relativeTo):
case LOCAL:
localTranslation = localTranslation * TranslationMatrix4(tv);
break;
case PARENT:
if parentNode != null:
localTranslation = parentNode.worldTranslation * localTranslation * TranslationMatrix4(tv);
else:
localTranslation = localTranslation * TranslationMatrix4(tv);
break;
case WORLD:
localTranslation = localTranslation * TranslationMatrix4(tv);
break;

电流旋转逻辑
rotate(Angle angle, Vector3 axis, TransformSpace relativeTo):
switch (relativeTo):
case LOCAL:
localRotation = localRotation * RotationMatrix4(angle, axis);
break;
case PARENT:
if parentNode != null:
localRotation = parentNode.worldRotation * localRotation * RotationMatrix4(angle, axis);
else:
localRotation = localRotation * RotationMatrix4(angle, axis);
break;
case WORLD:
localRotation = localRotation * RotationMatrix4(angle, axis);
break;

计算世界空间变换

为了完整起见, this节点的世界变换计算如下:
if parentNode != null:
worldTranslation = parent.worldTranslation * localTranslation;
worldRotation = parent.worldRotation * localRotation;
worldScale = parent.worldScale * localScale;
else:
worldTranslation = localTranslation;
worldRotation = localRotation;
worldScale = localScale;

此外,Node对 this的完整/累积转换是:
Matrix4 fullTransform():
Matrix4 localXform = worldTranslation * worldRotation * worldScale;

if parentNode != null:
return parent.fullTransform * localXform;

return localXform;

当请求将节点的转换发送到OpenGL着色器统一时,将使用 fullTransform矩阵。

最佳答案

worldTranslation = parentNode.worldTranslation * localTranslation;
worldRotation = parentNode.worldRotation * localRotation;
worldScale = parentNode.worldScale * localScale;


这不是连续转换累积的原理。很明显,为什么不考虑一下。

假设您有两个节点:父节点和子节点。父对象绕Z轴逆时针旋转90度。子项在X轴上的偏移为+5。好吧,逆时针旋转应该使它在Y轴上具有+5,是的(假设是右手坐标系)?

但事实并非如此。您的 localTranslation永远不会受到任何形式的轮换的影响。

所有转换都是如此。平移仅受平移影响,不受缩放或旋转影响。旋转不受翻译的影响。等等。

这就是您的代码要执行的操作,而不是您应该执行的操作。

保持矩阵的成分分解是一个好主意。也就是说,具有单独的平移,旋转和缩放(TRS)组件是一个好主意。这样可以更容易地以适当的顺序应用连续的局部变换。

现在,将组件保持为矩阵是错误的,因为这实际上没有任何意义,并且没有真正的原因在浪费时间和空间。翻译只是一个 vec3,与它一起存储其他13个组件没有任何好处。当您在本地累积翻译时,只需添加它们即可。

但是,当您需要累积节点的最终矩阵时,需要将每个TRS分解转换为它自己的局部矩阵,然后将其转换为父级的整体转换,而不是父级的单个TRS组件。也就是说,您需要在本地组成单独的转换,然后将它们与父转换矩阵相乘。用伪代码:
function AccumRotation(parentTM)
local localMatrix = TranslationMat(localTranslation) * RotationMat(localRotation) * ScaleMat(localScale)
local fullMatrix = parentTM * localMatrix

for each child
child.AccumRotation(fullMatrix)
end
end

每个 parent 将自己的累积轮换传递给 child 。根节点被赋予一个单位矩阵。

现在,TRS分解非常好,但是仅在处理局部变换时有效。也就是说,相对于父级的转换。如果要在对象的局部空间中旋转对象,则将四元数应用于其方向。

但是,在非本地空间执行转换完全是另一回事。例如,如果您想将世界空间的平移应用于已对其应用了一些任意系列转换的对象,这是一项艰巨的任务。好吧,实际上,这是一个容易的任务:计算对象的世界空间矩阵,然后在其左侧应用转换矩阵,然后使用父级世界空间矩阵的逆矩阵来计算与父级的相对转换。
function TranslateWorld(transVec)
local parentMat = this->parent ? this->parent.ComputeTransform() : IdentityMatrix
local localMat = this->ComputeLocalTransform()
local offsetMat = TranslationMat(localTranslation)
local myMat = parentMat.Inverse() * offsetMat * parentMat * localMat
end

P-1OP事物的含义实际上是一种常见的构造。这意味着将一般转换 O转换为 P的空间。因此,它将世界偏移量转换为父级矩阵的空间。然后,我们将其应用于本地转换。
myMat现在包含一个转换矩阵,当与父级的转换相乘时,它将像在世界空间中一样应用 transVec。那就是你想要的。

问题在于 myMat是一个矩阵,而不是TRS分解。您如何回到TRS分解?好吧……这需要非常简单的矩阵数学运算。它需要执行名为 Singular Value Decomposition的操作。即使执行了丑陋的数学运算,SVD也会失败。可能有一个不可分解的矩阵。

在我编写的场景图系统中,我创建了一个特殊的类,该类实际上是TRS分解与其表示的矩阵之间的联合。您可以查询它是否已分解,以及是否可以修改TRS组件。但是,一旦您尝试直接为其分配4x4矩阵值,它就会变成一个组合矩阵,您将无法再应用局部分解的变换。我什至从未尝试实现SVD。

哦,您可以在其中累积矩阵。但是,连续累积任意变换将不会产生与分解后的组件修改相同的结果。如果要影响旋转而又不影响先前的翻译,则只有在类处于分解状态时才可以这样做。

无论如何,您的代码都有一些正确的想法,但也有一些非常错误的想法。您需要确定进行TRS分解的重要性与能够应用非局部变换的重要性。

关于opengl - 相对于指定空间正确转换节点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37942389/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com