Fix computation of camera distance
authorOwen W. Taylor <otaylor@redhat.com>
Thu, 22 Jan 2009 14:17:16 +0000 (14:17 +0000)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Thu, 22 Jan 2009 14:17:16 +0000 (14:17 +0000)
Compute the value of the camera distance as exactly half the xx
component of the projection matrix. The heuristically derived
value for 60 degrees was off by about 0.016%, causing noticeable
blurring, and other field of view angles which didn't have the
heuristic adjustment off by much more.

Signed-off-by: Emmanuele Bassi <ebassi@linux.intel.com>
clutter/cogl/gl/cogl.c

index aa0ec78..78b2a90 100644 (file)
@@ -723,37 +723,55 @@ cogl_setup_viewport (guint        width,
                     float z_far)
 {
   GLfloat z_camera;
+  GLfloat projection_matrix[16];
 
   GE( glViewport (0, 0, width, height) );
 
   cogl_perspective (fovy, aspect, z_near, z_far);
 
-  GE( glLoadIdentity () );
-
   /*
-   * camera distance from screen, 0.5 * tan (FOV)
+   * In theory, we can compute the camera distance from screen as:
+   *
+   *   0.5 * tan (FOV)
+   *
+   * However, it's better to compute the z_camera from our projection
+   * matrix so that we get a 1:1 mapping at the screen distance. Consider
+   * the upper-left corner of the screen. It has object coordinates
+   * (0,0,0), so by the transform below, ends up with eye coordinate
+   *
+   *   x_eye = x_object / width - 0.5 = - 0.5
+   *   y_eye = (height - y_object) / width - 0.5 = 0.5
+   *   z_eye = z_object / width - z_camera = - z_camera
+   *
+   * From cogl_perspective(), we know that the projection matrix has
+   * the form:
+   *
+   *  (x, 0,  0, 0)
+   *  (0, y,  0, 0)
+   *  (0, 0,  c, d)
+   *  (0, 0, -1, 0)
    *
-   * We have been having some problems with this; the theoretically correct
-   * value of 0.866025404f for the default 60 deg fovy angle happens to be
-   * touch to small in reality, which on full-screen stage with an actor of
-   * the same size results in about 1px on the left and top edges of the
-   * actor being offscreen. Perhaps more significantly, it also causes
-   * hinting artifacts when rendering text.
+   * Applied to the above, we get clip coordinates of
    *
-   * So for the default 60 deg angle we worked out that the value of 0.869
-   * is giving correct stretch and no noticeable artifacts on text. Seems
-   * good on all drivers too.
+   *  x_clip = x * (- 0.5)
+   *  y_clip = y * 0.5
+   *  w_clip = - 1 * (- z_camera) = z_camera
+   *
+   * Dividing through by w to get normalized device coordinates, we
+   * have, x_nd = x * 0.5 / z_camera, y_nd = - y * 0.5 / z_camera.
+   * The upper left corner of the screen has normalized device coordinates,
+   * (-1, 1), so to have the correct 1:1 mapping, we have to have:
+   *
+   *   z_camera = 0.5 * x = 0.5 * y
+   *
+   * If x != y, then we have a non-uniform aspect ration, and a 1:1 mapping
+   * doesn't make sense.
    */
-#define DEFAULT_Z_CAMERA 0.869f
-  z_camera = DEFAULT_Z_CAMERA;
 
+  cogl_get_projection_matrix (projection_matrix);
+  z_camera = 0.5 * projection_matrix[0];
 
-  if (fovy != 60.0)
-  {
-    float fovy_rad = (fovy * G_PI) / 180;
-
-    z_camera = ((sinf (fovy_rad) / cosf (fovy_rad)) / 2);
-  }
+  GE( glLoadIdentity () );
 
   GE( glTranslatef (-0.5f, -0.5f, -z_camera) );
   GE( glScalef ( 1.0f / width,