I am working in iOS and OpenGL ES 2.0. Through trial and error I've figured out a frustum to where at a specific z depth pixels drawn are 1 to 1 with my source textures. So 1 pixel in my texture is 1 pixel on the screen. For 2d games this is good. Of course it means that I also factor in things like the size of the quad and the size of the texture.
For example if my sprite is a quad 32x32 pixels. The quad size is 3.2 units wide and tall. And the texcoords are 32 / the size of the texture wide and tall.
Then the frustum is:
matrixFrustum(-(float)backingWidth/frustumScale,(float)backingWidth/frustumScale,
-(float)backingHeight/frustumScale, (float)backingHeight/frustumScale, 40, 1000, mProjection);
Where frustumScale is 800 for a retina screen. Then at a distance of 800 from camera the sprite is pixel for pixel the same as photoshop.
For 3d games sometimes I still want to be able to do this. But depending on the scene I sometimes need the FOV to be different things.
I'm looking for a way to figure out what Z depth will achieve this same pixel unity for a given FOV.
For this my mProjection is set using:
matrixPerspective(cameraFOV, near, far, (float)backingWidth / (float)backingHeight, mProjection);
With testing I found that at an FOV of 45.0 a Z of 38.5 is very close to pixel unity. And at an FOV of 30.0 a Z of 59.5 is about right. But how can I calculate a value that is spot on?
Here's my matrixPerspecitve code:
void matrixPerspective(float angle, float near, float far, float aspect, mat4 m)
{
//float size = near * tanf(angle / 360.0 * M_PI);
float size = near * tanf(degreesToRadians(angle) / 2.0);
float left = -size, right = size, bottom = -size / aspect, top = size / aspect;
// Unused values in perspective formula.
m[1] = m[2] = m[3] = m[4] = 0;
m[6] = m[7] = m[12] = m[13] = m[15] = 0;
// Perspective formula.
m[0] = 2 * near / (right - left);
m[5] = 2 * near / (top - bottom);
m[8] = (right + left) / (right - left);
m[9] = (top + bottom) / (top - bottom);
m[10] = -(far + near) / (far - near);
m[11] = -1;
m[14] = -(2 * far * near) / (far - near);
}
And my mView is set using:
lookAtMatrix(cameraPos, camLookAt, camUpVector, mView);
* UPDATE *
I'm going to leave this here in case anyone has a different solution, can explain how they do it, or why this works.
This is what I figured out. In my system I use a 10th scale unit to pixels on non-retina displays and a 20th scale on retina displays. The iPhone is 640 pixels wide on retina and 320 pixels wide on non-retina (obsolete). So if I want something to be the full screen width I divide by 20 to get the OpenGL unit width. Then divide that by 2 to get the left and right unit position. Something 32 units wide centered on the screen goes from -16 to +16. Believe it or not I have an excel spreadsheet do all this math for me and output all the vertex data for my sprite sheet.
It's an arbitrary thing I made up to do .1 units = 1 non-retina pixel or 2 retina pixels. I could have made it .01 units = 2 pixels and someday I might switch to that. But for now it's the other. So the width of the screen in units is 32.0, and that means the left most pixel is at -16.0 and the right most is at 16.0.
After messing a bit I figured out that if I take the [0] value of an identity modelViewProjection matrix and multiply it by 16 I get the depth required to get 1:1 pixels. I don't know why. I don't know if the 16 is related to the screen size or just a lucky guess. But I did a test where I placed a sprite at that calculated depth and varied the FOV through all the valid values and the object stays steady on screen with 1:1 pixels. So now I'm just calculating the unityDepth that way.
If someone gives me a better answer I'll checkmark it.