Hemisphere Projection

I came across the following segment of code that is supposed to project an image on a hemisphere:

void main(void)
{
vColor = aColor;
vec4 pos = uModelViewMatrix * vertex;
float lenxy = length(pos.xy);
if(lenxy != 0.0)
{
float phi = atan(lenxy, -pos.z);
pos.xy = normalize(pos.xy);
// pos.xy is equal to (cos theta, sin theta)
float r = phi / (PI/2.);
// radius is less than or equal to 1.
pos.xy *= r;
}
gl_Position = uProjectionMatrix * pos;
}


The followings are what I need clarifications for:

1. Why would r be less than or equal to 1 by dividing phi by PI/2.?
2. Why are we multiplying pos.xy with r?
3. And, how does normalizing pos.xy give us cos theta and sin theta?

The followings are the accompanying images:

Thanks.

The value r will be between -1 and 1 because the atan() function returns a value between -π/2 and π/2. (Actually, that's probably the atan2() function since it takes 2 parameters.)

If you think back to basic trigonometry, sine and cosine functions are defined by the ratios of the radius of the circle to the length of either the horizontal or vertical sides of the right triangle they form. Or to put it more simply:

sine(theta) = opposite / hypotenuse

(The images in the Wikipedia article on sine explain it pretty well.)

In a unit circle, the hypotenuse is always a length of 1, by definition. So by normalizing the vector pos, its length becomes 1 and the x and y components of the vector become the lengths of the sides of the right triangle used to define sine and cosine (the opposite and adjacent sides in the above definitions). Like this:

As mentioned, sine(theta) = opposite / hypotenuse. Since this is a unit circle, the hypotenuse of that triangle has a length of 1. So sine(theta) = opposite / 1.0 or just opposite. And opposite is a line from normalized (pos.x, pos.y) straight down to the y axis. So the length of opposite is (normalized) pos.y. Likewise, cosine(theta) = adjacent / hypotenuse, which is just adjacent, which has a length of (normalized) pos.x.

So if r is now a value between -1 and 1, and you multiply the normalized pos by it, you'll get a vector of length r pointing in the direction of pos.

• I've added an image which I hope makes it clearer. As for why dividing phi by PI/2 makes the result less than or equal to one - that's what I explained in the very first sentence. The atan() function returns a value between -π/2 and +π/2. So if you divide that value by π/2, the result will be somewhere between -1.0 and 1.0. – user1118321 Oct 27 '16 at 3:53
• I should clarify that the atan() function returns the arctangent of its input(s). – user1118321 Oct 27 '16 at 3:55
• I believe that will flip the image horizontally. – user1118321 Oct 27 '16 at 4:04
• Let's ignore the sign for a moment; here's what I find unsettling: given the 2nd image above, shouldn't we be using arccos to find phi instead of atan? The angle is subtended by the hypothenuse (lengthXY) and the adjacent (pos.z); so, shouldn't it rather be using acos(lengthXY, -pos.z)? – Unheilig Oct 27 '16 at 7:08
• You use atan2 to get the sign. It's not possible to get the sign with anything other than atan2. The sign is very important so that your resulting vector points in the right direction and there's no acos2 or asin2. See stackoverflow.com/questions/29094261/… – Bjorn Tipling Oct 27 '16 at 13:52