1
\$\begingroup\$

I have a standard 3D rendering pipeline. Let's say I have a camera looking down at a flat plane that represents the floor. (in my game, z = height, so this is a plane where z == 0), and for simplicities sake, the camera is tilted so that floor fills the entire screen (there's no horizon).

I have an unproject function, where I can provide a screen (x,y) pair and get back the point on the floor plane. Let's say I do this once for every y value on the screen. If I then project each of these points, I get the original (x,y) cordinates back, but I can also look at the w value for the projected point. This value is dependent on the height and pitch of the camera, and increases in a non linear way towards the top of the window.

My question is - can I calculate this w value per line without going through the unproject-project process for each line on the screen? The value is seemingly a function of camera height, yaw and screen y, so I feel it must be possible to obtain it directly from these three values via some alegbra.

\$\endgroup\$

2 Answers 2

0
\$\begingroup\$

I'm a shader newbie and not yet an expert on camera properties (still vague on w value, depth?) but I'm more comfortable with the math.

The distance from the camera to your point will be a function of your camera FOV, as well as of course its distance from the ground and rotation.

The Secant function is likely what you are looking for. See this animation. Secant(viewing angle) will return the distance to a point on a plane, as a ratio of the distance between the camera and the plane, where viewing angle is the rotation of the camera, plus one half of the FOV times the distance from the middle of the screen.

For example, with an aspect ratio of 1:1 (so I don't have to assume how your FOV is bound) if your camera has an FOV of 32 degrees, positioned 9 units away from the ground and rotated 40 degrees "upward":

An object at the top middle of the screen is9 * Secant((40 + 16) * π / 180) away from the camera. Halfway between the center and bottom of the screen, 9 * Secant((40 - 8) * π/ 180) units away.

Multiplying by pi / 180 is necessary to convert the angle from degrees to radians, so it will work in Secant. Many libraries have trig functions that operate on degrees as well.

To find objects to the left and right, first add your camera vertical rotation to the vertical FOV angle (which is the distance of your screen point away from the screen center multiplied by half your FOV) into one combined vertical angle. Then your horizontal angle is just like the vertical, but always positive since your camera is not rotated left or right. If the combined vertical angle is 'a', the horizontal angle is 'b', and according to the pythagorean theorem a^2 + b^2 = c^2 where c will be your total viewing angle which you feed into Secant (converting degrees to radians where necessary, and then multiplying the final product by the distance of your camera from the plane).

I hope this helps, and I didn't make any errors (which is very possible).

\$\endgroup\$
0
\$\begingroup\$

The w value is meaningless by itself. In a 3D graphics context, a vector with 4 components (x, y, z, w) usually contains homogeneous coordinates, which are a particular mathematical structure that happens to give us two things we want to do lots of, translation and perspective projection, via matrix multiplication.

Any time you have homogeneous coordinates — usually because you multiplied by some 4×4 matrix, such as your unproject function probably does — you should usually convert them to normal Cartesian coordinates before looking at the components.

  • To convert homogeneous to Cartesian, divide xyz by w: (x/w, y/w, z/w).

    This is often called the “perspective division” when discussing the 3D graphics pipeline, because it's the key step without which you cannot have perspective. But the perspective is really defined by the projection matrix that works on homogeneous coordinates; the division is just how you exit the world of homogeneous coordinates.

  • To convert Cartesian to homogeneous, add a component that is 1: (x, y, z, 1).

    You could also use any factor, like (2x, 2y, 2z, 2), and get the same result, but there is no point in doing that under typical circumstances.

Getting back to your problem: you don't need to compute w for each line. Perform the conversion from homogeneous to Cartesian, so that you have only x, y, and z; then you will find that y and z are predictable for each line, and that is all the information you need.

\$\endgroup\$
3
  • \$\begingroup\$ It's not meaniningless - when projecting a 3D point to the screen, the w value is a measure of the effect on scale that perspective has on on that point. I have reasons for wanting to know that value for every line on the screen. \$\endgroup\$
    – Tom Davies
    Jun 12, 2023 at 8:23
  • \$\begingroup\$ @TomDavies You can obtain a scale factor by unprojecting twice with x different by 1, then taking the difference of the resulting world x values. That's a robustly defined value that works no matter what you do with matrices; w is not. \$\endgroup\$
    – Kevin Reid
    Jun 12, 2023 at 14:05
  • \$\begingroup\$ hmm but then I need two unprojects per line! My question was about doing it without even having to do one per line. \$\endgroup\$
    – Tom Davies
    Jun 12, 2023 at 15:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .