Last active
February 5, 2024 21:32
-
-
Save connorjak/a01f22c7e2c595c7716d9645594d5fa6 to your computer and use it in GitHub Desktop.
Copy pasted source code from UE 4.27.2 that shows math details of how camera perspective matrices are calculated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// COPY PASTED FROM UNREAL 4.27.2 SOURCE CODE | |
// Copyright Epic Games, Inc. All Rights Reserved. | |
//... | |
FMatrix FMinimalViewInfo::CalculateProjectionMatrix() const | |
{ | |
FMatrix ProjectionMatrix; | |
if (ProjectionMode == ECameraProjectionMode::Orthographic) | |
{ | |
const float YScale = 1.0f / AspectRatio; | |
const float HalfOrthoWidth = OrthoWidth / 2.0f; | |
const float ScaledOrthoHeight = OrthoWidth / 2.0f * YScale; | |
const float NearPlane = OrthoNearClipPlane; | |
const float FarPlane = OrthoFarClipPlane; | |
const float ZScale = 1.0f / (FarPlane - NearPlane); | |
const float ZOffset = -NearPlane; | |
ProjectionMatrix = FReversedZOrthoMatrix( | |
HalfOrthoWidth, | |
ScaledOrthoHeight, | |
ZScale, | |
ZOffset | |
); | |
} | |
else | |
{ | |
// Avoid divide by zero in the projection matrix calculation by clamping FOV | |
ProjectionMatrix = FReversedZPerspectiveMatrix( | |
FMath::Max(0.001f, FOV) * (float)PI / 360.0f, | |
AspectRatio, | |
1.0f, | |
GNearClippingPlane ); | |
} | |
if (!OffCenterProjectionOffset.IsZero()) | |
{ | |
const float Left = -1.0f + OffCenterProjectionOffset.X; | |
const float Right = Left + 2.0f; | |
const float Bottom = -1.0f + OffCenterProjectionOffset.Y; | |
const float Top = Bottom + 2.0f; | |
ProjectionMatrix.M[2][0] = (Left + Right) / (Left - Right); | |
ProjectionMatrix.M[2][1] = (Bottom + Top) / (Bottom - Top); | |
} | |
return ProjectionMatrix; | |
} | |
void FMinimalViewInfo::CalculateProjectionMatrixGivenView(const FMinimalViewInfo& ViewInfo, TEnumAsByte<enum EAspectRatioAxisConstraint> AspectRatioAxisConstraint, FViewport* Viewport, FSceneViewProjectionData& InOutProjectionData) | |
{ | |
// Create the projection matrix (and possibly constrain the view rectangle) | |
if (ViewInfo.bConstrainAspectRatio) | |
{ | |
// Enforce a particular aspect ratio for the render of the scene. | |
// Results in black bars at top/bottom etc. | |
InOutProjectionData.SetConstrainedViewRectangle(Viewport->CalculateViewExtents(ViewInfo.AspectRatio, InOutProjectionData.GetViewRect())); | |
InOutProjectionData.ProjectionMatrix = ViewInfo.CalculateProjectionMatrix(); | |
} | |
else | |
{ | |
float XAxisMultiplier; | |
float YAxisMultiplier; | |
const FIntRect& ViewRect = InOutProjectionData.GetViewRect(); | |
const int32 SizeX = ViewRect.Width(); | |
const int32 SizeY = ViewRect.Height(); | |
// If x is bigger, and we're respecting x or major axis, AND mobile isn't forcing us to be Y axis aligned | |
const bool bMaintainXFOV = | |
((SizeX > SizeY) && (AspectRatioAxisConstraint == AspectRatio_MajorAxisFOV)) || | |
(AspectRatioAxisConstraint == AspectRatio_MaintainXFOV) || | |
(ViewInfo.ProjectionMode == ECameraProjectionMode::Orthographic); | |
if (bMaintainXFOV) | |
{ | |
// If the viewport is wider than it is tall | |
XAxisMultiplier = 1.0f; | |
YAxisMultiplier = SizeX / (float)SizeY; | |
} | |
else | |
{ | |
// If the viewport is taller than it is wide | |
XAxisMultiplier = SizeY / (float)SizeX; | |
YAxisMultiplier = 1.0f; | |
} | |
float MatrixHalfFOV; | |
if (!bMaintainXFOV && ViewInfo.AspectRatio != 0.f && !CVarUseLegacyMaintainYFOV.GetValueOnGameThread()) | |
{ | |
// The view-info FOV is horizontal. But if we have a different aspect ratio constraint, we need to | |
// adjust this FOV value using the aspect ratio it was computed with, so we that we can compute the | |
// complementary FOV value (with the *effective* aspect ratio) correctly. | |
const float HalfXFOV = FMath::DegreesToRadians(FMath::Max(0.001f, ViewInfo.FOV) / 2.f); | |
const float HalfYFOV = FMath::Atan(FMath::Tan(HalfXFOV) / ViewInfo.AspectRatio); | |
MatrixHalfFOV = HalfYFOV; | |
} | |
else | |
{ | |
// Avoid divide by zero in the projection matrix calculation by clamping FOV. | |
// Note the division by 360 instead of 180 because we want the half-FOV. | |
MatrixHalfFOV = FMath::Max(0.001f, ViewInfo.FOV) * (float)PI / 360.0f; | |
} | |
if (ViewInfo.ProjectionMode == ECameraProjectionMode::Orthographic) | |
{ | |
const float OrthoWidth = ViewInfo.OrthoWidth / 2.0f * XAxisMultiplier; | |
const float OrthoHeight = (ViewInfo.OrthoWidth / 2.0f) / YAxisMultiplier; | |
const float NearPlane = ViewInfo.OrthoNearClipPlane; | |
const float FarPlane = ViewInfo.OrthoFarClipPlane; | |
const float ZScale = 1.0f / (FarPlane - NearPlane); | |
const float ZOffset = -NearPlane; | |
InOutProjectionData.ProjectionMatrix = FReversedZOrthoMatrix( | |
OrthoWidth, | |
OrthoHeight, | |
ZScale, | |
ZOffset | |
); | |
} | |
else | |
{ | |
InOutProjectionData.ProjectionMatrix = FReversedZPerspectiveMatrix( | |
MatrixHalfFOV, | |
MatrixHalfFOV, | |
XAxisMultiplier, | |
YAxisMultiplier, | |
GNearClippingPlane, | |
GNearClippingPlane | |
); | |
} | |
} | |
if (!ViewInfo.OffCenterProjectionOffset.IsZero()) | |
{ | |
const float Left = -1.0f + ViewInfo.OffCenterProjectionOffset.X; | |
const float Right = Left + 2.0f; | |
const float Bottom = -1.0f + ViewInfo.OffCenterProjectionOffset.Y; | |
const float Top = Bottom + 2.0f; | |
InOutProjectionData.ProjectionMatrix.M[2][0] = (Left + Right) / (Left - Right); | |
InOutProjectionData.ProjectionMatrix.M[2][1] = (Bottom + Top) / (Bottom - Top); | |
} | |
} | |
//... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright Epic Games, Inc. All Rights Reserved. | |
#pragma once | |
#include "CoreTypes.h" | |
#include "Math/UnrealMathUtility.h" | |
#include "Math/Plane.h" | |
#include "Math/Matrix.h" | |
class FPerspectiveMatrix | |
: public FMatrix | |
{ | |
public: | |
// Note: the value of this must match the mirror in Common.usf! | |
#define Z_PRECISION 0.0f | |
/** | |
* Constructor | |
* | |
* @param HalfFOVX Half FOV in the X axis | |
* @param HalfFOVY Half FOV in the Y axis | |
* @param MultFOVX multiplier on the X axis | |
* @param MultFOVY multiplier on the y axis | |
* @param MinZ distance to the near Z plane | |
* @param MaxZ distance to the far Z plane | |
*/ | |
FPerspectiveMatrix(float HalfFOVX, float HalfFOVY, float MultFOVX, float MultFOVY, float MinZ, float MaxZ); | |
/** | |
* Constructor | |
* | |
* @param HalfFOV half Field of View in the Y direction | |
* @param Width view space width | |
* @param Height view space height | |
* @param MinZ distance to the near Z plane | |
* @param MaxZ distance to the far Z plane | |
* @note that the FOV you pass in is actually half the FOV, unlike most perspective matrix functions (D3DXMatrixPerspectiveFovLH). | |
*/ | |
FPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ, float MaxZ); | |
/** | |
* Constructor | |
* | |
* @param HalfFOV half Field of View in the Y direction | |
* @param Width view space width | |
* @param Height view space height | |
* @param MinZ distance to the near Z plane | |
* @note that the FOV you pass in is actually half the FOV, unlike most perspective matrix functions (D3DXMatrixPerspectiveFovLH). | |
*/ | |
FPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ); | |
}; | |
class FReversedZPerspectiveMatrix : public FMatrix | |
{ | |
public: | |
FReversedZPerspectiveMatrix(float HalfFOVX, float HalfFOVY, float MultFOVX, float MultFOVY, float MinZ, float MaxZ); | |
FReversedZPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ, float MaxZ); | |
FReversedZPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ); | |
}; | |
#ifdef _MSC_VER | |
#pragma warning (push) | |
// Disable possible division by 0 warning | |
#pragma warning (disable : 4723) | |
#endif | |
FORCEINLINE FPerspectiveMatrix::FPerspectiveMatrix(float HalfFOVX, float HalfFOVY, float MultFOVX, float MultFOVY, float MinZ, float MaxZ) | |
: FMatrix( | |
FPlane(MultFOVX / FMath::Tan(HalfFOVX), 0.0f, 0.0f, 0.0f), | |
FPlane(0.0f, MultFOVY / FMath::Tan(HalfFOVY), 0.0f, 0.0f), | |
FPlane(0.0f, 0.0f, ((MinZ == MaxZ) ? (1.0f - Z_PRECISION) : MaxZ / (MaxZ - MinZ)), 1.0f), | |
FPlane(0.0f, 0.0f, -MinZ * ((MinZ == MaxZ) ? (1.0f - Z_PRECISION) : MaxZ / (MaxZ - MinZ)), 0.0f) | |
) | |
{ } | |
FORCEINLINE FPerspectiveMatrix::FPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ, float MaxZ) | |
: FMatrix( | |
FPlane(1.0f / FMath::Tan(HalfFOV), 0.0f, 0.0f, 0.0f), | |
FPlane(0.0f, Width / FMath::Tan(HalfFOV) / Height, 0.0f, 0.0f), | |
FPlane(0.0f, 0.0f, ((MinZ == MaxZ) ? (1.0f - Z_PRECISION) : MaxZ / (MaxZ - MinZ)), 1.0f), | |
FPlane(0.0f, 0.0f, -MinZ * ((MinZ == MaxZ) ? (1.0f - Z_PRECISION) : MaxZ / (MaxZ - MinZ)), 0.0f) | |
) | |
{ } | |
FORCEINLINE FPerspectiveMatrix::FPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ) | |
: FMatrix( | |
FPlane(1.0f / FMath::Tan(HalfFOV), 0.0f, 0.0f, 0.0f), | |
FPlane(0.0f, Width / FMath::Tan(HalfFOV) / Height, 0.0f, 0.0f), | |
FPlane(0.0f, 0.0f, (1.0f - Z_PRECISION), 1.0f), | |
FPlane(0.0f, 0.0f, -MinZ * (1.0f - Z_PRECISION), 0.0f) | |
) | |
{ } | |
FORCEINLINE FReversedZPerspectiveMatrix::FReversedZPerspectiveMatrix(float HalfFOVX, float HalfFOVY, float MultFOVX, float MultFOVY, float MinZ, float MaxZ) | |
: FMatrix( | |
FPlane(MultFOVX / FMath::Tan(HalfFOVX), 0.0f, 0.0f, 0.0f), | |
FPlane(0.0f, MultFOVY / FMath::Tan(HalfFOVY), 0.0f, 0.0f), | |
FPlane(0.0f, 0.0f, ((MinZ == MaxZ) ? 0.0f : MinZ / (MinZ - MaxZ)), 1.0f), | |
FPlane(0.0f, 0.0f, ((MinZ == MaxZ) ? MinZ : -MaxZ * MinZ / (MinZ - MaxZ)), 0.0f) | |
) | |
{ } | |
FORCEINLINE FReversedZPerspectiveMatrix::FReversedZPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ, float MaxZ) | |
: FMatrix( | |
FPlane(1.0f / FMath::Tan(HalfFOV), 0.0f, 0.0f, 0.0f), | |
FPlane(0.0f, Width / FMath::Tan(HalfFOV) / Height, 0.0f, 0.0f), | |
FPlane(0.0f, 0.0f, ((MinZ == MaxZ) ? 0.0f : MinZ / (MinZ - MaxZ)), 1.0f), | |
FPlane(0.0f, 0.0f, ((MinZ == MaxZ) ? MinZ : -MaxZ * MinZ / (MinZ - MaxZ)), 0.0f) | |
) | |
{ } | |
FORCEINLINE FReversedZPerspectiveMatrix::FReversedZPerspectiveMatrix(float HalfFOV, float Width, float Height, float MinZ) | |
: FMatrix( | |
FPlane(1.0f / FMath::Tan(HalfFOV), 0.0f, 0.0f, 0.0f), | |
FPlane(0.0f, Width / FMath::Tan(HalfFOV) / Height, 0.0f, 0.0f), | |
FPlane(0.0f, 0.0f, 0.0f, 1.0f), | |
FPlane(0.0f, 0.0f, MinZ, 0.0f) | |
) | |
{ } | |
#ifdef _MSC_VER | |
#pragma warning (pop) | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment