2
\$\begingroup\$

I'm working on a vector map renderer. I want to calculate the far plane for a protective transformation, based on the yaw, pitch, roll and height of the camera above the map. Yaw (looking to the left and right), pitch (the direction you also have on Google Maps e.g.) and roll (rotation around Y axis in this case) are limited to 30°.

I recorded a video here of the movements users are able to do: https://youtu.be/NejhvULvhys

As you can see there is some clipping, which demonstrates that the calculation of the far distance field is not correct yet. Though, please note that I do have a working version if you only use pitch and roll. So this question is about: How do I calculate the far distance plane if I not only want pitch and roll?

I prepared a geogebra drawing to explain the current calculations. It is not that simple because there are not that many right-angles: https://www.geogebra.org/m/cvhtwhng

enter image description here

In that diagram:

  • height of the blue triangle is the height of the camera.
  • Roll is not really relevant as it does not influence the far distance plane.
  • Yaw and pitch are equal to "pitch" in the diagram. Note that this is only a 2d cut of the real situation which is 3d. So the geogebra diagram is a simplification.

Finally, I have some code to share.

I have tried a few method to calculate the far distance plane based on the current pitch and yaw. Unfortunately, I have not gained confidence whether the correct is calculated. I am certain that the geogebra calculations are correct as they work well for the "only pitch" case. Though, the combination of both seems more involved.

Let me know if something is unclear!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Consider embedding an image of your diagram and the text of your code samples in the body of the question itself, to ensure they can't rot, and get them in front of more eyeballs (many visitors to questions don't click links, and just leave if the code/images are not immediately visible — so embedding this info helps attract more potential answers) \$\endgroup\$
    – DMGregory
    Sep 25, 2023 at 22:17
  • \$\begingroup\$ I maybe have an idea: Instead of using fovy for the pitch and fovx for the yaw, I maybe can calculate a "Diagonal Field of View" and then use the corresponding angle which is calcualted from pitch and yaw :thinking_face: \$\endgroup\$
    – maxammann
    Sep 26, 2023 at 21:35

1 Answer 1

1
\$\begingroup\$

One tactic we can use here is to fire a ray through each corner of the screen and take the farthest intersection with the map plane.

Based on your camera's field of view and aspect ratio, you can compute 4 vectors in camera space that point to the corners of the screen, with a z component of 1:

ray.y = ± tan(fov / 2.0)
ray.x = ± aspect * ray.y
ray.z = 1 (or -1 if that's "forward" for your coordinate system)

These only need to change when your window size or camera FoV change, not on every rotation.

When the camera rotates, you can transform these rays into world space using the camera transform matrix. (Or construct them directly by adding together camera.forward + tan(fov/2.0) * (±camera.up ± aspect * camera.right) )

Now you can use a ray vs plane intersection function to get the time t a particle starting at the camera and moving with this ray as its velocity would hit the map plane. The greatest of these 4 t values is your far plane depth (and the least is your near plane, if your map is 2D with no elevation / raised overlays).

This isn't an expensive physics raycast, just a very simple subtraction, dot product, and division:

float GetIntersectionTime(Vector3 rayOrigin, Vector3 rayDirecton, Vector3 planeOrigin, Vector3 planeNormal) {
    float distance = Vector3.Dot(planeOrigin - rayOrigin, planeNormal);

    float approachSpeed = Vector3.Dot(rayDirection, planeNormal);

    // Returns an infinity if the ray is
    // parallel to the plane and never intersects,
    // or NaN if the ray is in the plane
    // and intersects everywhere.
    return distance / approachSpeed;

    // Otherwise returns t such that
    // rayOrigin + t * rayDirection
    // is in the plane, to within rounding error.
}

Note that the distance term can be pre-calculated as it only changes when the camera translates, and is constant for all rays and all rotations from a given camera position. If your plane is axis-aligned, then the dot product is just taking the e.g. z component, simplifying even further.

\$\endgroup\$
9
  • \$\begingroup\$ Thanks for the response. I'll implement and test this method. I think I should have gone that way earlier. I had it in mind actually, but was convinced I could do it on the way I proposed. \$\endgroup\$
    – maxammann
    Sep 27, 2023 at 13:58
  • \$\begingroup\$ I just went through your answer in more detail. I'm not sure that the "intersection time" is the far plane depth. The distance from the camera to one of the 4 intersection points with the map plane. For example let's assume the camera looks straight down. The far plane should be at the height of the camera. However, the distance t is larger than the height of the camera. \$\endgroup\$
    – maxammann
    Sep 28, 2023 at 20:00
  • \$\begingroup\$ Also note that the camera is rotated around a point on the map, instead of the camera position. So yaw and pitch rotate around a point on the map. \$\endgroup\$
    – maxammann
    Sep 28, 2023 at 20:01
  • \$\begingroup\$ If you construct the ray so its component along the camera's forward axis is 1.0 as shown above, then the intersection time is interchangeable with distance. After t seconds the particle has moved t steps along the ray which means 1.0 * t units along the camera's axis — that exactly matches the depth measurement you need for placing the far plane. If you're getting another depth, you may have normalized the ray direction unnecessarily, so its component parallel to the camera's axis is less than 1.0. In that case, you can still recover the depth by multiplying t by the length of that component. \$\endgroup\$
    – DMGregory
    Sep 28, 2023 at 20:05
  • \$\begingroup\$ Alright I think I got now the idea. I missed the fact that ray.z = 1. Not everything is clear yet, but I'm continuing to implement this. Right now I'm only getting very small values for my t values. Integrating things is always a little difficult. WIP: gist.github.com/maxammann/a50b0ce5b839d0ecb24caeacb1846dc9 \$\endgroup\$
    – maxammann
    Sep 28, 2023 at 21:03

You must log in to answer this question.

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