【问题标题】:How to use Transformation Matrix for SpriteBatch correctly?如何正确使用 SpriteBatch 的变换矩阵?
【发布时间】:2013-01-09 07:07:19
【问题描述】:

我是 XNA 的新手,想在它之上开发一个轻量级的 2D 引擎,将实体组织成父子层次结构。我在画孩子的时候会想到矩阵,因为他们的位置、旋转和比例取决于他们的父母。

如果我使用SpriteBatch.Begin(),我的矩形可以在屏幕上绘制,但是当我将它们更改为:

        this.DrawingMatrix = Matrix.Identity;
        this.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullClockwise, null, this.DrawingMatrix);

什么都没有画了。我什至尝试了new Matrix()Matrix.CreateTranslation(0, 0, 0)DrawingMatrix

我的第一个问题是:为什么它不起作用?我没有使用任何相机或视口。

其次,在绘制实体之前,我调用PreDraw 来转换矩阵(然后我将在PostDraw 处重置为原始状态):

    protected virtual void PreDraw(Engine pEngine)
    {
        pEngine.DrawingMatrix *=
            Matrix.CreateTranslation(this.X, this.Y, 0) *
            Matrix.CreateScale(this.ScaleX, this.ScaleY, 1) *
            Matrix.CreateRotationZ(this.Rotation);
    }

请澄清以上代码的更正。而且我需要扩展的不是原点,而是ScaleCenterXScaleCenterY,我该如何实现?

添加:这是我的引擎绘制过程的示例:

  1. 调用这些代码:

        this.DrawingMatrix = Matrix.CreateTranslation(0, 0, 0);
        this.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullClockwise, null, this.DrawingMatrix);
    
  2. 致电PreDraw(),联系方式是:

    protected virtual void PreDraw(Engine pEngine)
    {
        pEngine.DrawingMatrix *=
            Matrix.CreateTranslation(this.X, this.Y, 0) *
            Matrix.CreateScale(this.ScaleX, this.ScaleY, 1) *
            Matrix.CreateRotationZ(this.Rotation);
    }
    
  3. 例如,在我的Rect 类中调用Draw()

    protected override void Draw(Engine pEngine)
    {
        pEngine.SpriteBatch.Draw(pEngine.RectangleTexture, new Rectangle(0, 0, (int)this.Width, (int)this.Height), new Rectangle(0, 0, 1, 1), this.Color);
    }
    

如果我将上面的Begin代码替换为this.SpriteBatch.Begin(),矩形绘制正确,所以我猜是因为矩阵。

【问题讨论】:

  • 使用 Matrix.Identity 应该导致与根本不指定矩阵相同的行为,所以我认为这不是你的问题。你能发布一些实际负责绘制精灵的代码吗?
  • @ColeCampbell 刚刚添加 :)

标签: algorithm matrix xna 2d transformation


【解决方案1】:

第一个问题是一个简单的错误:SpriteBatch 的默认值是CullCounterClockwise,但是您指定了CullClockwise 导致所有精灵都被背面剔除。如果您只想使用默认渲染状态,则可以传递 null - 您不需要明确指定它们。

(如果您使用负比例,则需要更改剔除模式。)

要回答您的第二个问题:您需要翻译“后退”以将缩放原点(您的 ScaleCenterXScaleCenterY)放在世界原点 (0,0)。转换总是发生在 (0,0) 附近。所以通常顺序是:将精灵原点翻译回世界原点,缩放,旋转,翻译以将精灵原点放置在所需的世界位置。

另外,我希望你的 PostDraw 没有应用反向转换(你让它听起来像它一样)。这很可能会导致精度问题。您应该保存并恢复矩阵。

【讨论】:

  • 很好地捕捉到了剔除模式,不知道我是怎么错过的。
  • 非常感谢!但是,我遇到了另一个问题。我看起来 XNA 中的Matrix 是不可变的,每次我修改时,都会创建一个新实例,所以我必须为每个Entity 继续调用Begin()End()。这是一个好习惯吗?有没有更好的方法来修改现有的矩阵?
  • Matrix 是一种值类型(可变类型)。因此,无论何时您传递它——例如在Begin 的参数中——它都会被复制。如果修改原件,则副本看不到更改。矩阵用作着色器参数 - 这意味着它必须是每批次的。批次是有成本的(详见this answer)。如果这导致您绘制足够多的批次以达到批次限制,您可能需要实施另一种绘图方法。将参数“堆叠”到Draw 所需的数学运算并不难计算 - 可能是最简单的方法。
  • 哦,stack参数是什么意思?
  • @W.N.我脑子里一片空白,试图想出合适的术语。但基本上当你的层次结构的一个元素有一个变换,然后一个孩子有另一个变换。当您绘制孩子时,会应用两个变换,首先是父级的。正如我所提到的——你应该在遍历场景图时保存和恢复转换——并且在遍历期间存储转换的适当数据结构是 stack。虽然矩阵是存储变换的一种方式 - 可以(但更复杂)将它们存储为 Draw 可以使用的旋转/缩放/平移。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 1970-01-01
  • 2012-09-04
  • 2010-10-25
  • 2021-02-27
相关资源
最近更新 更多