如何应用变换矩阵?
我试图获取 3D 空间中某个点的 2D 屏幕坐标,即我知道摄像机的平移、倾斜和滚动位置,并且我有我希望投影的点的 3D x、y、z 坐标。
我很难理解变换/投影矩阵,我希望这里的一些聪明人可以帮助我;)
这是我迄今为止整理的测试代码:
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?
} }
基本上,我需要的是获取给定的 2D XY 坐标3D 点相交的屏幕。 我不知道如何使用滚动、倾斜和平移矩阵来变换世界点(wp)。
非常感谢任何帮助!
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?
} }
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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是很复杂的事情。 请阅读一本有关该主题的书,以了解所有数学和基本细节。 如果你打算长期使用这些东西,你需要了解这些事情。 这个答案只是为了让你可以先实践一下。
矩阵相乘
首先要做的事情。 矩阵相乘是一件相当简单的事情。
假设您有矩阵 A、B 和 C,其中 AB = C >。 假设您想计算出矩阵 C 第 3 行第 2 列的值。
现在,您已在第 3 行第 2 列处获得了矩阵 C 的值。当然,挑战在于以编程方式执行此操作。
齐次坐标
您有 3D 坐标。 假设您有 (5, 2, 1)。 这些是笛卡尔坐标。 我们称它们为(x、y、z)。
齐次坐标意味着您在笛卡尔坐标末尾额外写入 1。 (5,2,1) 变为 (5,2,1,1)。 我们称它们为(x、y、z、w)。
每当您进行使 w ≠ 1 的转换时,您都将坐标的每个分量除以 w。 这会更改您的 x、y 和 z,并且再次使 w = 1。 (即使你的变换没有改变 w,这样做也没有坏处。它只是将所有内容除以 1,什么也不做。)
你可以使用同质坐标做一些非常酷的事情,即使它们背后的数学并不完全有意义。 正是在这一点上,我要求您再次查看此答案顶部的建议。
转变一点
我将在本节和以下各节中使用 OpenGL 术语和方法。 如果有任何不清楚或似乎与您的目标相冲突(因为这对我来说似乎有点像家庭作业:P),请发表评论。
我还将首先假设您的滚动、倾斜和平移矩阵是正确的。
当您想要使用变换矩阵来变换点时,可以将该矩阵与表示该点的列向量右乘。 假设您想通过某个变换矩阵 A 来平移 (5, 2, 1)。 您首先定义v = [5, 2, 1, 1]T。 (我用 [x、y、z、w]T 写小 T 表示您应该将其写为列向量。)
在这种情况下,Av = v 1,其中v1是你的变换点。 像矩阵乘法一样执行此乘法,其中 A 为 4×4,v 为 4×1。 您最终将得到一个 4×1 矩阵(这是另一个列向量)。
现在,如果您要应用多个变换矩阵,请首先将它们组合成一个变换矩阵。 通过按照您希望应用的顺序将矩阵相乘来实现此目的。
从编程角度来说,您应该从单位矩阵开始,然后对每个变换矩阵进行右乘。 令 I4 为 4×4 单位矩阵,并令 A1, A2, A3, ... 是您的变换矩阵。 让你的最终变换矩阵为 Afinal
Afinal ← I 4
A最终 ← A最终 A1< br>
A最终 ← A最终 A2< br>
A最终 ← A最终 A3< br>
请注意,我使用该箭头来表示赋值。 当您实现此操作时,请确保在矩阵乘法计算中仍在使用 Afinal 时不要覆盖它! 复制一份。
最后,进行与上面相同的乘法:Afinal v = v 1
从开始到结束
相机变换应该表示为视图矩阵。 在此执行 Aview v = v1 操作。 (v 将您的世界坐标表示为 4×1 列向量,Afinal 是您的 A view。)
投影变换描述了透视变换。 这就是使较近的物体变大而使较远的物体变小的原因。 这是在相机变换之后执行的。 如果您还不需要透视,只需使用单位矩阵作为投影矩阵即可。 不管怎样,在这里执行 A v1 = v2 。
接下来,您需要进行视角划分。 这更深入地研究了同质坐标,我还没有描述过。 无论如何,将 v2 的每个分量除以 v2 的最后一个分量。 如果v2 = [x,y,z,w ]T,然后将每个分量除以w(包括w本身)。 你最终应该得到 w = 1。(如果你的投影矩阵是单位矩阵,就像我之前描述的那样,这一步应该什么都不做。)
最后,取你的 v <子>2。 前两个坐标是您的 x 和 y 坐标。 第三个是z,你可以扔掉它。 (稍后,一旦您变得非常高级,您可以使用此 z 值来确定哪个点位于其他点的前面或后面。)此时,最后一个组件是 w = 1,所以你根本不再需要它了。
如果您跳过了透视图和透视图划分步骤,请使用
v_view
而不是上面的v_ndc
。这与 OpenGL 坐标系。 不同之处在于,您从世界坐标开始,而 OpenGL 从对象坐标开始。 区别如下:
从那时起,一切都是一样的。
This is complicated stuff. Please read a book about this topic to get all the math and nitty gritty details. If you plan on playing with this stuff at length, you need to know these things. This answer is just so you can get your feet wet and hack around.
Multiplying matrices
First things first. Multiplying matrices is a reasonably simple affair.
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.
You now have the value of matrix C at row 3, column 2. The challenge is, of course, to do this programmatically.
Homogenous coordinates
You have 3D coordinates. Let's say you have (5, 2, 1). These are Cartesian coordinates. Let's call them (x, y, z).
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).
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.
Transforming a point
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.
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.)
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).
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.
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
Afinal ← I4
Afinal ← Afinal A1
Afinal ← Afinal A2
Afinal ← Afinal A3
Note that I'm using that arrow to represent assignment. When you implement this, make sure not to overwrite Afinal while you're still using it in the matrix multiplication calculation! Make a copy.
Finally, do the same multiplication as above: Afinal v = v1
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.)
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.
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.)
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.
If you skipped the perspective and perspective divide steps, use
v_view
instead ofv_ndc
above.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:
From there on, everything is the same.
这个范围太大了,无法在这里得到一个好的答案:我建议阅读有关该主题的很好的参考资料。 我一直很喜欢 Foley 和 VanDam...
The scope of this is way too large to get a good answer here: I'd recommend reading a good reference on the topic. I've always liked the Foley and VanDam...
我在此处发布了一些代码,可以满足您的大部分需求。
它包含 OpenGL
gluPerspective()
和gluLookAt()
函数的 Java 实现:要使用其中的
project()
函数,请使用:返回的 code>x 和
y
值在 -0.5 ... 0.5 范围内I've posted some code here that does much of what you need.
It contains Java implementations of the OpenGL
gluPerspective()
andgluLookAt()
functions:To use the
project()
function therein, use:The
x
andy
values returned fall in the range -0.5 ... 0.5