Latest revision |
Your text |
Line 1: |
Line 1: |
| = OpenGL coordinate systems = | | = Investigations on update_camera() = |
|
| |
|
| In the Octave plotting backend, we find various OpenGL transformations. Some of the classic OpenGL transformation steps, as well as coordinate systems, are shown in the following picture: | | In the second part of <math display="inline">\rightarrow</math> axes::properties::update_camera () the view transformation "x_gl_mat1" and projection matrix "x_gl_mat2" are put together. The following images illustrate some of the properties of "x_gl_mat1". |
|
| |
|
| [[File:Octave_coordinate_systems.png|center|350px]]
| | == The Octave coordinate system == |
| | |
| = The Octave coordinate system = | |
|
| |
|
| In Octave a plot scene is defined by a "view point", a "camera target" and an "up vector". | | In Octave a plot scene is defined by a "view point", a "camera target" and an "up vector". |
|
| |
|
| [[File:Octave_view_point_setup_to_scale.png|center|750px]] | | [[File:Octave_view_point_setup_to_scale.png|center|750px]] |
|
| |
| = update_camera() =
| |
|
| |
| In the second part of <math display="inline">\rightarrow</math> "axes::properties::update_camera ()" the view transformation "x_gl_mat1" and projection matrix "x_gl_mat2" are put together. The following chapter illustrates some of the properties of "x_gl_mat1" and "x_gl_mat2".
| |
|
| |
|
| == The role of "x_gl_mat1" == | | == The role of "x_gl_mat1" == |
| === x_view ===
| |
|
| |
|
| The following section of code composes the matrix "x_view", which is a major subset of "x_gl_mat1". The matrix "x_gl_mat1" consists of multiple translations, scales and one rotation operation. The individual operation steps are shown in a picture below. | | The following section of code assembles the matrix "x_view", which is a subset of "x_gl_mat1". The matrix "x_gl_mat1" consists of multiple translations, scalings and one rotation operation. |
|
| |
|
| {{Code|Section of axes::properties::update_camera ()"|<syntaxhighlight lang="C" style="font-size:13px"> | | {{Code|Section of axes::properties::update_camera ()"|<syntaxhighlight lang="C" style="font-size:13px"> |
| // Unit length vector for direction of view "f" and up vector "UP" | | // Unit length vector for direction of view "f" |
| | // up vector "UP" scaled so that "s = f x UP" gets unit length |
| ColumnVector F (c_center), f (F), UP (c_upv); | | ColumnVector F (c_center), f (F), UP (c_upv); |
| normalize (f); | | normalize (f); |
| normalize (UP); | | normalize (UP); |
|
| |
| // Scale "UP" vector, so that norm(f x UP) becomes 1
| |
| if (std::abs (dot (f, UP)) > 1e-15)
| |
| {
| |
| double fa = 1 / sqrt (1 - f(2)*f(2));
| |
| scale (UP, fa, fa, fa);
| |
| }
| |
|
| |
|
| // Calculate the vector rejection UP onto f | | // Calculate the vector rejection UP onto f |
Line 57: |
Line 44: |
| </syntaxhighlight>}} | | </syntaxhighlight>}} |
|
| |
|
| === x_gl_mat1 ===
| | To visualize the matrix properties, the "x_gl_mat1" matrix is multiplied by the object coordinates. The plot box is now aligned with the Z-axis and the view point is at the origin <math display="inline">[0,0,0]</math>. The matrix transforms world coordinates into camera coordinates. The purple planes show the near and far clipping planes. |
|
| |
|
| To visualize the matrix properties, the "x_gl_mat1" matrix is multiplied by the object coordinates. After the transformation, the plot box is aligned with the Z-axis and the view point is at the origin <math display="inline">[0,0,0]</math>. The matrix transforms world coordinates into camera coordinates. The purple planes show the near and far clipping planes.
| | [[File:Octave_x_gl_mat1_setup.png|center|250px]] |
| | |
| [[File:Octave_x_gl_mat1_setup.png|center|300px]] | |
|
| |
|
| The individual translation, scaling and rotation operations of "x_gl_mat1", are shown in the following figure: | | The individual translation, scaling and rotation operations of "x_gl_mat1", are shown in the following figure: |
|
| |
|
| [[File:Octave_x_gl_mat1_steps.png|center|1100px]] | | [[File:Octave_x_gl_mat1_steps.png|center|1100px]] |
|
| |
| == The role of "x_gl_mat2" ==
| |
|
| |
| === Bounding box ===
| |
|
| |
| The matrix "x_gl_mat2" is composed of the sub matrices "x_viewport" and "x_projection". The purpose of these matrices is to fit the associated 2D image of the above transformation result into a "bounding box". The bounding box is defined as follows:
| |
|
| |
| * bb(0), bb(1): Position of the "viewport"
| |
| * bb(2), bb(3): Width and height of the "viewport"
| |
|
| |
| Hint: If you debug in "update_camera ()", you can print "bb":
| |
|
| |
| (gdb) print *bb.rep.data@bb.rep.len
| |
| (gdb) $1 = {72.79, 31.50, 434, 342.29}
| |
|
| |
| Compare the result with the output on the Octave prompt:
| |
|
| |
| hax = axes ();
| |
| get (hax, "position")
| |
| ans = 73.80 47.20 434.00 342.30
| |
| get (gcf, 'position')
| |
| ans = 22 300 560 420
| |
|
| |
| Where 420 - 342.30 - 31.5 + 1 = 47.20
| |
|
| |
| === x_projection ===
| |
|
| |
| In the following simplified code section the matrix "x_projection" is composed. It is used to normalize the image of the above transformation. For this purpose, the field of view (FOV) must be calculated:
| |
|
| |
| {{Code|Section of axes::properties::update_camera ()"|<syntaxhighlight lang="C" style="font-size:13px">
| |
| if (cameraviewanglemode_is ("auto"))
| |
| {
| |
| if ((bb(2)/bb(3)) > (xM/yM))
| |
| // When the image is scaled to the size of the bounding box,
| |
| // the height collides with the bounding box first. Therefore,
| |
| // the camera view angle is defined by the image height yM.
| |
| af = 1.0 / yM;
| |
| else
| |
| // The image width collides with the bounding box.
| |
| af = 1.0 / xM;
| |
|
| |
| // The view angle "v_angle", also called field of view "FOV",
| |
| // is formed by the hypotenuse and the adjacent side, which is given by
| |
| // the distance between the view point and the camera target "norm (F)".
| |
| // The ratio of the opposite side, given by "af", to the adjacent side in
| |
| // a right-angled triangle is the tangent of the view angle.
| |
| v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
| |
| cameraviewangle = v_angle;
| |
| }
| |
| else
| |
| v_angle = get_cameraviewangle ();
| |
|
| |
| // x_projection: identity "diag([1, 1, 1, 1])"
| |
| Matrix x_projection = xform_matrix ();
| |
| // Calculate backwards from the angle to the ratio. This step
| |
| // is necessary because "v_angle" can be set manually.
| |
| double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
| |
| // Normalize to one. Resulting coordinates are "normalized device coordinates".
| |
| scale (x_projection, pf, pf, 1);
| |
| </syntaxhighlight>}}
| |
|
| |
| === x_viewport ===
| |
|
| |
| "x_viewport" is a transformation used to place the previously "normalized" plot box in the center and to fit it tightly into the bounding box:
| |
|
| |
| {{Code|Section of axes::properties::update_camera ()"|<syntaxhighlight lang="C" style="font-size:13px">
| |
| double pix = 1;
| |
| if (autocam)
| |
| {
| |
| if ((bb(2)/bb(3)) > (xM/yM))
| |
| pix = bb(3);
| |
| else
| |
| pix = bb(2);
| |
| }
| |
| else
| |
| pix = (bb(2) < bb(3) ? bb(2) : bb(3));
| |
|
| |
| // x_viewport: identity "diag([1, 1, 1, 1])"
| |
| Matrix x_viewport = xform_matrix ();
| |
| // Move to the center of the bounding box inside the figure.
| |
| translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
| |
| // Scale either to width or height, to fit correctly into the bounding box
| |
| scale (x_viewport, pix, -pix, 1);
| |
|
| |
| x_gl_mat2 = x_viewport * x_projection;
| |
| </syntaxhighlight>}}
| |
|
| |
| Note: The matrix "x_gl_mat2" scales x, y. However the z-coordinate is not modified!
| |
|
| |
| = setup_opengl_transformation () =
| |
|
| |
| == OpenGL backend ==
| |
|
| |
| In the OpenGL backend, the view matrix, an orthographic matrix, and the viewport transform are used to transform the octave plot into the screen window.
| |
|
| |
| {{Code|Section of opengl_renderer::setup_opengl_transformation ()"|<syntaxhighlight lang="C" style="font-size:13px">
| |
|
| |
| Matrix x_zlim = props.get_transform_zlim ();
| |
|
| |
| xZ1 = x_zlim(0)-(x_zlim(1)-x_zlim(0))/2;
| |
| xZ2 = x_zlim(1)+(x_zlim(1)-x_zlim(0))/2;
| |
|
| |
| // Load x_gl_mat1 and x_gl_mat2
| |
| Matrix x_mat1 = props.get_opengl_matrix_1 ();
| |
| Matrix x_mat2 = props.get_opengl_matrix_2 ();
| |
|
| |
| m_glfcns.glMatrixMode (GL_MODELVIEW);
| |
| m_glfcns.glLoadIdentity ();
| |
| m_glfcns.glScaled (1, 1, -1);
| |
| // Matrix x_gl_mat1
| |
| m_glfcns.glMultMatrixd (x_mat1.data ());
| |
| m_glfcns.glMatrixMode (GL_PROJECTION);
| |
| m_glfcns.glLoadIdentity ();
| |
|
| |
| Matrix vp = get_viewport_scaled ();
| |
| // Install orthographic projection matrix with viewport
| |
| // setting "0, vp(2), vp(3), 0" and near / far values "xZ1, xZ2"
| |
| m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2);
| |
| // Matrix x_gl_mat2
| |
| m_glfcns.glMultMatrixd (x_mat2.data ());
| |
| m_glfcns.glMatrixMode (GL_MODELVIEW);
| |
|
| |
| m_glfcns.glClear (GL_DEPTH_BUFFER_BIT);
| |
|
| |
| </syntaxhighlight>}}
| |
|
| |
| Hint: If you debug in "setup_opengl_transformation ()", you can print the viewport "vp":
| |
|
| |
| (gdb) print *vp.rep.data@vp.rep.len
| |
| (gdb) $1 = {0, 0, 560, 420}
| |
|
| |
| This is consistent with the window size.
| |