需要OpenGL 3D变换的旋转矩阵。

3

问题是我有两个在三维空间中的点,其中y+向上,x+向右,z+朝向你。我想将一个长度等于两点之间距离的圆柱体定位在它们之间,使其两个中心端点接触这两个点。我已经将圆柱体平移到了两个点的中心位置,并需要帮助设计一个旋转矩阵来应用于圆柱体,以便它朝向正确的方向。整个事物的变换矩阵如下:

translate(中心点) * rotateX(一些X度数) * rotateZ(一些Z度数)

平移是最后应用的,这样我就可以在平移之前将其定位到正确的方向。

以下是我目前为止的进展:

mat4 getTransformation(vec3 point, vec3 parent)
{
    float deltaX = point.x - parent.x;
    float deltaY = point.y - parent.y;
    float deltaZ = point.z - parent.z;

    float yRotation = atan2f(deltaZ, deltaX) * (180.0 / M_PI);
    float xRotation = atan2f(deltaZ, deltaY) * (180.0 / M_PI);
    float zRotation = atan2f(deltaX, deltaY) * (-180.0 / M_PI);
    if(point.y < parent.y)
    {
        zRotation = atan2f(deltaX, deltaY) * (180.0 / M_PI);
    }

    vec3 center = vec3((point.x + parent.x)/2.0, (point.y + parent.y)/2.0, (point.z + parent.z)/2.0);
    mat4 translation = Translate(center);
    return translation * RotateX(xRotation) * RotateZ(zRotation) * Scale(radius, 1, radius) * Scale(0.1, 0.1, 0.1);
}

我尝试了下面给出的解决方案,但似乎根本没有起作用

mat4 getTransformation(vec3 parent, vec3 point)
{
    // moves base of cylinder to origin and gives it unit scaling
    mat4 scaleFactor = Translate(0, 0.5, 0) * Scale(radius/2.0, 1/2.0, radius/2.0) * cylinderModel;

    float length = sqrtf(pow((point.x - parent.x), 2) + pow((point.y - parent.y), 2) + pow((point.z - parent.z), 2));
    vec3 direction = normalize(point - parent);
    float pitch = acos(direction.y);
    float yaw = atan2(direction.z, direction.x);

    return Translate(parent) * Scale(length, length, length) * RotateX(pitch) * RotateY(yaw) * scaleFactor;
}

运行上述代码后得到以下结果:enter image description here

每个黑点都是一个点,其父节点就是产生它的那个点(前一个点)。我想让树枝与这些点匹配。基本上,我正在尝试实现用于随机生成树的空间殖民算法。我已经大部分完成了,但我希望将树枝映射到它上面,使其看起来更好。我可以使用GL_LINES来进行通用连接,但如果我把它搞定了,它会显得更漂亮。该算法在此处有详细解释。
以下是我想做的事情的图像(请原谅我的绘画技巧)。


请注意,缺少一个自由度:圆柱绕其自身轴线的滚动。看到您的抽象公式,我猜想您并不关心这将是哪个角度。(这很好,我只是想告诉您,在两点之间定位圆柱还有其他可能性。) - leemes
你能给我一些其他可能性的提示吗?自从早上11点开始,我一直在努力正确地定位它,哈哈。 - boddie
我只是想说,你最初的目标描述在数学意义上留下了无限多的解决方案(而不是如何编程)。你现有的想法可能是最简单的解决方案,因为它没有这种自由度。 - leemes
你知道如何使用 acos2 吗? - Beta
你尝试过先解决一个更简单的问题吗?比如二维问题? - Beta
显示剩余3条评论
3个回答

2

嗯,有任意数量的旋转矩阵符合您的约束条件。但是任何一个都可以。我们不需要尝试找出特定的旋转,而是直接写下矩阵。假设您的圆柱在没有应用变换时,其轴沿着Z轴。因此,您必须将局部空间Z轴向这两个点之间的方向进行变换。即 z_t = normalize(p_1 - p_2),其中normalize(a) = a / length(a)

