XNA - 与SpriteBatch.Draw变换等价的矩阵是什么?

4

假设我有这样一个接口:

interface Drawable {
    Vector2 DrawPosition { get; }
    Texture2D Texture { get; }
    float Rotation { get; }
    Vector2 Origin { get; }
    Vector2 Scale { get; }
    bool FlipHorizontally { get; }
}

在扩展Microsoft.Xna.Framework.Game的类中,我重写了Draw(GameTime)方法,下面是其中的代码:

Drawable d = ...;
spriteBatch.Begin();
spriteBatch.Draw(d.Texture, d.DrawPosition, new Rectangle(0, 0, d.Texture.Width, d.Texture.Height), Color.White, d.Rotation, d.Origin, d.Scale, d.FlipHorizontally ? SpriteEffects.FlipHorizontally : SpriteEffects.None, 0);
spriteBatch.End();

这里使用了SpriteBatch.Draw(Texture2D texture, Vector2 position, Nullable sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)重载方法。

假设我有一组顶点,它们粗略地勾勒出了d.Texture返回的图像(也就是说,如果我在Microsoft Paint中打开该图像并用铅笔描绘每个顶点,则它们会相当贴近)。如果我想要绘制这些点使它们覆盖纹理,并使用GraphicsDevice.DrawUserPrimitives()方法绘制,那么是否有一种方法仅使用矩阵来转换这些顶点?关键是只能使用矩阵,因为我没有其他绘图选择,实际上我需要使用转换后的顶点进行其他操作。我已经尝试过类似于

Matrix.CreateTranslation(new Vector3(-d.Origin, 0))
    * Matrix.CreateScale(new Vector3(d.Scale, 0))
    * Matrix.CreateRotationZ(d.Rotation)
    * Matrix.CreateTranslation(new Vector3(d.DrawPosition, 0)));

但它失败得很彻底。这个问题有解决方案吗?

“它失败得相当严重”并不能充分描述正在发生的情况。 - Andrew Russell
2个回答

2

您的矩阵代码似乎对于将模型放置在世界空间中的World矩阵是正确的。所以我猜测问题可能出在以下几个方面:

  • 您的基元(primitives)在模型空间(model-space)中的位置不正确。Sprite batch会创建一个包含单个多边形的精灵,其中(0,0)为精灵左上角的顶点,({texture width}, {texture height})为右下角的顶点。您的基元需要具有相同的大小并处于相同的位置。

  • 您的Projection(投影)矩阵不正确。请参见此答案。请注意,SpriteBatch使用翻转的(客户端空间)坐标系。

  • 您的背面剔除模式不正确(没有考虑到翻转坐标系)。

  • 您可能遇到了深度缓冲区(depth buffer)问题(您需要在远和近平面内进行绘制,并且是否被任何对象进行了深度剔除?)

如果仍然存在问题,请从DirectX SDK中获取PIX并使用它来确定您的游戏实际上绘制了什么内容。


0

哦,我的天啊,各位,非常抱歉。我刚刚意识到它不匹配的整个原因是因为我把顶点搞错了!好吧,我想如果你们需要在做同样的事情时需要帮助,这是我的最终版本:

abstract class Drawable
{
    public abstract Vector2 DrawPosition { get; }
    public abstract Texture2D Texture { get; }
    public abstract float Rotation { get; }
    public abstract Vector2 Origin { get; }
    public abstract Vector2 Scale { get; }
    public abstract bool FlipHorizontally { get; }

    public abstract Vector2[] Vertices { get; }

    public Matrix TransformationMatrix
    {
        get
        {
            return Matrix.CreateTranslation(-new Vector3(Texture.Width * Scale.X / 2, 0, 0))
                * Matrix.CreateScale(new Vector3(FlipHorizontally ? -1 : 1, 1, 1))
                * Matrix.CreateTranslation(new Vector3(Texture.Width * Scale.X / 2, 0, 0))
                * Matrix.CreateTranslation(-new Vector3(Origin, 0))
                * Matrix.CreateScale(new Vector3(Scale, 0))
                * Matrix.CreateRotationZ(Rotation)
                * Matrix.CreateTranslation(new Vector3(DrawPosition, 0));
        }
    }
}

class Camera
{
    private readonly Viewport viewport;

    public Matrix GetViewMatrix()
    {
        return Matrix.CreateScale(1, -1, 1) * Matrix.CreateTranslation(0, viewport.Height, 0);
    }

    public Vector2 MouseToWorld(int x, int y)
    {
        return Vector2.Transform(new Vector2(x, y), Matrix.CreateScale(1, -1, 1) * Matrix.CreateTranslation(0, viewport.Height, 0));
    }
}

class Game1 : Microsoft.Xna.Framework.Game
{
    private Drawable avatar;
    private Camera camera;
    ...
    protected override void Initialize() {
        avatar = ...;
        camera = new Camera(graphics.GraphicsDevice.Viewport);
        basicEffect = new BasicEffect(graphics.GraphicsDevice);
        basicEffect.VertexColorEnabled = true;
        basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height, 0, 0, 1);

        base.Initialize();
    }
    ...
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, camera.GetViewMatrix());
        spriteBatch.Draw(avatar.Texture, avatar.DrawPosition, new Rectangle(0, 0, avatar.Texture.Width, avatar.Texture.Height), Color.White, avatar.Rotation, avatar.Origin, avatar.Scale, avatar.FlipHorizontally ? SpriteEffects.FlipHorizontally : SpriteEffects.None, 0);
        spriteBatch.End();

        basicEffect.CurrentTechnique.Passes[0].Apply();
        VertexPositionColor[] vertices = new VertexPositionColor[avatar.Vertices.Length + 1];
        Matrix m = MakeAffineTransform(avatar);
        for (int i = 0; i < avatar.Vertices.Length; i++)
        {
            vertices[i] = new VertexPositionColor(Vector3.Transform(new Vector3(Vector2.Transform(avatar.Vertices[i], m), 0), camera.GetViewMatrix()), Color.Black);
            Console.WriteLine(vertices[i]);
        }
        vertices[vertices.Length - 1] = vertices[0];
        graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, vertices, 0, vertices.Length - 1);

        base.Draw(gameTime);
    }
    ...
}

它工作得非常好!这实际上翻转了原点,使其位于左下角,并翻转了y轴,使增加的值向上,减少的值向下。相机可以是一个很好的基础,并且可以轻松更新(例如,如果您想让它跟随屏幕上的某些东西),以便您可以将世界坐标传递给它(原点在左下角),它将返回屏幕坐标。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接