views:

110

answers:

4

Hi folks,

I have a 256x256 texture in my view frustum that I need to move to a z-position where the texture is replicated on-screen at ACTUAL size.

What I have so far is:

    const float zForTrueScale = -((itemSize/2) / tanf(DEGREES_TO_RADIANS(fieldOfView/2)) ) * 2;     

where itemSize is the size of my texture in world space (2.0 units). This is to calculate what Z (adjacent) is when itemSize/2 (opposite) is 1.0. I thought this would work.

This formula shows a texture consistently too small by about 10% no matter what FOV I use.

Thanks

EDIT: I am moving around images in 3D space and need a seamless transition to the correct z-distance. I can't use orthagonal projection, it has to be in a view frustum.

+2  A: 

Without knowing too much about your situation, it's almost certainly easier to push an orthogonal projection onto the projection matrix stack, draw your texture and then pop it off again.

Pike65
No, I am moving around images in 3D space and need a seamless transition to the correct z-distance. I can't use orthagonal projection, it has to be in 3D.
Sam
Ah OK. Sorry - misunderstood. In that case: my trig is pretty rusty, but is the multiplication at the end needed?
Pike65
A: 

Although it would be slow, you could just use glDrawPixels() and be done with it.

...not if it's performance critical, though!

Drew Hall
A: 

Is your framebuffer square ?

If not, then you have a horizontal field of view and a vertical one. Are you using the correct one ? Also, how do you know for sure you want it to be 2.0 units in world coordinates ? If your end-goal is to map it to pixels, then you need to take the viewport into account, and work back from that.

I also agree with Pike65 that I don't get where the *2 is coming from.

Bahbar
The *2 is because the trig is using the length of the opposite, (half the item width), and then at the end I multiply it back out to get the full length. I could have just not halved the item width to begin with, but it read better to me at the time.Anyway, my question is, "how do I take the viewport into account and work back from that". My framebuffer is square.
Sam
I found that by adding 13.5/FOV to the end result gives me an accurate enough result for any FOV.. but I don't understand where 13.5 comes from, it's just a horrible magic number sat in my code at the moment.
Sam
For the trig: /2 makes sense to get half the item width. but tan() is half-item/z. There is no *2 involved there.What are your view/proj matrices looking like at the time of draw ? what quad do you pass in to draw the texture ?
Bahbar
Yes, apologies, you are right about the trig... I guess my code is more broken than I thought! My view frustum is setup with a square aspect ratio and the texture vertices are just -1.0 to 1.0 both ways. I am working with ES so using a triangle strip, just two triangles. Not doing anything to the model matrix, starting with a blank slate...
Sam
+5  A: 

You can do this slightly more easily than messing about with fields of view as the info you need is held directly in the projection matrix.

Thus given your projection matrix will look like this:

xScale     0               0                     0
0        yScale            0                     0
0          0     -(zf + zn)/(zf - zn)  -(2 * zf) / (zf-zn) 
0          0              -1                     0

Where z-Far (zf) and the z-Near (zn), xScale and yScale are known.

To choose the correct Z-Depth we'll start off with making sure w ends up as 1. This way when we do the divide by w it won't change anything.

Fortunately w is very easy to get at. It is simply the negative of the input z. Thus an input of -1 into z will return a w of 1.

We'll assume you are using a resolution of 1024x768 and that the texture you want at the correct scale is 256x256.

We'll also further assume that your rectangle is setup with top left at a position of -1, 1 and bottom right at 1, -1.

So lets plug these in and work out the z.

if we use the following:

-1, 1, -1, 1
1, -1, -1, 1

we will get out something as follows.

1 / xScale, -1 / yScale, someZThatIsn'tImportant, 1
-1 / xScale, 1 / yScale, someZThatIsn'tImportant, 1

The viewport transform transforms those values such that -1, 1 is 0, 0 and 1, -1 is 1024,768

So we can see that works by doing ((x + 1) / 2) * 1024 and ((1 - y) / 2) * 768

So if we assume an xScale of 3/4 and a yScale of 1 we can see that by plugging that in we'll get the following:

For top left:

x = -3/4
=> ((-3/4 + 1) / 2) * 1024
=> 128
y = 1
=> ((1 - 1) / 2) * 768
=> 0

For bottom right:

x = 3/4
=> ((3/4 + 1) / 2) * 1024
=> 896
y = -1
=> ((1 + 1) / 2) * 768
=> 768

You can thus see that we have a 768x768 pixel image centered in the screen. Obviously to get 256x256 we need to get the w to be 3 so that post w divide we have those coordinates a 3rd of the size ((xScale * 1024) / 256 should be equal to (yScale * 768) / 256 to get a square projection.

So if our final coordinates are as follows:

-1, 1, -3, 1
and
1, -1, -3, 1

we will get the following out (after w-divide):

-0.25, 0.333, unimportantZ, 1
and
0.25, -0.333, unimportantZ, 1

Run those through the screen equations above and we get

For top left:

x = -0.25
=> ((-0.25 + 1) / 2) * 1024
=> 384
y = 0.3333
=> ((1 - 0.3333) / 2) * 768
=> 256

For bottom right:

x = 0.25
=> ((0.25 + 1) / 2) * 1024
=> 640
y = -0.333
=> ((1 + 0.33333) / 2) * 768
=> 512

640 - 384 = 256
512 - 256 = 256

Thus we now have the final rect at the correct pixel size...

Goz
Wow, nice answer, thanks
Sam