// L============================================================================= // L This software is distributed under the MIT license. // L Copyright 2021 Péter Kardos // L============================================================================= #pragma once #include "../Matrix/MatrixImpl.hpp" #include "../Vector.hpp" #include "IdentityBuilder.hpp" namespace mathter { template class PerspectiveBuilder { static_assert(!std::is_integral_v); public: PerspectiveBuilder(T fovX, const Vector& ratios, T nearPlane, T farPlane, T projNearPlane = 0, T projFarPlane = 1) : fovX(fovX), ratios(ratios), nearPlane(nearPlane), farPlane(farPlane), projNearPlane(projNearPlane), projFarPlane(projFarPlane) {} PerspectiveBuilder& operator=(const PerspectiveBuilder&) = delete; template operator Matrix() const { Matrix m; Set(m); return m; } private: template void Set(Matrix& m) const { assert((nearPlane < 0 && farPlane < nearPlane) || (0 < nearPlane && nearPlane < farPlane)); using UVec = Vector; m = Zero(); // Layout be like (precede_vector): // w 0 0 0 // 0 h 0 0 // 0 0 A B // 0 0 C 0 auto absFovX = std::abs(U(fovX)); U N = U(nearPlane); U F = U(farPlane); U n = U(projNearPlane); U f = U(projFarPlane); U C = U(nearPlane) < U(0) ? U(-1) : U(1); U A = C * (f * F - n * N) / (F - N); U B = C * F * N * (n - f) / (F - N); UVec adjRatios = U(ratios(0)) / UVec(ratios); U w = tan(U(0.5) * absFovX); adjRatios /= w; for (int i = 0; i < adjRatios.Dimension(); ++i) { m(i, i) = adjRatios(i); } m(m.RowCount() - 2, m.ColumnCount() - 2) = A; if (Order == eMatrixOrder::FOLLOW_VECTOR) { std::swap(B, C); } m(m.RowCount() - 2, m.ColumnCount() - 1) = B; m(m.RowCount() - 1, m.ColumnCount() - 2) = C; } const T fovX; const Vector ratios; const T nearPlane; const T farPlane; const T projNearPlane = 0; const T projFarPlane = 1; }; /// Creates a general, n-dimensional perspective projection matrix. /// Field of view on the first axis (usually denoted X) in radians. /// Aspect ratio (or ratios in higher dimensions). FovX/FovY. /// Near bound of the projected volume on the last axis (Z in 3D). /// Far bound of the projected volume on the last axis (Z in 3D). /// The near plane is taken here after projection. /// The far plane is taken here after projection. /// The pre-projection near and far planes must be on the same side of the last axis, i.e. both negative or both positive. /// The post-projection near and far planes can be arbitrary, that is, near larger, near smaller, both positive, both negative, /// one positive the other negative, etc. /// Negative invert image on the axis which has a negative value. template auto Perspective(T fovX, const Vector& ratios, T nearPlane, T farPlane, T projNearPlane, T projFarPlane) { using NonIntegral = std::conditional_t, float, T>; return PerspectiveBuilder{ fovX, ratios, nearPlane, farPlane, projNearPlane, projFarPlane }; } /// Creates a 2D projection matrix. /// Field of view. /// Lower bound of the volume on the Y axis. /// Upper bound of the volume on the Y axis. /// Near plane is taken here after projection. /// Far plane is taken here after projection. /// The pre-projection near and far planes must be on the same side of the last axis, i.e. both negative or both positive. /// The post-projection near and far planes can be arbitrary, that is, near larger, near smaller, both positive, both negative, /// one positive the other negative, etc. template auto Perspective(T fov, T nearPlane, T farPlane, T projNearPlane, T projFarPlane) { return Perspective(std::abs(fov), Vector{ fov < T(0) ? T(-1) : T(1) }, nearPlane, farPlane, projNearPlane, projFarPlane); } /// Creates a 3D projection matrix. /// Field of view. /// FovX/FovY, so 1.777 for a 16:9 screen. /// Lower bound of the volume on the Y axis. /// Upper bound of the volume on the Y axis. /// Near plane is taken here after projection. /// Far plane is taken here after projection. /// The pre-projection near and far planes must be on the same side of the last axis, i.e. both negative or both positive. /// The post-projection near and far planes can be arbitrary, that is, near larger, near smaller, both positive, both negative, /// one positive the other negative, etc. /// The can be negative, in which case the second axis (i.e. Y) is inverted. template auto Perspective(T fov, T aspectRatio, T nearPlane, T farPlane, T projNearPlane, T projFarPlane) { return Perspective(std::abs(fov), Vector{ fov < T(0) ? T(-1) : T(1), T(1) / aspectRatio }, nearPlane, farPlane, projNearPlane, projFarPlane); } } // namespace mathter