Having matrix problems writing custom primitive/sprite batch

Hey,

I am writing a replacement for the SpriteBatch that also handles lines and such using the DrawPrimitives methods (and handles all the indices and such), for use in a Windows 2D game engine.

Everything is going great, but I’m having real difficulty nailing down the matrices so that EVERYTHING is perfect.

The following is the closest I have come to spot on with the matrices:

    World = Matrix.CreateTranslation(0.5f, 0.5f, 0);
    View = Matrix.CreateLookAt(
          new Vector3(0.0f, 0.0f, 1.0f),
          Vector3.Zero,
          Vector3.Up
        );

    Projection = Matrix.CreateOrthographicOffCenter(
          0,             //Left
          Width,  //Right
          Height, //Bottom
          0,             //Top
          0f,            //zNearPlane
          1f             //zFarPlane
        );

Assigning these matrix values to an effect works great for the drawing of lines, however when using the same effect to draw a sprite with a textured quad I get color bleeding from whatever pixel a given pixel is next to on the texture.

I worked out that it had something to with the World matrix being declared not using a whole number, however if I use:

    World = Matrix.CreateTranslation(1, 1, 0);

Sprites no longer have color bleeding, but if I draw something to 0,0 on the screen it is 1px to the right of or below the top left pixel.

Also, if I use

    World = Matrix.CreateTranslation(0, 0, 0);

Everything is fine except that positions are now no longer 0 based which I don’t want.

Can someone out there please help?

Thanks

In which order do you multiply your matrices?
Are you doing world * view * proj?

Hi,

At the moment I’m assigning them straight to an AlphaTestEffect World, View and Projection properties which is applied to any calls to the DrawIndexedUserPrimitives method. Is there something I am missing?

Without some code i cannot tell much more :slight_smile:
Are you using clamp for the addressUV in your sampler?

Hey, sorry for the late reply.

I’m using a linear clamp.

