Skip to content

Interop with graphics APIs

Yoan edited this page Dec 29, 2017 · 4 revisions

At some point you're probably going to need to send vectors and/or matrices to graphics APIs such as OpenGL, Direct3D, Vulkan, etc.

Know your storage layout (for matrices)

Did you pick row-major or column-major ?
Either way, if you plan to send a matrix to OpenGL (for instance, via glUniformMatrix4fv()), you'll be interested in the following.

  • There's no guarantee that matrix elements are tightly packed in memory!
    This is especially the case for matrices of repr_simd modules!
    To ensure that elements are tightly packed in memory, you MUST call the is_packed() method.
    If it returns false, don't panic!, there is still into_row_array() and into_col_array().
    Either way, as_row_ptr() and as_col_ptr() do implicitly assert!(self.is_packed()).
  • Matrices provide a gl_should_transpose(&self) method.
    If you don't have a matrix instance handy, you may also use GL_SHOULD_TRANSPOSE associated constant.
    They both give you a bool which you may cast to a GLboolean as the transpose parameter, as shown below.
// Assuming m is an instance of row-major matrix...
assert!(m.is_packed());
glUniformMatrix4fv(loc, 1, m.gl_should_transpose() as _, m.as_row_ptr());

// Assuming m is an instance of column-major matrix...
assert!(m.is_packed());
glUniformMatrix4fv(loc, 1, m.gl_should_transpose() as _, m.as_col_ptr());

// Assuming m is a matrix with any storage layout...
let rows = m.into_row_array();
glUniformMatrix4fv(loc, 1, m.gl_should_transpose() as _, &rows);
glUniformMatrix4fv(loc, 1, GL_TRUE, &rows);
let cols = m.into_col_array();
glUniformMatrix4fv(loc, 1, m.gl_should_transpose() as _, &cols);
glUniformMatrix4fv(loc, 1, GL_FALSE, &cols);

Know your depth clipping range

Projection matrix functions are suffixed by either _zo or _no, which mean, respectively, "Zero to One" and "Negative one to One".

  • Direct3D and Vulkan: It's [0; 1].
    Note that in Vulkan, the Y axis points downwards! See this article, for instance.
  • OpenGL: It's [-1; 1].
    But, since OpenGL 4.5 (or with the GL_ARB_clip_control extension), this can be changed to be [0; 1] by using glClipControl().

Know your handedness

Most projection matrix functions have a _lh or _rh suffix, that tells if they are intended for, respectively, left-handed and right-handed spaces.

You might have a hard time making sense of what a left-handed projection matrix does differently from a right-handed one.
The answer lies in the handedness of Normalized Device Coordinates (NDC). It just so happens that, in both Direct3D and OpenGL, NDC space is left-handed, because either 0 or -1 is "near", and 1 is "far" (i.e "forward").
I know of no other graphics API that reverses this, therefore vek assumes it (just like GLM does). Just be careful if you reverse the mapping via glDepthRange().

Therefore, "left-handed" projection matrices map from left-handed (your space) to left-handed (NDC).
"Right-handed" projection matrices do some more work since they map from right-handed (your space) to left-handed (NDC).

One thing where handedness shouldn't make sense is model/view matrices, because essentially, the need to change handedness only arises when projecting your world into NDC.
GLM does provide lookAtRH and lookAtLH, but all the right-handed version does is flip the Z-axis of transformed points.
vek has them too, but they will remain private until someone proves to me that there is a sane use case for these.