如何在 C# 中一次旋转、缩放和平移矩阵?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/636081/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 11:12:55  来源:igfitidea点击:

How to rotate, scale, and translate a matrix all at once in C#?

c#matrixtranslationslimdx

提问by x4000

Okay, this is something that should be a simple matrix question, but my understanding of matrices is somewhat limited. Here's the scenario: I have a 1px by 1px sprite that I want to scale by some amount x and y (different amounts on each side), and then I want to rotate that sprite by some angle, and then I want to be able to precisely position the whole thing (from the top left or the center, makes no difference to me).

好的,这应该是一个简单的矩阵问题,但我对矩阵的理解有些有限。这是场景:我有一个 1px x 1px 的精灵,我想按 x 和 y 的数量(每侧不同的数量)进行缩放,然后我想将该精灵旋转某个角度,然后我希望能够精确定位整个事物(从左上角或中心,对我来说没有区别)。

So far my code is vaguely close, but it tends to be off by some random amount depending on the angle I pass in.

到目前为止,我的代码有点接近,但根据我传入的角度,它往往会偏离一些随机量。

I would think that this would do it:

我认为这会做到:

        Point center = new Point( 50, 50 );
        float width = 60;
        float height = 100;
        float angle = 0.5;
        Vector3 axis = new Vector3( center.X, center.Y, 0 );
        axis.Normalize();
        Matrix m = Matrix.Scaling( width, height, 0 ) *
            Matrix.RotationAxis( axis, angle ) *
            Matrix.Translation( center.X, center.Y, 0 );

But it tends to shrink the scale of the rotated line way down, even though I think it's positioning it sort of right.

但它往往会缩小旋转线的比例,即使我认为它的定位是正确的。

I've also tried this:

我也试过这个:

  Matrix m = Matrix.Transformation2D( new Vector2( center.X, center.Y ), 0f,
      new Vector2( width, height ), new Vector2( center.X, center.Y ),
      angle, Vector2.Zero );

The line looks exactly right, with the exact right size and shape, but I can't position it correctly at all. If I use the translation vector at the end of the call above, or if I set a position using Sprite.Draw, neither works right.

这条线看起来完全正确,大小和形状完全正确,但我根本无法正确定位它。如果我在上面的调用结束时使用平移向量,或者如果我使用 Sprite.Draw 设置位置,则两者都不正确。

This is all in SlimDX. What am I doing wrong?

这一切都在 SlimDX 中。我究竟做错了什么?

采纳答案by Ben S

I just went through the pain of learning matrix transformation, but using XNA.

我刚刚经历了学习矩阵变换的痛苦,但使用了 XNA。

I found thesearticlestobeveryhelpful in understanding what happens. The method calls are very similar in XNA and the theory behind it all should apply to you, even with SlimDX.

我发现这些文章了解发生了什么帮助。XNA 中的方法调用非常相似,即使使用 SlimDX,其背后的理论也应该适用于您。

From glancing at your code, I think you should be translating at the start, to the origin, and then translating again at the end, to the final position, though I'm still a little bit of a newbie at this as well.

从看你的代码来看,我认为你应该在开始时翻译到原点,然后在最后再翻译到最终位置,尽管我在这方面仍然有点新手。

The order I would do it in is:

我要做的顺序是:

  • Translate to origin
  • Scale
  • Rotate
  • Translate to desired location
  • 翻译成原点
  • 规模
  • 旋转
  • 翻译到所需位置

The reason for translating to the origin first is that rotations are based from the origin. Therefore to rotate something about a certain point, place that point on the origin before rotating.

首先平移到原点的原因是旋转基于原点。因此,要围绕某个点旋转某物,请在旋转前将该点放在原点上。

回答by RS Conley

What you need to do is do each transformation one step at a time. Do the scaling first draw it. Is it where you expect it. Then throw in the the rotate, and then the translate. This will help uncover incorrect assumptions.

您需要做的是一次一步地进行每个转换。先做缩放绘制它。是否在您期望的地方。然后投入旋转,然后是平移。这将有助于发现不正确的假设。

You can also draw in temporary lines to help figure where the coordinates are. For example if you do your stretching and rotation and expect your end point to be at 0,0. Drawing a another line with one endpoint at 0,0 will serve as a double check to see if that really the case.

您还可以绘制临时线来帮助确定坐标的位置。例如,如果您进行拉伸和旋转并期望终点为 0,0。绘制另一条端点为 0,0 的线将作为双重检查,看看是否真的如此。

For your specific problem the problem may be with the rotation. After you have scaled and rotate the line is now off center causing you problems when you are translating.

对于您的具体问题,问题可能出在旋转上。缩放和旋转后,线现在偏离中心,导致您在翻译时出现问题。

The general solution is to move the line back to the origin do any operations that involve changing it shape or orientation. Afterward because you are at a known good location you can translate to your final destination.

一般的解决方案是将线移回原点,执行任何涉及更改其形状或方向的操作。之后,因为您在一个已知的好位置,您可以翻译到您的最终目的地。

RESPONSE TO COMMENTS

回应评论

If translation is an issue and you are transforming the coordinate system then you will need to write a conversion function to convert between the old system and the new.

如果转换是一个问题并且您正在转换坐标系,那么您将需要编写一个转换函数来在旧系统和新系统之间进行转换。

For example if you are rotating 45 degrees. Before the rotation you could translate by 0,1 to move up 1 inch. After rotation you will have to translate by roughly -.70707, .070707 to move up 1 inch relative to the original coordinate system.

例如,如果您旋转 45 度。在旋转之前,您可以平移 0,1 以向上移动 1 英寸。旋转后,您必须大致平移 -.70707, .070707 才能相对于原始坐标系向上移动 1 英寸。

回答by Aric TenEyck

Are you rotating around the correct axis? I'm a newbie at matrix stuff, but it seems to me that since the sprite exists in the x, y space, the rotation axis should be the Z-Axis - i.e. (0,0,1).

您是否围绕正确的轴旋转?我是矩阵方面的新手,但在我看来,由于精灵存在于 x、y 空间中,旋转轴应该是 Z 轴 - 即(0,0,1)。

回答by x4000

Okay, this is now working. Here's my working code for this, in case someone else needs it:

好的,现在可以了。这是我的工作代码,以防其他人需要它:

    Point sourceLoc = new Point ( 50, 50 );
    float length = 60;
    float thickness = 2;
    float angle = 0.5;
    Matrix m = Matrix.Scaling( length, thickness, 0 ) *
            Matrix.RotationZ( angle ) *
            Matrix.Translation( sourceLoc.X, sourceLoc.Y, 0 );
    sprite.Transform = m;

    sprite.Draw( this.tx, Vector3.Zero, Vector3.Zero, Color.Red );

This will draw an angled line of your chosen length, with a thickness equal to your chosen thickness (presuming your texture is a 1x1 pixel white image). The source location is where the line will emit from, with whatever angle you specify (in radians). So if you start at zero and increment by something like 0.1 until you hit 2PI, and then reset to 0, you'll have a line that rotates around a center like a clock hand or radar sweep. This is what I was looking for -- thanks to all who contributed!

这将绘制一条您选择的长度的斜线,其厚度等于您选择的厚度(假设您的纹理是一个 1x1 像素的白色图像)。源位置是线将发射的位置,无论您指定什么角度(以弧度为单位)。因此,如果您从 0 开始并以 0.1 之类的增量递增,直到达到 2PI,然后重置为 0,您将有一条像时钟指针或雷达扫描一样围绕中心旋转的线。这就是我一直在寻找的——感谢所有贡献者!