# Spritebatch Vertex shader world matrix change after each draw

I have a spritebatch where I draw some textures transformed in 3d space.

The easy way was just Begin with the transformation matrix, draw, and end it right after.

But that is silly, so I'm trying with a vertex shader.

My results however aren't the desired ones. After the first draw&transformation, the rest of the textures stop apprearing, that hints me that I cant change the world matrix between draws.

How should I approach this?

I set the projection matrix like this, before the sp.Begin:

shader.Parameters["Projection"].SetValue(Matrix.CreateOrthographicOffCenter(
0, sp.GraphicsDevice.Viewport.Width,
sp.GraphicsDevice.Viewport.Height, 0, 0, 1)
);
sp.Begin(SpriteSortMode.Deferred, null, null, null, null, shader);


afterwards:

    rotM = Matrix.CreateFromYawPitchRoll(MathHelper.ToRadians(startRotations.Y), MathHelper.ToRadians(startRotations.X), MathHelper.ToRadians(startRotations.Z))
//scale after rotation so I can destroy the z value after rotation
* Matrix.CreateScale(endScale.X, endScale.Y, 0)
* Matrix.CreateTranslation((Origin.X + PositionRec.X + Offset.X) * endScale.X, (Origin.Y + PositionRec.Y + Offset.Y) * endScale.Y, 0);

sp.Draw(BackgroundTexture, Vector2.Zero, PositionRec, BackgroundColor * alpha);
rotM = Matrix.Identity;


float4x4 World;
float4x4 Projection;

{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;

};

{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;

};

{

float4 worldPosition = mul(input.Position, World);

output.Position = mul(worldPosition, Projection);

output.TexCoord= input.TexCoord;

return output;
}


float4 PixelShaderFunction(VertexShaderOutput input, bool front:  VFACE) : COLOR0
{
float4 Color = tex2D(ColorMapSampler, input.TexCoord);
return Color;

}


Why is this happening? It really looks like that the world matrix that I use is only changed the first time, why?

EDIT:

Ok since I wanted to reuse spritebatch to render quads in 3d, I had to addapt 2 classes in monogame to get this to work.

in spritebatch.cs i had to include some new methods:

     public void Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, Matrix transform)
{

Draw(texture, position, sourceRectangle, color, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, transform);
}

public void Draw(Texture2D texture,
Vector2 position,
Rectangle? sourceRectangle,
Color color,
float rotation,
Vector2 origin,
float scale,
SpriteEffects effect,
float depth,
Matrix transform)
{
CheckValid(texture);

var w = texture.Width * scale;
var h = texture.Height * scale;
if (sourceRectangle.HasValue)
{
w = sourceRectangle.Value.Width * scale;
h = sourceRectangle.Value.Height * scale;
}

DrawInternal(texture,
new Vector4(position.X, position.Y, w, h),
sourceRectangle,
color,
rotation,
origin * scale,
effect,
depth,
transform);
}

internal void DrawInternal(Texture2D texture,
Vector4 destinationRectangle,
Rectangle? sourceRectangle,
Color color,
float rotation,
Vector2 origin,
SpriteEffects effect,
float depth,
Matrix transform)
{
var item = _batcher.CreateBatchItem();

item.Depth = depth;
item.Texture = texture;

if (sourceRectangle.HasValue)
{
_tempRect = sourceRectangle.Value;
}
else
{
_tempRect.X = 0;
_tempRect.Y = 0;
_tempRect.Width = texture.Width;
_tempRect.Height = texture.Height;
}

_texCoordTL.X = _tempRect.X / (float)texture.Width;
_texCoordTL.Y = _tempRect.Y / (float)texture.Height;
_texCoordBR.X = (_tempRect.X + _tempRect.Width) / (float)texture.Width;
_texCoordBR.Y = (_tempRect.Y + _tempRect.Height) / (float)texture.Height;

if ((effect & SpriteEffects.FlipVertically) != 0)
{
var temp = _texCoordBR.Y;
_texCoordBR.Y = _texCoordTL.Y;
_texCoordTL.Y = temp;
}
if ((effect & SpriteEffects.FlipHorizontally) != 0)
{
var temp = _texCoordBR.X;
_texCoordBR.X = _texCoordTL.X;
_texCoordTL.X = temp;
}

item.Set(destinationRectangle.X,
destinationRectangle.Y,
-origin.X,
-origin.Y,
destinationRectangle.Z,
destinationRectangle.W,
(float)Math.Sin(rotation),
(float)Math.Cos(rotation),
color,
_texCoordTL,
_texCoordBR, transform);

if (_sortMode == SpriteSortMode.Immediate)
_batcher.DrawBatch(_sortMode);
}