It would be a bit difficult to split out the code from my project and put it up here, so I’ve modified a test project that uses the stock SpriteBatch with the same AlphaTestEffect used by DrawUserIndexedPrimitives to draw lines and still illistrates the problems I am having with the matrices:

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace WithVertexAlpha
{
  /// <summary>
  /// This is the main type for your game.
  /// </summary>
  public class WithVertexAlphaGame : Game
  {
    Matrix worldMatrix;
    Matrix viewMatrix;
    Matrix projectionMatrix;

    BasicEffect basicEffect;
    VertexDeclaration vertexDeclaration;
    VertexPositionColor[] PointList;

    short[] lineListIndices;

    RasterizerState rasterizerState;

    GamePadState currentGamePadState;
    GamePadState lastGamePadState;

    KeyboardState currentKeyboardState;
    KeyboardState lastKeyboardState;

    GraphicsDeviceManager graphics;

    public WithVertexAlphaGame()
    {
      graphics = new GraphicsDeviceManager(this);
      graphics.IsFullScreen = false;
      graphics.PreferredBackBufferHeight = 1080;
      graphics.PreferredBackBufferWidth = 1920;
      Window.IsBorderless = true;

      Content.RootDirectory = "Content";
    }

   
    protected override void Initialize()
    {
      InitializeTransform();
      InitializeEffect();

      rasterizerState = new RasterizerState();
      rasterizerState.FillMode = FillMode.WireFrame;
      rasterizerState.CullMode = CullMode.None;

      base.Initialize();
    }


    SpriteBatch sb;
    AlphaTestEffect alphaTestEffect;
    Texture2D t2D;
    protected override void LoadContent()
    {
      sb = new SpriteBatch(GraphicsDevice);
      alphaTestEffect = new AlphaTestEffect(GraphicsDevice);
      alphaTestEffect.World = worldMatrix;
      alphaTestEffect.View = viewMatrix;
      alphaTestEffect.Projection = projectionMatrix;
      t2D = Content.Load<Texture2D>("TestTexture");

      // TODO: use this.Content to load your game content here
    }
    protected override void UnloadContent()
    {
      // TODO: Unload any non ContentManager content here
    }
    private void InitializeTransform()
    {
      worldMatrix = Matrix.CreateTranslation(0.5f, 0.5f, 0f);
      //worldMatrix = Matrix.Identity;

      viewMatrix = Matrix.CreateLookAt(
          new Vector3(0, 0, 1),
          new Vector3(0, 0, 0),//Vector3.Zero,
          Vector3.Up
          );

      projectionMatrix =
        Matrix.CreateOrthographicOffCenter(
          0,                                     //Left
          (float)GraphicsDevice.Viewport.Width,  //Right
          (float)GraphicsDevice.Viewport.Height , //Bottom
          0,                                     //Top
          0f,                               //zNearPlane
          1f                                       //zFarPlane
        );

    }

    AlphaTestEffect vertexEffectWithAlpha;
    private void InitializeEffect()
    {

      vertexDeclaration = new VertexDeclaration(new VertexElement[]
                {
                    new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
                    new VertexElement(12, VertexElementFormat.Color, VertexElementUsage.Color, 0)
                }
      );

      basicEffect = new BasicEffect(GraphicsDevice);
      basicEffect.VertexColorEnabled = true;

      basicEffect.World = worldMatrix;
      basicEffect.View = viewMatrix;
      basicEffect.Projection = projectionMatrix;
      
      
      Texture2D pixel = new Texture2D(GraphicsDevice, 2, 1, false, SurfaceFormat.Color);
      pixel.SetData(new[] { Color.White, Color.Transparent });
      vertexEffectWithAlpha = new AlphaTestEffect(GraphicsDevice);
      vertexEffectWithAlpha.Texture = pixel;
      vertexEffectWithAlpha.VertexColorEnabled = true;
      vertexEffectWithAlpha.World = worldMatrix;
      vertexEffectWithAlpha.View = viewMatrix;
      vertexEffectWithAlpha.Projection = projectionMatrix;

      //vertexEffectWithAlpha.;
      
    }
    protected override void Update(GameTime gameTime)
    {
      // Allows the game to exit
      if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();
#if WINDOWS
      if (Keyboard.GetState().IsKeyDown(Keys.Escape))
        this.Exit();
#endif

      base.Update(gameTime);
    }

    int FrameCount = 0;
    VertexPositionColorTexture[] PointListWithTexture;
    protected override void Draw(GameTime gameTime)
    {
      #region Vertices
      PointList = new VertexPositionColor[100];

      //Top left corner lines
      PointList[0] = new VertexPositionColor(new Vector3(1, 0, 1), Color.Red);
      PointList[1] = new VertexPositionColor(new Vector3(200, 0, 1), Color.Red);
      PointList[2] = new VertexPositionColor(new Vector3(0, 1, 1), Color.Green);
      PointList[3] = new VertexPositionColor(new Vector3(0, 200, 1), Color.Green);

      //Middle to edge stuff
      PointList[4] = new VertexPositionColor(new Vector3(200, 200, 1), Color.Black);
      PointList[5] = new VertexPositionColor(new Vector3(1520 - 1, 200, 1), Color.Red);// new Color(255, 0, 0, 50));// Color.White);
      PointList[6] = new VertexPositionColor(new Vector3(200, 1080 - 1, 1), Color.White);

      //Right edge stuff
      PointList[7] = new VertexPositionColor(new Vector3(1920 - 1, 0, 0), Color.Red);
      PointList[8] = new VertexPositionColor(new Vector3(1920 - 1, 150, 0), Color.Red);

      //Render target stuff
      PointList[9] = new VertexPositionColor(new Vector3(0, 0, 1), Color.Purple);
      PointList[10] = new VertexPositionColor(new Vector3(10, 10, 1), Color.Purple);
      PointList[11] = new VertexPositionColor(new Vector3(2, 400, 1), Color.Blue);
                                                            
      PointListWithTexture = new VertexPositionColorTexture[PointList.Length];
      for (int index = 0; index < PointListWithTexture.Length; index++ )
      {
        VertexPositionColor point = PointList[index];
        PointListWithTexture[index] = new VertexPositionColorTexture(point.Position, point.Color, new Vector2(1f - (point.Color.A * (1f / 255f)), 0));
      }

      #endregion
      
      #region Indices
      lineListIndices = new short[PointList.Length * 2];

      //Top left corner lines
      lineListIndices[0] = 0;
      lineListIndices[1] = 1;
      lineListIndices[2] = 2;
      lineListIndices[3] = 3;

      //Middle to edge stuff
      lineListIndices[4] = 4;
      lineListIndices[5] = 5;
      lineListIndices[6] = 4;
      lineListIndices[7] = 6;

      //Right edge
      lineListIndices[8] = 7;
      lineListIndices[9] = 8;

      lineListIndices[10] = 9;
      lineListIndices[11] = 10;
      lineListIndices[12] = 11;

      #endregion


      FrameCount++;
      //RenderTarget2D r2D = new RenderTarget2D(GraphicsDevice, 600, 600, false, SurfaceFormat.Color, DepthFormat.Depth24);
      //GraphicsDevice.SetRenderTarget(r2D);
      //GraphicsDevice.Clear(new Color(109, 0, 0, 100));
      //Draw_Vertices(PointList, PointList.Length, lineListIndices, 0, 5);
      //sb.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone, alphaTestEffect);
      //sb.Draw(t2D, new Rectangle(700, 198, 617, 107), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 0.5f);
      //sb.End();
      //GraphicsDevice.SetRenderTarget(null);
                                                                 
      GraphicsDevice.Clear(Color.SteelBlue);                             
      Draw_Vertices(PointList, PointList.Length, lineListIndices, 0, 6);
      sb.Begin(SpriteSortMode.Deferred, null, SamplerState.LinearClamp, null, null, vertexEffectWithAlpha);
      //sb.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone, alphaTestEffect);
      sb.Draw(t2D, new Rectangle(20, 20, 417, 107), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 0.5f);
      ////sb.Draw(r2D, new Rectangle(20, 20, 600, 600), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 1f);   
      sb.End();                                   
      //Draw_Vertices(PointListWithTexture, PointList.Length, lineListIndices, 0, 5);                              

      base.Draw(gameTime);
    }

    private void Draw_Vertices(VertexPositionColorTexture[] vertexData, int numVertices, short[] indices, int firstIndex, int primitiveCount)
    {
      foreach (EffectPass pass in vertexEffectWithAlpha.CurrentTechnique.Passes)
      {
        pass.Apply();
        GraphicsDevice.BlendState = BlendState.AlphaBlend;

        DrawLineList(vertexData, numVertices, indices, firstIndex, primitiveCount);
      }
    }
    private void Draw_Vertices(VertexPositionColor[] vertexData, int numVertices, short[] indices, int firstIndex, int primitiveCount)
    {
      foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
      {
        pass.Apply();
      
        DrawLineList(vertexData, numVertices, indices, firstIndex, primitiveCount);
      }
    }

    private void DrawLineList(VertexPositionColorTexture[] vertexData, int numVertices, short[] indices, int firstIndex, int primitiveCount)
    {
      GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColorTexture>(
        PrimitiveType.LineList,
        vertexData, //
        0,  // vertex buffer offset to add to each element of the index buffer
        numVertices,  // number of vertices in pointList
        indices, //  // the index buffer
        firstIndex,  // first index element to read
        primitiveCount   // number of primitives to draw
      );
    }
    private void DrawLineList(VertexPositionColor[] vertexData, int numVertices, short[] indices, int firstIndex, int primitiveCount)
    {
      GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
        PrimitiveType.LineList,
        vertexData, //
        0,  // vertex buffer offset to add to each element of the index buffer
        numVertices,  // number of vertices in pointList
        indices, //  // the index buffer
        firstIndex,  // first index element to read
        primitiveCount   // number of primitives to draw
      );
    }
  }
}

The test sprite is:

Again to clarify the problem I am looking to create Matrices for an effect so that primitives that have that effect applied to appear relative to the top left of the screen (0,0 would be the top left pixel and Viewport.Width-1,Viewport.Height-1 would be the bottom right pixel) and doesn’t blur the pixels of the texture when drawing quads with the effect.

Cheers!

Some platforms require the halfpixel offset and some don’t.Which platform do you have?
Windows/DX , WindowsGL?

What do you mean by that?

The CreateOrthographicOffCenter() gets the bounds of the viewport, so I think you need to use (0,width-1,Height-1,0).

I’m using Direct X.

I mean if I use Matrix,CreateTranslation(0, 0, 0) if I want to draw something to the top left pixel I have to draw something to 1,1 as 0,0 is to the left and above of the viewport.

You’re right, the Width Height stuff is just an oversight of messing around with the matrices.