4
\$\begingroup\$

I need the formula to create a orthographic projection matrix. I am using GLM math libary and DirectX11. The reason I cannot use GLM to create it is because the NDC spaces are different between DX11 and OpenGL, thus I need to manually create one.

I am however using right-handed coordinate system and column-major matrices, just like GLM.

This is my attempt, but I get wrongly images when rendering with it, so it is probably wrong. I am using it for cascading shadow maps, and from the graphics debugger, I probably need to mirror it, but it is just a guess. Any ideas?

Mat4 OrthographicMatrix(const float left, const float right, const float top, const float bottom, const float zNear, const float zFar)
{
    Mat4 ret(1.0f);

    ret[0][0] = 2.0f / (right - left);
    ret[1][1] = 2.0f / (top - bottom);
    ret[2][2] = 1.0f / (zNear - zFar);
    ret[3][0] = (left + right) / (left - right);
    ret[3][1] = (top + bottom) / (bottom - top);
    ret[3][2] = (zNear) / (zNear - zFar);

    return ret;
}
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Duplicate of this \$\endgroup\$ Aug 15, 2014 at 4:59
  • \$\begingroup\$ Please don't cross post. Only post on the site where your question is most relevant. Please remove one of the posts. \$\endgroup\$
    – House
    Aug 15, 2014 at 14:13

2 Answers 2

4
\$\begingroup\$

The D3DX function D3DXMatrixOrthoOffCenterRH constructs an orthographic projection matrix based on the top/left/right/bottom coordinates of the view volume. Per the documentation, the formula used is:

\begin{matrix} 2/(r-l) & 0 & 0 & 0 \\ 0 & 2/(t-b) & 0 & 0 \\ 0 & 0 & 1/(zn-zf) & 0 \\ (l+r)/(l-r) & (t+b)/(b-t) & zn/(zn-zf) & 1 \end{matrix}

where top, left, bottom and right coordinates are represented as t, l, b and r respectively. The minimum and maximum depth values are zn and zf.

Since the math here appears to be the identical to the math for your individual components, you're probably seeing a problem in the storage of those components (resulting in an effective transposition). I'd verify the storage location of your last three components is actually what you need it to be.

(Don't just blindly transpose your storage; it's possible you have two transpose-esque bugs elsewhere cancelling eachother out and you're just adding a third; you'll want to review your matrix storage class carefully as where as everywhere in your basic pipeline that you are expecting right-handed systems and conventions to make sure you're not accidentally doing the wrong thing.)

\$\endgroup\$
2
\$\begingroup\$

Rather than use the legacy D3DXMath, consider using DirectXMath instead
Plus you get all the source in the header.

This computes the matrix as a row-major, right-handed matrix:

inline XMMATRIX XMMatrixOrthographicOffCenterRH
(
    float ViewLeft, 
    float ViewRight, 
    float ViewBottom, 
    float ViewTop, 
    float NearZ, 
    float FarZ
)
{
    assert(!XMScalarNearEqual(ViewRight, ViewLeft, 0.00001f));
    assert(!XMScalarNearEqual(ViewTop, ViewBottom, 0.00001f));
    assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f));

#if defined(_XM_NO_INTRINSICS_)

    float ReciprocalWidth = 1.0f / (ViewRight - ViewLeft);
    float ReciprocalHeight = 1.0f / (ViewTop - ViewBottom);
    float fRange = 1.0f / (NearZ-FarZ);

    XMMATRIX M;
    M.m[0][0] = ReciprocalWidth + ReciprocalWidth;
    M.m[0][1] = 0.0f;
    M.m[0][2] = 0.0f;
    M.m[0][3] = 0.0f;

    M.m[1][0] = 0.0f;
    M.m[1][1] = ReciprocalHeight + ReciprocalHeight;
    M.m[1][2] = 0.0f;
    M.m[1][3] = 0.0f;

    M.m[2][0] = 0.0f;
    M.m[2][1] = 0.0f;
    M.m[2][2] = fRange;
    M.m[2][3] = 0.0f;

    M.r[3] = XMVectorSet(-(ViewLeft + ViewRight) * ReciprocalWidth, 
                         -(ViewTop + ViewBottom) * ReciprocalHeight,
                         fRange * NearZ,
                         1.0f);
    return M;

You can transpose it to get the column-major version.

\$\endgroup\$

You must log in to answer this question.

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