in SpritebatchItem.cs

        public void Set(float x, float y, float dx, float dy, float w, float h, float sin, float cos, Color color, Vector2 texCoordTL, Vector2 texCoordBR, Matrix transform)
{
// TODO, Should we be just assigning the Depth Value to Z?
// According to http://blogs.msdn.com/b/shawnhar/archive/2011/01/12/spritebatch-billboards-in-a-3d-world.aspx
// We do.
vertexTL.Position.X = x + dx * cos - dy * sin;
vertexTL.Position.Y = y + dx * sin + dy * cos;
vertexTL.Position.Z = Depth;
vertexTL.Position = Vector3.Transform(vertexTL.Position, transform);
vertexTL.Color = color;
vertexTL.TextureCoordinate.X = texCoordTL.X;
vertexTL.TextureCoordinate.Y = texCoordTL.Y;

vertexTR.Position.X = x + (dx + w) * cos - dy * sin;
vertexTR.Position.Y = y + (dx + w) * sin + dy * cos;
vertexTR.Position.Z = Depth;
vertexTR.Position = Vector3.Transform(vertexTR.Position, transform);
vertexTR.Color = color;
vertexTR.TextureCoordinate.X = texCoordBR.X;
vertexTR.TextureCoordinate.Y = texCoordTL.Y;

vertexBL.Position.X = x + dx * cos - (dy + h) * sin;
vertexBL.Position.Y = y + dx * sin + (dy + h) * cos;
vertexBL.Position.Z = Depth;
vertexBL.Position = Vector3.Transform(vertexBL.Position, transform);
vertexBL.Color = color;
vertexBL.TextureCoordinate.X = texCoordTL.X;
vertexBL.TextureCoordinate.Y = texCoordBR.Y;

vertexBR.Position.X = x + (dx + w) * cos - (dy + h) * sin;
vertexBR.Position.Y = y + (dx + w) * sin + (dy + h) * cos;
vertexBR.Position.Z = Depth;
vertexBR.Position = Vector3.Transform(vertexBR.Position, transform);
vertexBR.Color = color;
vertexBR.TextureCoordinate.X = texCoordBR.X;
vertexBR.TextureCoordinate.Y = texCoordBR.Y;
}


I don't know about the performance, are these calculations costly?

What do you guys think?

I think the rotation isn't working properly, going to investigate further

• gamedev.stackexchange.com/questions/50369/… so I cant add new vertex information? you guys know any way to go around that? – Roger Martins Dec 17 '13 at 8:34
• The edit code could be altered to simply accept the world matrix you were using previously. Then just create the vertex as it was before and transform the position at the end using the world matrix. This way it will work for different purposes later. – OriginalDaemon Dec 17 '13 at 12:30
• that's what i'm doing, I just added the transform matrix to the draw/draw internal, and after the vertex information is set I transform the result vertexTL.Position = Vector3.Transform(vertexTL.Position, transform); I actualy don't need to change the world matrix because spritebatch allready does it for me, but yeah if I want a moving camera or other kinds of projection that's a good idea. – Roger Martins Dec 17 '13 at 12:44
• My bad, thought the effect was being done with the trig bits. – OriginalDaemon Dec 17 '13 at 12:48

SpriteBatch.draw(...) probably isn't doing what you think it is. It doesn't immediately draw. This is supposed to be the case - especially since you are using deferred mode.

Making draw calls can be expensive so you want to do as few of them as possible. So for sprites, which are 4 vertices, you want to "batch" them. The XNA solution is the "SpriteBatch".

SpriteBatch.draw(...) actually just builds an internal buffer of sprites to draw later, with texture information and transformed vertices, and then, at an appropriate time, it draws them. This appropriate time is determined by the modes set in spriteBatch.begin().

Deferred mode basically tells sprite batch not to draw anything until the very end so the order can be sorted for all sprites. So I believe it won't actually make any real draw calls until SpriteBatch.end().

This means that your world matrix is actually only set before the last SpriteBatch.draw() call and thus is always the matrix you construct for the last sprite.

Regardless, the sprite batch will use the position information supplied in the SpriteBatch.draw() call to create a rectangle in the buffer which is already transformed correctly into world coordinates. This means the vertex is already in world coordinates when it reaches the vertex shader and shouldn't be transformed again with the world matrix. You actually just want to apply your view*projection matrix transformation in the vertex shader (and view is probably Matrix.Identity so can be ignored).

shader.Parameters["World"].SetValue(Matrix.Identity);