# Infinite Seamless Background HLSL Shader in Monogame doesn't work with non square textures

I'm trying to write a shader that draws a seamless background infinitely in Monogame.

sampler TextureSampler : register(s0);
float2 ViewportSize;
float4x4 ScrollMatrix;
struct VertexToPixel {
float4 Position : SV_Position0;
float4 TexCoord : TEXCOORD0;
float4 Color : COLOR0;
};
VertexToPixel SpriteVertexShader(float4 color : COLOR0, float4 texCoord : TEXCOORD0, float4 position : POSITION0) {
VertexToPixel Output = (VertexToPixel)0;

// Half pixel offset for correct texel centering.
position.xy -= 0.5;

position.xy = position.xy / ViewportSize;
position.xy *= float2(2, -2);
position.xy -= float2(1, -1);

// Transform our texture coordinates to account for camera
texCoord = mul(texCoord, ScrollMatrix);

//pass position and color to PS
Output.Color = color;
Output.Position = position;
Output.TexCoord = texCoord;

return Output;
}

technique SpriteBatch {
pass {
}
}


I'm using Monogame.Extended so I can use their Camera2D class. I have this function to draw my texture:

public static void drawSeamlessBackground(SpriteBatch s, Texture2D t, GraphicsDevice gd, float parallax, Camera2D cam) {
//TODO: Make this work with non square textures.
Vector2 textureSize = new Vector2(t.Width, t.Height);
Rectangle view = gd.Viewport.Bounds;
Matrix m = Matrix.CreateTranslation(new Vector3(-cam.Origin / textureSize, 0.0f)) *
Matrix.CreateScale(1f / cam.Zoom) *
Matrix.CreateRotationZ(-cam.Rotation) *
Matrix.CreateTranslation(new Vector3(cam.Origin / textureSize, 0.0f)) *
Matrix.CreateTranslation(new Vector3((cam.Position * parallax) / textureSize, 0.0f));

//Normally this next line would not be here since the viewport doesn't usually change.

s.Draw(t, new Vector2(0, 0), view, Color.White);
s.End();
}


This whole thing works fine for square textures, but when the width and the height are different, there's distortion that happens when I rotate my camera.

In the next image, there are two textures, one that is square and one that is a rectangle.

Here is what it looks like with no rotation:

Here is what it looks like with some rotation:

The red texture has square dimensions (500x500). The white texture has rectangular dimensions (1280x720).

I'm sure this is a simple fix, but I can't figure it out.

• The code is based on: david-gouveia.com/portfolio/… Here are the textures: imgur.com/a/n9WrG – Apos May 29 '17 at 20:13
• Looks like the scale is being applied to the matrix on the wrong side of the rotation. (A rotation followed by a scale makes a shear. A scale followed by a rotation keeps the perpendiculars you want) – DMGregory May 29 '17 at 22:07
• I tried to use only this instead: Matrix m = Matrix.CreateRotationZ(-cam.Rotation); But it still does the shear effect. – Apos May 29 '17 at 22:16

After working on this for a while, I found a solution. Just like someone else said in a comment, a shear happens when scaling is done before a rotation. It turns out my texCoord had the wrong ratio. It needed to use the texture's ratio. The shader is perfectly fine, but I need to apply the texture's ratio to my matrix before the rotation.

public static void drawSeamlessBackground(SpriteBatch s, Texture2D t, GraphicsDevice gd, float parallax, Camera2D cam) {
Vector2 textureSize = new Vector2(t.Width, t.Height);
Rectangle view = gd.Viewport.Bounds;

Vector2 textureRatio = new Vector2(t.Width / t.Height, 1);
if (t.Width < t.Height) {
textureRatio = new Vector2(1, t.Height / t.Width);
}

Matrix m = Matrix.CreateTranslation(new Vector3(-cam.Origin / textureSize, 0.0f)) *
Matrix.CreateScale(1f / cam.Zoom) *
Matrix.CreateScale(textureRatio.X, textureRatio.Y, 1) *
Matrix.CreateRotationZ(-cam.Rotation) *
Matrix.CreateScale(1f / textureRatio.X, 1f /textureRatio.Y, 1) *
Matrix.CreateTranslation(new Vector3(cam.Origin / textureSize, 0.0f)) *
Matrix.CreateTranslation(new Vector3((cam.Position * parallax) / textureSize, 0.0f));