From da8ab3d97998d85f2a847333e67f9105cf5ce8e6 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sun, 23 Apr 2023 12:29:31 +1000 Subject: [PATCH] Simplify projection matrix calculation and add more explanatory comments --- src/Graphics.h | 7 +++++-- src/Graphics_D3D9.c | 36 +++++++++++++++++++++++++++++------- src/Vectors.c | 14 +++++++------- src/_GraphicsBase.h | 3 +++ 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/Graphics.h b/src/Graphics.h index b43499cb4..7d5dbf467 100644 --- a/src/Graphics.h +++ b/src/Graphics.h @@ -200,10 +200,13 @@ CC_API void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix); CC_API void Gfx_LoadIdentityMatrix(MatrixType type); CC_API void Gfx_EnableTextureOffset(float x, float y); CC_API void Gfx_DisableTextureOffset(void); -/* Calculates an orthographic matrix suitable with this backend. (usually for 2D) */ + +/* Calculates an orthographic projection matrix suitable with this backend. (usually for 2D) */ void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix); -/* Calculates a projection matrix suitable with this backend. (usually for 3D) */ +/* Calculates a perspective projection matrix suitable with this backend. (usually for 3D) */ void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix); +/* NOTE: Projection matrix calculation is here because it can depend the graphics backend */ +/* (e.g. OpenGL uses a Z clip range of [-1, 1], whereas Direct3D9 uses a range of [0, 1]) */ /* Outputs a .png screenshot of the backbuffer. */ cc_result Gfx_TakeScreenshot(struct Stream* output); diff --git a/src/Graphics_D3D9.c b/src/Graphics_D3D9.c index ebc001ad3..04c8a90bb 100644 --- a/src/Graphics_D3D9.c +++ b/src/Graphics_D3D9.c @@ -717,9 +717,18 @@ void Gfx_DisableTextureOffset(void) { } void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix) { - Matrix_Orthographic(matrix, 0.0f, width, 0.0f, height, ORTHO_NEAR, ORTHO_FAR); - matrix->row3.Z = 1.0f / (ORTHO_NEAR - ORTHO_FAR); - matrix->row4.Z = ORTHO_NEAR / (ORTHO_NEAR - ORTHO_FAR); + /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthooffcenterrh */ + /* The simplified calculation below uses: L = 0, R = width, T = 0, B = height */ + float zNear = ORTHO_NEAR, zFar = ORTHO_FAR; + *matrix = Matrix_Identity; + + matrix->row1.X = 2.0f / width; + matrix->row2.Y = -2.0f / height; + matrix->row3.Z = 1.0f / (zNear - zFar); + + matrix->row4.X = -1.0f; + matrix->row4.Y = 1.0f; + matrix->row4.Z = zNear / (zNear - zFar); } static float CalcZNear(float fov) { @@ -732,11 +741,24 @@ static float CalcZNear(float fov) { return 0.00390625f; } +static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) { - Matrix_PerspectiveFieldOfView(matrix, fov, aspect, CalcZNear(fov), zFar); - /* Adjust the projection matrix to produce reversed Z values */ - matrix->row3.Z = -matrix->row3.Z - 1.0f; - matrix->row4.Z = -matrix->row4.Z; + /* Deliberately swap zNear/zFar in projection matrix calculation to produce */ + /* a projection matrix that results in a reversed depth buffer */ + /* https://developer.nvidia.com/content/depth-precision-visualized */ + float zNear_ = zFar; + float zFar_ = CalcZNear(fov); + + /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh */ + float c = (float)Cotangent(0.5f * fov); + *matrix = Matrix_Identity; + + matrix->row1.X = c / aspect; + matrix->row2.Y = c; + matrix->row3.Z = zFar_ / (zNear_ - zFar_); + matrix->row3.W = -1.0f; + matrix->row4.Z = (zNear_ * zFar_) / (zNear_ - zFar_); + matrix->row4.W = 0.0f; } diff --git a/src/Vectors.c b/src/Vectors.c index 083bb6d67..371c5bd8e 100644 --- a/src/Vectors.c +++ b/src/Vectors.c @@ -167,7 +167,7 @@ void Matrix_Mul(struct Matrix* result, const struct Matrix* left, const struct M } void Matrix_Orthographic(struct Matrix* result, float left, float right, float top, float bottom, float zNear, float zFar) { - /* Transposed, source https://msdn.microsoft.com/en-us/library/dd373965(v=vs.85).aspx */ + /* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glortho */ *result = Matrix_Identity; result->row1.X = 2.0f / (right - left); @@ -181,20 +181,20 @@ void Matrix_Orthographic(struct Matrix* result, float left, float right, float t static double Tan_Simple(double x) { return Math_Sin(x) / Math_Cos(x); } void Matrix_PerspectiveFieldOfView(struct Matrix* result, float fovy, float aspect, float zNear, float zFar) { - float c = zNear * (float)Tan_Simple(0.5f * fovy); + float c = (float)Tan_Simple(0.5f * fovy); - /* Transposed, source https://msdn.microsoft.com/en-us/library/dd373537(v=vs.85).aspx */ + /* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum */ /* For a FOV based perspective matrix, left/right/top/bottom are calculated as: */ /* left = -c * aspect, right = c * aspect, bottom = -c, top = c */ /* Calculations are simplified because of left/right and top/bottom symmetry */ *result = Matrix_Identity; - result->row4.W = 0.0f; - result->row1.X = zNear / (c * aspect); - result->row2.Y = zNear / c; - result->row4.Z = -(2.0f * zFar * zNear) / (zFar - zNear); + result->row1.X = 1.0f / (c * aspect); + result->row2.Y = 1.0f / c; result->row3.Z = -(zFar + zNear) / (zFar - zNear); + result->row4.Z = -(2.0f * zFar * zNear) / (zFar - zNear); result->row3.W = -1.0f; + result->row4.W = 0.0f; } void Matrix_LookRot(struct Matrix* result, Vec3 pos, Vec2 rot) { diff --git a/src/_GraphicsBase.h b/src/_GraphicsBase.h index c7aa553f1..e60597f7f 100644 --- a/src/_GraphicsBase.h +++ b/src/_GraphicsBase.h @@ -236,6 +236,9 @@ void Gfx_RestoreAlphaState(cc_uint8 draw) { } +/*########################################################################################################################* +*---------------------------------------------------------Textures--------------------------------------------------------* +*#########################################################################################################################*/ static void CopyTextureData(void* dst, int dstStride, const struct Bitmap* src, int srcStride) { /* We need to copy scanline by scanline, as generally srcStride != dstStride */ cc_uint8* src_ = (cc_uint8*)src->scan0;