如何应用变换矩阵? [英] How to apply a transformation matrix?

查看:23
本文介绍了如何应用变换矩阵?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试获取 3D 空间中某个点的 2D 屏幕坐标,即我知道相机的平移、倾斜和滚动的位置,并且我有我希望获得的点的 3D x、y、z 坐标项目.

I am trying to get the 2D screen coordinates of a point in 3D space, i.e. I know the location of the camera its pan, tilt and roll and I have the 3D x,y,z coordinates of a point I wish to project.

我很难理解转换/投影矩阵,我希望这里的一些聪明人可以帮助我;)

I am having difficulty understanding transformation/projection matrices and I was hoping some intelligent people here could help me along ;)

这是我迄今为止拼凑的测试代码:

Here is my test code I have thrown together thus far:

public class TransformTest {

public static void main(String[] args) {

    // set up a world point (Point to Project)
    double[] wp = {100, 100, 1};
    // set up the projection centre (Camera Location)
    double[] pc = {90, 90, 1};

    double roll = 0;
    double tilt = 0;
    double pan = 0;

    // translate the point
    vSub(wp, pc, wp);

    // create roll matrix
    double[][] rollMat = {
            {1, 0, 0},
            {0, Math.cos(roll), -Math.sin(roll)},
            {0, Math.sin(roll), Math.cos(roll)},
    };
    // create tilt matrix
    double[][] tiltMat = {
            {Math.cos(tilt), 0, Math.sin(tilt)},
            {0, 1, 0},
            {-Math.sin(tilt), 0, Math.cos(tilt)},
    };
    // create pan matrix
    double[][] panMat = {
            {Math.cos(pan), -Math.sin(pan), 0},
            {Math.sin(pan), Math.cos(pan), 0},
            {0, 0, 1},
    };

    // roll it
    mvMul(rollMat, wp, wp);
    // tilt it
    mvMul(tiltMat, wp, wp);
    // pan it
    mvMul(panMat, wp, wp);

}

public static void vAdd(double[] a, double[] b, double[] c) {
    for (int i=0; i<a.length; i++) {
        c[i] = a[i] + b[i];
    }
}

public static void vSub(double[] a, double[] b, double[] c) {
    for (int i=0; i<a.length; i++) {
        c[i] = a[i] - b[i];
    }      
}

public static void mvMul(double[][] m, double[] v, double[] w) {

    // How to multiply matrices?
} }

基本上,我需要的是获取 3D 点相交的给定屏幕的 2D XY 坐标.我不确定如何使用滚动、倾斜和平移矩阵来转换世界点 (wp).

Basically, what I need is to get the 2D XY coordinates for a given screen where the 3D point intersects. I am not sure how to use the roll, tilt and pan matrices to transform the world point (wp).

非常感谢您对此的任何帮助!

Any help with this is greatly appreciated!

推荐答案

这很复杂.请阅读有关此主题的书以获取所有数学和细节.如果你打算长期玩这些东西,你需要知道这些东西.这个答案只是为了让你可以弄湿自己的脚并四处走动.

第一件事.矩阵相乘是一个相当简单的事情.

First things first. Multiplying matrices is a reasonably simple affair.

假设您有矩阵 ABC,其中 AB = C.假设您想计算矩阵 C 在第 3 行第 2 列的值.

