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);
shader.Parameters["World"].SetValue(rotM);
shader.CurrentTechnique.Passes[0].Apply();
sp.Draw(BackgroundTexture, Vector2.Zero, PositionRec, BackgroundColor * alpha);
rotM = Matrix.Identity;
Vertex shader:
float4x4 World;
float4x4 Projection;
struct VertexShaderInput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output;
float4 worldPosition = mul(input.Position, World);
output.Position = mul(worldPosition, Projection);
output.TexCoord= input.TexCoord;
return output;
}
pixel shader:
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?
Thanks in advance, Roger
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
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. \$\endgroup\$