Browse Source

Math: off-center Matrix4::perspectiveProjection().

pull/362/head
Vladimír Vondruš 7 years ago
parent
commit
a1cc0cb62d
  1. 2
      doc/changelog.dox
  2. 71
      src/Magnum/Math/Matrix4.h
  3. 44
      src/Magnum/Math/Test/Matrix4Test.cpp

2
doc/changelog.dox

@ -133,6 +133,8 @@ See also:
- @ref Math::Frustum::begin() / @ref Math::Frustum::end() accessors for - @ref Math::Frustum::begin() / @ref Math::Frustum::end() accessors for
easy range-for access to @ref Math::Frustum planes easy range-for access to @ref Math::Frustum planes
- New @ref Math::Matrix4::perspectiveProjection(const Vector2<T>&, const Vector2<T>&, T, T)
overload for calculating off-center projection matrices
- @ref Color3ub and @ref Color4ub can be printed with - @ref Color3ub and @ref Color4ub can be printed with
@ref Corrade::Utility::Debug as actual colors using the @ref Corrade::Utility::Debug as actual colors using the
@ref Corrade::Utility::Debug::color modifier @ref Corrade::Utility::Debug::color modifier

71
src/Magnum/Math/Matrix4.h

@ -298,10 +298,14 @@ template<class T> class Matrix4: public Matrix4x4<T> {
* 0 & 0 & -1 & 0 * 0 & 0 & -1 & 0
* \end{pmatrix} * \end{pmatrix}
* @f] * @f]
*
* If you need an off-center projection, use
* @ref perspectiveProjection(const Vector2<T>&, const Vector2<T>&, T, T)
* instead.
*
* @see @ref perspectiveProjection(Rad<T> fov, T, T, T), * @see @ref perspectiveProjection(Rad<T> fov, T, T, T),
* @ref orthographicProjection(), @ref Matrix3::projection(), * @ref orthographicProjection(), @ref Matrix3::projection(),
* @ref Constants::inf() * @ref Constants::inf()
* @m_keywords{gluPerspective()}
*/ */
static Matrix4<T> perspectiveProjection(const Vector2<T>& size, T near, T far); static Matrix4<T> perspectiveProjection(const Vector2<T>& size, T near, T far);
@ -340,6 +344,12 @@ template<class T> class Matrix4: public Matrix4x4<T> {
* \end{pmatrix} * \end{pmatrix}
* @f] * @f]
* *
* This function is similar to the classic @cpp gluPerspective() @ce,
* with the difference that @p fov is *horizontal* instead of vertical.
* If you need an off-center projection (as with the classic
* @cpp glFrustum() @ce function), use
* @ref perspectiveProjection(const Vector2<T>&, const Vector2<T>&, T, T).
*
* @see @ref orthographicProjection(), @ref Matrix3::projection(), * @see @ref orthographicProjection(), @ref Matrix3::projection(),
* @ref Constants::inf() * @ref Constants::inf()
* @m_keywords{gluPerspective()} * @m_keywords{gluPerspective()}
@ -348,6 +358,44 @@ template<class T> class Matrix4: public Matrix4x4<T> {
return perspectiveProjection(T(2)*near*std::tan(T(fov)*T(0.5))*Vector2<T>::yScale(T(1)/aspectRatio), near, far); return perspectiveProjection(T(2)*near*std::tan(T(fov)*T(0.5))*Vector2<T>::yScale(T(1)/aspectRatio), near, far);
} }
/**
* @brief 3D off-center perspective projection matrix
* @param bottomLeft Bottom left corner of the near clipping plane
* @param topRight Top right corner of the near clipping plane
* @param near Distance to near clipping plane, positive is
* ahead
* @param far Distance to far clipping plane, positive is
* ahead
*
* If @p far is finite, the result is: @f[
* \boldsymbol{A} = \begin{pmatrix}
* \frac{2n}{r - l} & 0 & \frac{r + l}{r - l} & 0 \\
* 0 & \frac{2n}{t - b} & \frac{t + b}{t - b} & 0 \\
* 0 & 0 & \frac{n + f}{n - f} & \frac{2nf}{n - f} \\
* 0 & 0 & -1 & 0
* \end{pmatrix}
* @f]
*
* For infinite @p far, the result is: @f[
* \boldsymbol{A} = \begin{pmatrix}
* \frac{2n}{r - l} & 0 & \frac{r + l}{r - l} & 0 \\
* 0 & \frac{2n}{t - b} & \frac{t + b}{t - b} & 0 \\
* 0 & 0 & -1 & -2n \\
* 0 & 0 & -1 & 0
* \end{pmatrix}
* @f]
*
* Equivalent to the classic @cpp glFrustum() @ce function. If
* @p bottomLeft and @p topRight are a negation of each other, this
* function is equivalent to @ref perspectiveProjection(const Vector2<T>&, T, T).
*
* @see @ref perspectiveProjection(Rad<T> fov, T, T, T),
* @ref orthographicProjection(), @ref Matrix3::projection(),
* @ref Constants::inf()
* @m_keywords{glFrustum()}
*/
static Matrix4<T> perspectiveProjection(const Vector2<T>& bottomLeft, const Vector2<T>& topRight, T near, T far);
/** /**
* @brief Matrix oriented towards a specific point * @brief Matrix oriented towards a specific point
* @param eye Location to place the matrix * @param eye Location to place the matrix
@ -971,6 +1019,27 @@ template<class T> Matrix4<T> Matrix4<T>::perspectiveProjection(const Vector2<T>&
{ T(0), T(0), m32, T(0)}}; { T(0), T(0), m32, T(0)}};
} }
template<class T> Matrix4<T> Matrix4<T>::perspectiveProjection(const Vector2<T>& bottomLeft, const Vector2<T>& topRight, const T near, const T far) {
const Vector2<T> xyDifference = topRight - bottomLeft;
const Vector2<T> xyScale = 2*near/xyDifference;
const Vector2<T> xyOffset = (topRight + bottomLeft)/xyDifference;
T m22, m32;
if(far == Constants<T>::inf()) {
m22 = T(-1);
m32 = T(-2)*near;
} else {
const T zScale = T(1.0)/(near-far);
m22 = (far+near)*zScale;
m32 = T(2)*far*near*zScale;
}
return {{ xyScale.x(), T(0), T(0), T(0)},
{ T(0), xyScale.y(), T(0), T(0)},
{xyOffset.x(), xyOffset.y(), m22, T(-1)},
{ T(0), T(0), m32, T(0)}};
}
template<class T> Matrix4<T> Matrix4<T>::lookAt(const Vector3<T>& eye, const Vector3<T>& target, const Vector3<T>& up) { template<class T> Matrix4<T> Matrix4<T>::lookAt(const Vector3<T>& eye, const Vector3<T>& target, const Vector3<T>& up) {
const Vector3<T> backward = (eye - target).normalized(); const Vector3<T> backward = (eye - target).normalized();
const Vector3<T> right = cross(up, backward).normalized(); const Vector3<T> right = cross(up, backward).normalized();

44
src/Magnum/Math/Test/Matrix4Test.cpp

@ -93,6 +93,8 @@ struct Matrix4Test: Corrade::TestSuite::Tester {
void perspectiveProjectionInfiniteFar(); void perspectiveProjectionInfiniteFar();
void perspectiveProjectionFov(); void perspectiveProjectionFov();
void perspectiveProjectionFovInfiniteFar(); void perspectiveProjectionFovInfiniteFar();
void perspectiveProjectionOffCenter();
void perspectiveProjectionOffCenterInfiniteFar();
void lookAt(); void lookAt();
void fromParts(); void fromParts();
@ -158,6 +160,8 @@ Matrix4Test::Matrix4Test() {
&Matrix4Test::perspectiveProjectionInfiniteFar, &Matrix4Test::perspectiveProjectionInfiniteFar,
&Matrix4Test::perspectiveProjectionFov, &Matrix4Test::perspectiveProjectionFov,
&Matrix4Test::perspectiveProjectionFovInfiniteFar, &Matrix4Test::perspectiveProjectionFovInfiniteFar,
&Matrix4Test::perspectiveProjectionOffCenter,
&Matrix4Test::perspectiveProjectionOffCenterInfiniteFar,
&Matrix4Test::lookAt, &Matrix4Test::lookAt,
&Matrix4Test::fromParts, &Matrix4Test::fromParts,
@ -501,6 +505,10 @@ void Matrix4Test::perspectiveProjection() {
/* NDC is left-handed, so point on near plane should be -1, far +1 */ /* NDC is left-handed, so point on near plane should be -1, far +1 */
CORRADE_COMPARE(actual.transformPoint({0.0f, 0.0f, -32.0f}), Vector3(0.0f, 0.0f, -1.0f)); CORRADE_COMPARE(actual.transformPoint({0.0f, 0.0f, -32.0f}), Vector3(0.0f, 0.0f, -1.0f));
CORRADE_COMPARE(actual.transformPoint({0.0f, 0.0f, -100.0f}), Vector3(0.0f, 0.0f, +1.0f)); CORRADE_COMPARE(actual.transformPoint({0.0f, 0.0f, -100.0f}), Vector3(0.0f, 0.0f, +1.0f));
/* The version with bottom/left/top/right should give the same result if
it's centered */
CORRADE_COMPARE(Matrix4::perspectiveProjection({-8.0f, -4.5f}, {8.0f, 4.5f}, 32.0f, 100.0f), expected);
} }
void Matrix4Test::perspectiveProjectionInfiniteFar() { void Matrix4Test::perspectiveProjectionInfiniteFar() {
@ -515,6 +523,10 @@ void Matrix4Test::perspectiveProjectionInfiniteFar() {
in direction of far plane +1 */ in direction of far plane +1 */
CORRADE_COMPARE(actual.transformPoint({0.0f, 0.0f, -32.0f}), Vector3(0.0f, 0.0f, -1.0f)); CORRADE_COMPARE(actual.transformPoint({0.0f, 0.0f, -32.0f}), Vector3(0.0f, 0.0f, -1.0f));
CORRADE_COMPARE(actual.transformVector({0.0f, 0.0f, -1.0f}), Vector3(0.0f, 0.0f, +1.0f)); CORRADE_COMPARE(actual.transformVector({0.0f, 0.0f, -1.0f}), Vector3(0.0f, 0.0f, +1.0f));
/* The version with bottom/left/top/right should give the same result if
it's centered */
CORRADE_COMPARE(Matrix4::perspectiveProjection({-8.0f, -4.5f}, {8.0f, 4.5f}, 32.0f, Constants::inf()), expected);
} }
void Matrix4Test::perspectiveProjectionFov() { void Matrix4Test::perspectiveProjectionFov() {
@ -533,6 +545,38 @@ void Matrix4Test::perspectiveProjectionFovInfiniteFar() {
CORRADE_COMPARE(Matrix4::perspectiveProjection(Deg(27.0f), 2.35f, 32.0f, Constants::inf()), expected); CORRADE_COMPARE(Matrix4::perspectiveProjection(Deg(27.0f), 2.35f, 32.0f, Constants::inf()), expected);
} }
void Matrix4Test::perspectiveProjectionOffCenter() {
Matrix4 expected({ 4.0f, 0.0f, 0.0f, 0.0f},
{ 0.0f, 7.111111f, 0.0f, 0.0f},
{-0.125f, -0.1111111f, -1.9411764f, -1.0f},
{ 0.0f, 0.0f, -94.1176452f, 0.0f});
/* Shifted by (-1, -0.5) compared to the perspectiveProjection() test */
Matrix4 actual = Matrix4::perspectiveProjection({-9.0f, -5.0f}, {7.0f, 4.0f}, 32.0f, 100.0f);
CORRADE_COMPARE(actual, expected);
/* NDC is left-handed, so point on the near plane top right corner should
be (1, 1, -1), and a point in the center on the far plane roughly (0, 0,
+1) due to the "off-centerness" */
CORRADE_COMPARE(actual.transformPoint({7.0f, 4.0f, -32.0f}), Vector3(1.0f, 1.0f, -1.0f));
CORRADE_COMPARE(actual.transformPoint({0.0f, 0.0f, -100.0f}), Vector3(0.125f, 0.1111111f, +1.0f));
}
void Matrix4Test::perspectiveProjectionOffCenterInfiniteFar() {
Matrix4 expected({ 4.0f, 0.0f, 0.0f, 0.0f},
{ 0.0f, 7.111111f, 0.0f, 0.0f},
{-0.125f, -0.1111111f, -1.0f, -1.0f},
{ 0.0f, 0.0f, -64.0f, 0.0f});
/* Shifted by (-1, -0.5) compared to perspectiveProjectionInfiniteFar() */
Matrix4 actual = Matrix4::perspectiveProjection({-9.0f, -5.0f}, {7.0f, 4.0f}, 32.0f, Constants::inf());
CORRADE_COMPARE(actual, expected);
/* NDC is left-handed, so point on the near plane bottom left corner should
be (1, 1, -1) and a *vector* in the direction of the far plane roughly
(0, 0, +1) due to the "off-centerness" */
CORRADE_COMPARE(actual.transformPoint({-9.0f, -5.0f, -32.0f}), Vector3(-1.0f, -1.0f, -1.0f));
CORRADE_COMPARE(actual.transformVector({0.0f, 0.0f, -1.0f}), Vector3(0.125f, 0.1111111f, +1.0f));
}
void Matrix4Test::lookAt() { void Matrix4Test::lookAt() {
Vector3 translation{5.3f, -8.9f, -10.0f}; Vector3 translation{5.3f, -8.9f, -10.0f};
Vector3 target{19.0f, 29.3f, 0.0f}; Vector3 target{19.0f, 29.3f, 0.0f};

Loading…
Cancel
Save