现在我们只需要将其变为完整的三维坐标基。我们从一个不与z_t平行的任意向量开始。比如说(1,0,0)或者(0,1,0)或者(0,0,1)中的一个;使用标量积·(也称为内积或点积)和z_t,并使用绝对值最小的向量,我们称之为u向量。 伪代码如下:

# Start with (1,0,0)
mindotabs = abs( z_t · (1,0,0) )
minvec = (1,0,0)
for u_ in (0,1,0), (0,0,1):
    dotabs = z_t · u_
    if dotabs < mindotabs:
        mindotabs = dotabs
        minvec = u_

u = minvec_

然后,您需要对该向量进行正交化,得到一个本地y变换 y_t = normalize(u - z_t · u)
最后通过叉积得到x变换 x_t = z_t × y_t
为了将圆柱移动到正确位置,您需要将其与匹配的平移矩阵相结合。
变换矩阵实际上只是您“来自”空间的轴写成从另一个空间看到的形式。因此,所得到的矩阵,即您要查找的旋转矩阵,仅是将x_t、y_t和z_t向量并排作为矩阵。OpenGL使用所谓的齐次矩阵,因此您需要使用0,0,0,1作为底部行和右侧列来填充它以形成4×4的形式。
然后,您可以将其加载到OpenGL中;如果使用固定功能,则使用glMultMatrix应用旋转,如果使用着色器,则将其乘以最终传递给glUniform的矩阵。

我有点困惑第二段中的“u”是什么意思。我也不明白你所说的“绝对值最小的向量”。这里我们指的是哪些向量? - boddie

0

从一个单位长度的圆柱体开始,其中一个端点(我称之为C1)位于原点(请注意,您的图像表明您的圆柱体的中心在原点,但您可以轻松地将其转换为我开始的内容)。另一个端点(我称之为C2)位于(0,1,0)

我想称呼你在世界坐标系中的两个点为P1P2,我们想要将C1定位到P1,将C2定位到P2

首先通过P1平移圆柱体,这样就成功地将C1定位到P1

然后按照distance(P1, P2)缩放圆柱体,因为它最初的长度为1

剩余的旋转可以使用球面坐标计算。如果您不熟悉这种类型的坐标系:它就像GPS坐标:两个角度;一个围绕极轴(在您的情况下是世界的Y轴)的角度,我们通常称之为偏航角,另一个是俯仰角(在您的情况下是模型空间中的X轴)。这两个角度可以通过将P2-P1(即P2相对于P1的本地偏移量)转换为球面坐标来计算。首先围绕X轴旋转俯仰角,然后围绕Y轴旋转偏航角。
类似以下伪代码可以实现它:
Matrix getTransformation(Point P1, Point P2) {
    float length = distance(P1, P2);
    Point direction = normalize(P2 - P1);
    float pitch = acos(direction.y);
    float yaw = atan2(direction.z, direction.x);

    return translate(P1) * scaleY(length) * rotateX(pitch) * rotateY(yaw);
}

我尝试了你的解决方案,我的代码如上所示,但似乎没有产生影响。 - boddie
在您更新的代码中,请使用Scale(radius, length, radius)而不是Scale(length, length, length)。你看到了什么?我猜你没有看出什么问题。然后尝试改变顺序。并且尝试一步一步地进行。首先尝试将圆柱体定位在“parent”处,然后按长度缩放它(如果这失败了,可能是因为矩阵顺序错误),等等。很容易弄错顺序! - leemes
如果有帮助的话,我在上面发布了我正在尝试做的事情的类型。 - boddie
我从这里获取了公式,但假设r=1并使用atan2而不是atan - leemes
为什么要使用度数?生成这些矩阵时,您肯定会使用弧度中的 sincos。我建议在代码库中的任何地方都使用弧度,除了用户输入角度。 - leemes
显示剩余3条评论

0

将圆柱轴称为A。第二次旋转(关于X)不能改变AX之间的角度,因此我们必须通过Z第一次旋转来获得正确的角度。

将目标向量(两点之间的向量)称为B。取-acos(BX/BY),这就是第一次旋转的角度。

再次取B,忽略X部分,并查看其在(Y,Z)平面上的投影。取acos(BZ/BY),这就是第二次旋转的角度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接