From 0d31f7461b31698ea5bf92ec66ff5056a6ad7360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 24 Aug 2023 17:23:28 +0200 Subject: [PATCH] Math: add near/far plane distance queries to Matrix4. Strange, I thought it could be extracted the same way for both orthographic and perspective projection... --- doc/changelog.dox | 4 + src/Magnum/Math/Matrix4.h | 111 +++++++++++++++++++++++++-- src/Magnum/Math/Test/Matrix4Test.cpp | 24 ++++++ 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index e5a426603..e0abb7695 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -218,6 +218,10 @@ See also: match the classic `glOrtho()` and `gluOrtho2D()` APIs, complementing @ref Math::Matrix4::perspectiveProjection(const Vector2&, const Vector2&, T, T) that got added in 2019.10 +- New @ref Math::Matrix4::orthographicProjectionNear() const, + @ref Math::Matrix4::orthographicProjectionFar() const, + @ref Math::Matrix4::perspectiveProjectionNear() const and + @ref Math::Matrix4::perspectiveProjectionFar() const queries - Added @ref Math::Intersection::rayRange() (see [mosra/magnum#484](https://github.com/mosra/magnum/pull/484)) - Added @ref Math::RectangularMatrix::RectangularMatrix(IdentityInitT, T) constructor as it might be useful to create non-square identity matrices as diff --git a/src/Magnum/Math/Matrix4.h b/src/Magnum/Math/Matrix4.h index d784922c9..c26657c78 100644 --- a/src/Magnum/Math/Matrix4.h +++ b/src/Magnum/Math/Matrix4.h @@ -336,7 +336,10 @@ template class Matrix4: public Matrix4x4 { * If you need an off-center projection (as with the classic * @m_class{m-doc-external} [glOrtho()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml) * function), use @ref orthographicProjection(const Vector2&, const Vector2&, T, T). - * @see @ref perspectiveProjection(), @ref Matrix3::projection() + * @see @ref perspectiveProjection(), + * @ref orthographicProjectionNear() const, + * @ref orthographicProjectionFar() const, + * @ref Matrix3::projection() */ static Matrix4 orthographicProjection(const Vector2& size, T near, T far); @@ -365,6 +368,8 @@ template class Matrix4: public Matrix4x4 { * @ref orthographicProjection(const Vector2&, T, T). * * @see @ref perspectiveProjection(), + * @ref orthographicProjectionNear() const, + * @ref orthographicProjectionFar() const, * @ref Matrix3::projection(const Vector2&, const Vector2&) * @m_keywords{glOrtho()} */ @@ -398,8 +403,10 @@ template class Matrix4: public Matrix4x4 { * @ref perspectiveProjection(const Vector2&, const Vector2&, T, T) * instead. * @see @ref perspectiveProjection(Rad, T, T, T), - * @ref orthographicProjection(), @ref Matrix3::projection(), - * @ref Constants::inf() + * @ref orthographicProjection(), + * @ref perspectiveProjectionNear() const, + * @ref perspectiveProjectionFar() const, + * @ref Matrix3::projection(), @ref Constants::inf() */ static Matrix4 perspectiveProjection(const Vector2& size, T near, T far); @@ -443,8 +450,10 @@ template class Matrix4: public Matrix4x4 { * If you need an off-center projection (as with the classic * @m_class{m-doc-external} [glFrustum()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml) * function), use @ref perspectiveProjection(const Vector2&, const Vector2&, T, T). - * @see @ref orthographicProjection(), @ref Matrix3::projection(), - * @ref Constants::inf() + * @see @ref orthographicProjection(), + * @ref perspectiveProjectionNear() const, + * @ref perspectiveProjectionFar() const, + * @ref Matrix3::projection(), @ref Constants::inf() * @m_keywords{gluPerspective()} */ static Matrix4 perspectiveProjection(Rad fov, T aspectRatio, T near, T far) { @@ -486,6 +495,8 @@ template class Matrix4: public Matrix4x4 { * * @see @ref perspectiveProjection(Rad fov, T, T, T), * @ref orthographicProjection(const Vector2&, const Vector2&, T, T), + * @ref perspectiveProjectionNear() const, + * @ref perspectiveProjectionFar() const, * @ref Matrix3::projection(), @ref Constants::inf() * @m_keywords{glFrustum()} */ @@ -1032,6 +1043,96 @@ template class Matrix4: public Matrix4x4 { Vector3& translation() { return (*this)[3].xyz(); } constexpr Vector3 translation() const { return (*this)[3].xyz(); } /**< @overload */ + /** + * @brief Distance to near plane of an orthographic projection matrix + * @m_since_latest + * + * Assuming a matrix @f$ \boldsymbol{A} @f$ constructed with + * @ref orthographicProjection(), returns the distance to its near + * plane: @f[ + * \frac{\boldsymbol{A}_{3,2} + 1}{\boldsymbol{A}_{2,2}} = + * \frac{\frac{n + f}{n - f} + 1}{\frac{2}{n - f}} = + * \frac{\frac{n + f + n - f}{n - f}}{\frac{2}{n - f}} = + * \frac{2n}{2} = n + * @f] + * @see @ref orthographicProjectionFar() const, + * @ref perspectiveProjectionNear() const + */ + Float orthographicProjectionNear() const { + return ((*this)[3][2] + T(1))/(*this)[2][2]; + } + + /** + * @brief Distance to far plane of an orthographic projection matrix + * @m_since_latest + * + * Assuming a matrix @f$ \boldsymbol{A} @f$ constructed with + * @ref orthographicProjection(), returns the distance to its far + * plane: @f[ + * \frac{\boldsymbol{A}_{3,2} - 1}{\boldsymbol{A}_{2,2}} = + * \frac{\frac{n + f}{n - f} - 1}{\frac{2}{n - f}} = + * \frac{\frac{n + f - n + f}{n - f}}{\frac{2}{n - f}} = + * \frac{2f}{2} = f + * @f] + * @see @ref orthographicProjectionNear() const, + * @ref perspectiveProjectionFar() const + */ + Float orthographicProjectionFar() const { + return ((*this)[3][2] - T(1))/(*this)[2][2]; + } + + /** + * @brief Distance to near plane of a perspective projection matrix + * @m_since_latest + * + * Assuming a matrix @f$ \boldsymbol{A} @f$ constructed with + * @ref perspectiveProjection(), returns the distance to its near + * plane: @f[ + * \frac{\boldsymbol{A}_{3,2}}{\boldsymbol{A}_{2,2} - 1} = + * \frac{\frac{2nf}{n - f}}{\frac{n + f}{n - f} - 1} = + * \frac{\frac{2nf}{n - f}}{\frac{n + f - n + f}{n - f}} = + * \frac{2nf}{2f} = n + * @f] + * + * The same equation works for a perspective projection with an + * infinite far plane: @f[ + * \frac{\boldsymbol{A}_{3,2}}{\boldsymbol{A}_{2,2} - 1} = + * \frac{-2n}{-1 - 1} = + * \frac{-2n}{-2} = n + * @f] + * @see @ref perspectiveProjectionFar() const, + * @ref orthographicProjectionNear() const + */ + Float perspectiveProjectionNear() const { + return (*this)[3][2]/((*this)[2][2] - T(1)); + } + + /** + * @brief Distance to far plane of a perspective projection matrix + * @m_since_latest + * + * Assuming a matrix @f$ \boldsymbol{A} @f$ constructed with + * @ref perspectiveProjection() with a positive far value, returns the + * distance to its far plane: @f[ + * \left\lvert \frac{\boldsymbol{A}_{3,2}}{\boldsymbol{A}_{2,2} + 1}\right\rvert = + * \left\lvert \frac{\frac{2nf}{n - f}}{\frac{n + f}{n - f} + 1}\right\rvert = + * \left\lvert \frac{\frac{2nf}{n - f}}{\frac{n + f + n - f}{n - f}}\right\rvert = + * \left\lvert \frac{2nf}{2n} \right\rvert = f + * @f] + * + * The same equation works for a perspective projection with an + * infinite far plane: @f[ + * \left\lvert \frac{\boldsymbol{A}_{3,2}}{\boldsymbol{A}_{2,2} + 1} \right\rvert = + * \left\lvert \frac{-2n}{-1 + 1} \right\rvert = + * \left\lvert \frac{-2n}{0} \right\rvert = \infty + * @f] + * @see @ref perspectiveProjectionNear() const, + * @ref orthographicProjectionFar() const + */ + Float perspectiveProjectionFar() const { + return std::abs((*this)[3][2]/((*this)[2][2] + T(1))); + } + /** * @brief Inverted rigid transformation matrix * diff --git a/src/Magnum/Math/Test/Matrix4Test.cpp b/src/Magnum/Math/Test/Matrix4Test.cpp index ee3114457..0f58906fe 100644 --- a/src/Magnum/Math/Test/Matrix4Test.cpp +++ b/src/Magnum/Math/Test/Matrix4Test.cpp @@ -112,6 +112,9 @@ struct Matrix4Test: Corrade::TestSuite::Tester { void uniformScalingPartNotUniform(); void normalMatrixPart(); void vectorParts(); + void orthographicProjectionParts(); + void perspectiveProjectionParts(); + void perspectiveProjectionInfiniteFarParts(); void invertedRigid(); void invertedRigidNotRigid(); void transform(); @@ -183,6 +186,9 @@ Matrix4Test::Matrix4Test() { &Matrix4Test::uniformScalingPartNotUniform, &Matrix4Test::normalMatrixPart, &Matrix4Test::vectorParts, + &Matrix4Test::orthographicProjectionParts, + &Matrix4Test::perspectiveProjectionParts, + &Matrix4Test::perspectiveProjectionInfiniteFarParts, &Matrix4Test::invertedRigid, &Matrix4Test::invertedRigidNotRigid, &Matrix4Test::transform, @@ -1018,6 +1024,24 @@ void Matrix4Test::vectorParts() { CORRADE_COMPARE(translation, Vector3(-5.0f, 12.0f, 0.5f)); } +void Matrix4Test::orthographicProjectionParts() { + Matrix4 projection = Matrix4::orthographicProjection({5.0f, 4.0f}, 1.0f, 9.0f); + CORRADE_COMPARE(projection.orthographicProjectionNear(), 1.0f); + CORRADE_COMPARE(projection.orthographicProjectionFar(), 9.0f); +} + +void Matrix4Test::perspectiveProjectionParts() { + Matrix4 projection = Matrix4::perspectiveProjection({16.0f, 9.0f}, 32.0f, 100.0f); + CORRADE_COMPARE(projection.perspectiveProjectionNear(), 32.0f); + CORRADE_COMPARE(projection.perspectiveProjectionFar(), 100.0f); +} + +void Matrix4Test::perspectiveProjectionInfiniteFarParts() { + Matrix4 projection = Matrix4::perspectiveProjection({16.0f, 9.0f}, 32.0f, Constants::inf()); + CORRADE_COMPARE(projection.perspectiveProjectionNear(), 32.0f); + CORRADE_COMPARE(projection.perspectiveProjectionFar(), Constants::inf()); +} + void Matrix4Test::invertedRigid() { Matrix4 actual = Matrix4::rotation(-74.0_degf, Vector3(-1.0f, 0.5f, 2.0f).normalized())* Matrix4::reflection(Vector3(0.5f, -2.0f, 2.0f).normalized())*