【问题标题】:Rotation Matrix given angle and point in X,Y,Z旋转矩阵在 X,Y,Z 中给定角度和点
【发布时间】:2011-07-08 12:21:15
【问题描述】:

我正在处理图像,我想根据角度、原点以及 x、y 和 z 坐标旋转 xyz 空间中的所有像素。

我只需要设置正确的矩阵 (4x4),然后我会很好。角度以度为单位,而不是弧度,x、y、z 都将从 -1 到 1(浮点数)

编辑:

好的,这是我编写的代码,用于围绕由原点和 X、Y、Z 坐标定义的给定线进行旋转。

        float ang = angD * (float)(Math.PI / 180);  // from degrees to radians, if needed
        //U = n*n(t) + cos(a)*(I-n*n(t)) + sin(a)*N(x).

        var u = MatrixDouble.Identity(4);  // 4x4 Identity Matrix
        u = u.Multiply(Math.Cos(ang));

        var n = new MatrixDouble(1, 4, new List<double> { x, y, z, 0 });
        var nt = n.Transpose();

        // This next part is the N(x) matrix.  The data is inputted in Column
        // first order and fills in the 4x4 matrix with the given 16 Doubles
        var nx = new MatrixDouble(4, 4, new List<double> { 0, z, -y, 0, -z, 0, x, 0, y, -x, 0, 0, 0, 0, 0, 1 });

        nx = nx.Multiply(Math.Sin(ang));

        var ret = nt.Multiply(n);
        ret[3, 3] = 1;

        u = u.Subtract(ret);

        u = ret.Add(u.Add(nx));

这有点复杂,我使用的是自定义 Matrix 库,但使用任何正常运行的 Matrix 库都应该不会太难实现。

呸,很多数学题!

【问题讨论】:

  • 我猜您希望“x、y 和 z 坐标”定义的点保持不变。您如何将 xyz 空间中的一个点表示为 4 向量?
  • 我没有将点表示为 4x4 向量。变换矩阵是 4x4,点是 4x1。将它们相乘得到我的 p',它是仅旋转的像素。我会用我制作的代码更新我的帖子。
  • “4x4 矢量”?你对术语不小心。
  • 哇,我觉得自己很笨。 4x4 矩阵 :) 大声笑,至少我没有说矩阵!嘻嘻

标签: c# math matrix


【解决方案1】:

函数rotateAroundAxis() 围绕3D 中的任意轴旋转点。这是我使用解析几何和编程对过程进行建模的 3D 旋转解决方案。代码在 JavaScript 中。

function rotateAroundAxis(A, B, C, alpha, precision) {
  // A is rotated point, BC is axis, alpha is angle
  // A, B, C are points in format [Ax, Ay, Az], alpha is float, precision is int
  // A2 is output in format [A2x, A2y, A2z]
  if((A[0] - B[0])*(A[1] - C[1]) == (A[1] - B[1])*(A[0] - C[0]) && (A[1] - B[1])*(A[2] - C[2]) == (A[1] - C[1])*(A[2] - B[2]) && (A[0] - B[0])*(A[2] - C[2]) == (A[0] - C[0])*(A[2] - B[2])) {
    return A
  }// Return the original point if it is on the axis.
  var D = findClosestPoint(A, B, C, precision);
  var w = crossProduct(new Array(C[0] - B[0], C[1] - B[1], C[2] - B[2]), new Array(C[0] - A[0], C[1] - A[1], C[2] - A[2]));
  var W = pointPlusVector(A, w);
  var sizeAW = vectorSize(A, W);
  var sizeDA = vectorSize(D, A);
  var sizeAE = sizeDA*(Math.sin(0.5*alpha))/(Math.cos(0.5*alpha));
  var E = new Array(A[0] + (W[0] - A[0])*sizeAE/sizeAW, A[1] + (W[1] - A[1])*sizeAE/sizeAW, A[2] + (W[2] - A[2])*sizeAE/sizeAW);
  var sizeDE = vectorSize(D, E);
  var sizeEF = sizeAE*Math.sin(alpha/2);
  var F = new Array(D[0] + (E[0] - D[0])*(sizeDE - sizeEF)/sizeDE, D[1] + (E[1] - D[1])*(sizeDE - sizeEF)/sizeDE, D[2] + (E[2] - D[2])*(sizeDE - sizeEF)/sizeDE);
  var A2 = new Array(A[0] + 2*(F[0] - A[0]), A[1] + 2*(F[1] - A[1]), A[2] + 2*(F[2] - A[2]))
  return A2;
}

function angleSize(A, S, B) {
  ux = A[0] - S[0]; uy = A[1] - S[1]; uz = A[2] - S[2];
  vx = B[0] - S[0]; vy = B[1] - S[1]; vz = B[2] - S[2];
  if((Math.sqrt(ux*ux + uy*uy + uz*uz)*Math.sqrt(vx*vx + vy*vy + vz*vz)) == 0) {return 0}
  return Math.acos((ux*vx + uy*vy + uz*vz)/(Math.sqrt(ux*ux + uy*uy + uz*uz)*Math.sqrt(vx*vx + vy*vy + vz*vz)));
}

function findClosestPoint(N, B, C, precision) {
  // We will devide the segment BC into many tiny segments and we will choose the point F where the |NB F| distance is the shortest.
  if(B[0] == C[0] && B[1] == C[1] && B[2] == C[2]) {return B}
  var shortest = 0;
  for(var i = 0; i <= precision; i++) {
    var Fx = Math.round(precision*precision*(B[0] + (C[0] - B[0])*i/precision))/(precision*precision);
    var Fy = Math.round(precision*precision*(B[1] + (C[1] - B[1])*i/precision))/(precision*precision);
    var Fz = Math.round(precision*precision*(B[2] + (C[2] - B[2])*i/precision))/(precision*precision);
    var sizeF = vectorSize(new Array(N[0], N[1], N[2]), new Array(Fx, Fy, Fz));
    if(i == 0 || sizeF < shortest) { // first run or condition
      shortest = sizeF;
      F = new Array(Fx, Fy, Fz);
    }
  }
  // recursion, if it is an outer point return findClosestPoint(we mirror further point in the closer one)
  if(F[0] == Math.round(precision*precision*(B[0]))/(precision*precision) && F[1] == Math.round(precision*precision*(B[1]))/(precision*precision) && F[2] == Math.round(precision*precision*(B[2]))/(precision*precision)) { // F == B
    if(Math.round(precision*precision*180*angleSize(C, B, N)/Math.PI)/(precision*precision) <= 90){return F} else {return findClosestPoint(N, new Array(2*B[0] - C[0], 2*B[1] - C[1], 2*B[2] - C[2]), B, precision)}
  } else if (F[0] == Math.round(precision*precision*(C[0]))/(precision*precision) && F[1] == Math.round(precision*precision*(C[1]))/(precision*precision) && F[2] == Math.round(precision*precision*(C[2]))/(precision*precision)) { // F == C
    if(Math.round(precision*precision*180*angleSize(B, C, N)/Math.PI)/(precision*precision) <= 90) {return F} else {return findClosestPoint(N, C, new Array(2*C[0] - B[0], 2*C[1] - B[1], 2*C[2] - B[2]), precision)}
  } else {return F;}
}

function vectorSize(A, B) {
  var ux = A[0] - B[0];
  var uy = A[1] - B[1];
  var uz = A[2] - B[2];
  return Math.sqrt(ux*ux + uy*uy + uz*uz);
}

function crossProduct(u, v) {
  return (new Array(u[1]*v[2] - u[2]*v[1], u[2]*v[0] - u[0]*v[2], u[0]*v[1] - u[1]*v[0]));
}

function pointPlusVector (A, v) {
  return (new Array(A[0] + v[0], A[1] + v[1], A[2] + v[2]));
}

【讨论】:

    【解决方案2】:

    完整的旋转矩阵在https://sites.google.com/site/glennmurray/Home/rotation-matrices-and-formulas 得到并给出。

    来自论文:

    5.2 绕原点旋转的简化矩阵

    请注意,这里假设 (u, v, w) 是旋转轴的方向向量,并且 u^2 + v^2 + w^2 = 1。

    如果你有一个点 (x, y, z) 想要旋转,那么我们可以得到一个由七个变量组成的函数来产生旋转点:

    f(x, y, z, u, v, w, theta) =

    该论文还包括绕任意轴(不一定通过原点)旋转的矩阵和公式、在 Apache 许可下可用的 Java 代码,以及一个说明旋转的网络应用程序的链接。

    【讨论】:

    • 你的数学有点难以消化,这确实奏效了。我花了一点时间,但我让它工作了,这是一个一般情况。谢谢!
    【解决方案3】:

    使用 Matrix3D 结构 (MSDN) - 表示用于在 3-D 空间中进行转换的 4 x 4 矩阵

    在这里查看教程:Building a 3D Engine

    基本上,矩阵是为 X、Y 和 Z 旋转构建的,然后您可以按任意顺序将旋转相乘。

    public static Matrix3D NewRotateAroundX(double radians)
    {
        var matrix = new Matrix3D();
        matrix._matrix[1, 1] = Math.Cos(radians);
        matrix._matrix[1, 2] = Math.Sin(radians);
        matrix._matrix[2, 1] = -(Math.Sin(radians));
        matrix._matrix[2, 2] = Math.Cos(radians);
        return matrix;
    }
    public static Matrix3D NewRotateAroundY(double radians)
    {
        var matrix = new Matrix3D();
        matrix._matrix[0, 0] = Math.Cos(radians);
        matrix._matrix[0, 2] = -(Math.Sin(radians));
        matrix._matrix[2, 0] = Math.Sin(radians);
        matrix._matrix[2, 2] = Math.Cos(radians);
        return matrix;
    }
    public static Matrix3D NewRotateAroundZ(double radians)
    {
        var matrix = new Matrix3D();
        matrix._matrix[0, 0] = Math.Cos(radians);
        matrix._matrix[0, 1] = Math.Sin(radians);
        matrix._matrix[1, 0] = -(Math.Sin(radians));
        matrix._matrix[1, 1] = Math.Cos(radians);
        return matrix;
    }
    

    【讨论】:

    • 虽然您可以按任意顺序将矩阵相乘,但这样做不一定会得到相同的答案。矩阵乘法不可交换。我给出了一个关于轴的旋转矩阵乘积的例子,在我的答案中链接的论文中给出了不同乘法顺序的不同答案。
    • 虽然 MatrixX * MatrixY 确实不一定等于 MatrixY * MatrixX,但由 OP 决定乘法的顺序。
    • 您混淆了 2 个不同的 Matrix3D。即使您参考了 MSDN,Matrix 3D 也没有“_matrix”成员。
    猜你喜欢
    • 2016-12-31
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    • 1970-01-01
    • 2017-08-12
    • 1970-01-01
    • 2021-04-14
    • 1970-01-01
    相关资源
    最近更新 更多