Let's say you have matrices A, B, and C, where AB = C. Let's say you want to figure out the value of matrix C at row 3, column 2.

  • A的第三行和B的第二列.您现在应该拥有 AB 中相同数量的值.(如果你没有为这两个矩阵定义矩阵乘法.你不能这样做.)如果两者都是 4×4 矩阵,你应该有来自 A 的 4 个值(第 3) 和 B 中的 4 个值(第 2 列).
  • A 的每个值乘以 B 的每个值.您应该最终得到 4 个新值.
  • 添加这些值.
  • Take the third row of A and the second column of B. You should have the same number of values from A and B now. (If you don't matrix multiplication isn't defined for those two matrices. You can't do it.) If both are 4×4 matrices, you should have 4 values from A (row 3) and 4 values from B (column 2).
  • Multiply each value of A with each value of B. You should end up with 4 new values.
  • Add these values.

您现在在第 3 行第 2 列拥有矩阵 C 的值.当然,挑战在于以编程方式执行此操作.

You now have the value of matrix C at row 3, column 2. The challenge is, of course, to do this programmatically.

/* AB = C

Row-major ordering
a[0][0] a[0][2] a[0][3]...
a[1][0] a[1][4] ...
a[2][0] ...
...*/
public static mmMul(double[][] a, double[][] b, double[][] c) {
    c_height = b.length; // Height of b
    c_width = a[0].length; // Width of a
    common_side = a.length; // Height of a, width of b

    for (int i = 0; i < c_height; i++) {
        for (int j = 0; j < c_width; j++) {
            // Ready to calculate value of c[i][j]
            c[i][j] = 0;

            // Iterate through ith row of a, jth col of b in lockstep
            for (int k = 0; k < common_side; k++) {
                c[i][j] += a[i][k] * b[k][j];
            }
        }
    }
}

<小时>

齐次坐标

您有 3D 坐标.假设您有 (5, 2, 1).这些是笛卡尔坐标.让我们称它们为 (x, y, z).

齐次坐标意味着您在笛卡尔坐标的末尾多写一个 1.(5, 2, 1) 变成 (5, 2, 1, 1).我们称它们为(xyzw).

Homogenous coordinates mean that you write an extra 1 at the end of your Cartesian coordinates. (5, 2, 1) becomes (5, 2, 1, 1). Let's call them (x, y, z, w).

每当你做一个使 w ≠ 1 的变换时,你用 w 除以坐标的每个分量.这会改变你的 xyz,并再次使 w = 1.(即使您的转换没​​有改变w,这样做也没有坏处.它只是将所有内容除以 1,没有任何作用.)

Whenever you do a transformation that makes w ≠ 1, you divide every component of your coordinates by w. This changes your x, y, and z, and it makes w = 1 again. (There is no harm in doing this even when your transformation doesn't change w. It just divides everything by 1, which does nothing.)

您可以使用齐次坐标做一些非常酷的事情,即使它们背后的数学并不完全合理.正是在这一点上,我请您再次查看此答案顶部的建议.

There is some majorly cool stuff you can do with homogenous coordinates, even if the math behind them doesn't make total sense. It is at this point that I ask you to look again at the advice at the top of this answer.

我将在本节和后续章节中使用 OpenGL 术语和方法.如果有任何不清楚或似乎与您的目标相冲突(因为这对我来说似乎有点像家庭作业:P),请发表评论.

I'll be using OpenGL terminology and approaches in this and following sections. If anything is unclear or seems to conflict with your goals (because this seems vaguely homework-like to me :P), please leave a comment.

我还将首先假设您的滚动、倾斜和平移矩阵是正确的.

I'll also start by assuming that your roll, tilt, and pan matrices are correct.

当您想使用转换矩阵转换一个点时,您可以将该矩阵与代表您的点的列向量右乘.假设您想通过某个转换矩阵 A 来转换 (5, 2, 1).您首先定义 v = [5, 2, 1, 1]T.(我写 [x, y, z, w]Tlittle T 表示应该把它写成一个列向量.)

When you want to transform a point using a transformation matrix, you right-multiply that matrix with a column vector representing your point. Say you want to translate (5, 2, 1) by some transformation matrix A. You first define v = [5, 2, 1, 1]T. (I write [x, y, z, w]T with the little T to mean that you should write it as a column vector.)

// Your point in 3D
double v[4][5] = {{5}, {2}, {1}, {1}}

在这种情况下,Av = v1,其中 v1是你的转化点.像矩阵乘法一样进行乘法,其中 A 是 4×4,v 是 4×1.你最终会得到一个 4×1 的矩阵(这是另一个列向量).

In this case, Av = v1, where v1 is your transformed point. Do this multiplication like a matrix multiplication, where A is 4×4 and v is 4×1. You will end up with a 4×1 matrix (which is another column vector).

// Transforming a single point with a roll
double v_1[4][6];
mmMul(rollMat, v, v_1);

现在,如果您要应用多个变换矩阵,请先将它们组合成一个变换矩阵.为此,请按照您希望应用的顺序将矩阵相乘.

Now, if you have several transformation matrices to apply, first combine them into one transformation matrix. Do this by multiplying the matrices together in the order that you want them applied.

以编程方式,您应该从单位矩阵开始并右乘每个变换矩阵.设 I4 为 4×4 单位矩阵,令 A1, A2, A3, ... 成为你的变换矩阵.让你的最终变换矩阵是 Afinal

Programmatically, you should start with the identity matrix and right-multiply each transformation matrix. Let I4 be 4×4 identity matrix, and let A1, A2, A3, ... be your transformation matrices. Let your final transformation matrix be Afinal

Afinal4
AfinalAfinal A1
AfinalAfinal A2
AfinalAfinal A3

AfinalI4
AfinalAfinal A1
AfinalAfinal A2
AfinalAfinal A3

请注意,我使用该箭头来表示分配.当您实现这一点时,请确保在矩阵乘法计算中仍然使用它时不要覆盖 Afinal !复制一份.

// A composite transformation matrix (roll, then tilt)

double a_final[4][4] =
{
    {1, 0, 0, 0},
    {0, 1, 0, 0},
    {0, 0, 1, 0},
    {0, 0, 0, 1}
}; // the 4 x 4 identity matrix

double a_final_copy[4][4];
mCopy(a_final, a_final_copy); // make a copy of a_final
mmMul(rollMat, a_final_copy, a_final);
mCopy(a_final, a_final_copy); // update the copy
mmMul(tiltMat, a_final_copy, a_final);

最后,做和上面一样的乘法:Afinal v = v1

Finally, do the same multiplication as above: Afinal v = v1

// Use the above matrix to transform v
mmMul(a_final, v, v_1);

<小时>

从头到尾

相机变换应表示为视图矩阵.在此处执行您的 Aview v = v1 操作.(v 将您的世界坐标表示为 4×1 列向量,Afinal 是您的 A查看.)


From start to finish

Camera transformations should be represented as a view matrix. Perform your Aview v = v1 operation here. (v represents your world coordinates as a 4×1 column vector, Afinal is your Aview.)

// World coordinates to eye coordinates
// A_view is a_final from above
mmMult(a_view, v_world, v_view);

投影变换描述了透视变换.这就是使较近的物体变大而较远的物体变小的原因.这是在相机变换之后执行的.如果您还不需要透视,只需将单位矩阵用于投影矩阵.无论如何,在这里执行A v1 = v2.

Projection transformations describe a perspective transform. This is what makes nearer objects bigger and farther objects smaller. This is performed after the camera transformation. If you don't want perspective yet, just use the identity matrix for the projection matrix. Anyway, perform A v1 = v2 here.

// Eye coordinates to clip coordinates
// If you don't care about perspective, SKIP THIS STEP
mmMult(a_projection, v_view, v_eye);

接下来,您需要进行透视划分.这将深入研究同质坐标,我还没有描述过.无论如何,将 v2 的每个分量除以 v2 的最后一个分量.如果 v2 = [x, y, z, w]T,然后将每个组件除以w(包括w本身).你应该以 w = 1 结束.(如果你的投影矩阵是单位矩阵,就像我之前描述的那样,这一步应该什么都不做.)

Next, you need to do a perspective divide. This delves deeper into homogenous coordinates, which I haven't described yet. Anyway, divide every component of v2 by the last component of v2. If v2 = [x, y, z, w]T, then divide each component by w (including w itself). You should end up with w = 1. (If your projection matrix is the identity matrix, like I described earlier, this step should do nothing.)

// Clip coordinates to normalized device coordinates
// If you skipped the previous step, SKIP THIS STEP
for (int i = 0; i < 4; i++) {
    v_ndc[i] = v_eye[i] / v[3];
}

最后,拿你的 v2.前两个坐标是 xy 坐标.第三个是 z,你可以扔掉它.(稍后,一旦你变得非常高级,你可以使用这个 z 值来确定哪个点在其他点之前或之后.)此时,最后一个组件是 w = 1,所以你不再需要它了.

Finally, take your v2. The first two coordinates are your x and y coordinates. The third is z, which you can throw away. (Later, once you get very advanced, you can use this z value to figure out which point is in front of or behind some other point.) And at this point, the last component is w = 1, so you don't need that at all anymore.

x = v_ndc[0]
y = v_ndc[1]
z = v_ndc[2]  // unused; your screen is 2D

如果您跳过了透视和透视分割步骤,请使用 v_view 而不是上面的 v_ndc.

If you skipped the perspective and perspective divide steps, use v_view instead of v_ndc above.

这非常类似于 OpenGL 坐标系.不同之处在于您从世界坐标开始,而 OpenGL 从对象坐标开始.区别如下:

This is very similar to the set of OpenGL coordinate systems. The difference is that you start with world coordinates, while OpenGL starts with object coordinates. The difference is as follows:

  • 从世界坐标开始
    • OpenGL 从对象坐标开始
    • OpenGL 使用 ModelView 矩阵将对象坐标转换为眼睛坐标

    从那以后,一切都一样.

    From there on, everything is the same.

    这篇关于如何应用变换矩阵?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