3ddistanceprojectionheightmaplevel-of-detail

Computing the distance at which one cell of an axis aligned grid projects to one pixel on screen


Given an axis-aligned uniform grid in the X/Y plane (world space) in a 3D scene and a virtual camera looking at this grid from a certain position and direction. How can I calculate the distance I need to move the camera along its line of sight so that one of the grid cells is projected onto one pixel on the screen (fills one screen pixel)? The camera projection parameters (field of view, near and far clip plane) and the width and height of the screen are known.

This base distance is to be used to determine the level of detail for rendering/raycasting a heightmap (uniform grid of elevation values). The algorithm I am trying to implement is described in the paper "Maximum Mipmaps for Fast, Accurate, and Scalable Dynamic Height Field Rendering" by Tevs et al., 2008 (see Sect. 3.3). During raycasting, the current distance between the camera and an intersection point of the ray is compared with the base distance. If the current distance is smaller than the base distance, a higher level of detail is rendered (lower mipmap level). If the current distance is greater than the base distance, a lower level of detail is rendered (higher mipmap level).


Solution

  • You can depart from the "Screen Space Geometric Error" of your cells. That's a heuristic used to calculate the "error" of the perspective projection of a sphere on the screen. According to the heuristic, when the height of the sphere enclosing your geometry (in pixels) i.e., perspective projected on the screen, is greater than a tolerance then you need to increase the level of detail of your geometry.

    The following equation is used to calculate the geometric error of the sphere:

    err_s = (diam_s / dist_s) * yres / (2.0 * tan(fov/2.0))
    

    where err_s is the number of vertical pixels of the sphere projected on the screen, diam_s is the diameter of the sphere (it is constant per each sphere), dist_s is the euclidean distance from the center of the sphere to the camera, y_res is the vertical screen resolution in pixels and fov is the camera's field of view in radians.

    Since you already know the value of err_s (1 pixel), you can calculate dist_s by doing:

    dist_s = (diam_s / err_s) * yres / (2.0 * tan(fov/2.0))
    

    Again, that's an heuristic approximation (more accurate when sphere is projected right in the center of the screen), but works well.