Bicubic's picture

Pixel perfect sprites with perspective projection

Today's adventure in OpenGL land: Drawing 2d sprites.

From glancing at a few samples and digging around, I found how an orthographic projection is set:

GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
                int[] viewPort = new int[4];
                GL.GetInteger(GetPName.Viewport, viewPort);
 
//Proj
GL.LoadIdentity();
GL.MatrixMode(MatrixMode.Projection);
 
 
//WorldView
GL.LoadIdentity();
GL.Ortho(viewPort[0], viewPort[0] + viewPort[2], viewPort[1] + viewPort[3], viewPort[1], -100, 100);
GL.MatrixMode(MatrixMode.Modelview);

I wanted a perspective projection that gives the same results but allows perspective-correct texturing on geometry. After some hacking around, I came to this:

//Projection
float aspect = ClientRectangle.Width / (float)ClientRectangle.Height;
Matrix4 p = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspect, 0.01f, 10000f);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref p);
 
//WorldView
float z = ClientRectangle.Height * 1.208f;                        
p = Matrix4.CreateTranslation(-ClientRectangle.Width / 2f, -ClientRectangle.Height / 2f, 0);
p *= Matrix4.LookAt(new Vector3(0, 0, z), new Vector3(0, 0, -1), Vector3.UnitY);
p *= Matrix4.Scale(1f, -1f, 1f);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref p);

It works, but I don't entirely understand why it works. I wrote it knowing that it'll come down to calculating the distance between camera and geometry 'z' but I have literally guessed the magic number 1.208 from a pair of tests and am still amazed that this produces pixel perfect results. If anyone understand this, it'll be interesting to hear the full explanation. Everyone else feel free to use it, it seems to work for all resolutions and aspect ratios.

Edit: And in a tragic turn of events, it turns out that its not quite pixel perfect. Seemingly its now pixel perfect at 1.207. My quest to solve this properly continues!