Direct3D11: Use reverse depth buffer for better range precision (Thanks 123DMWM)

This should reduce Z-fighting with very far away objects, e.g. the water/bedrock outside level with a view distance of 4096
This commit is contained in:
UnknownShadow200 2023-04-23 21:23:40 +10:00
parent 9411b5d63f
commit cc18e0a449
12 changed files with 112 additions and 77 deletions

View File

@ -84,12 +84,12 @@ static const char PS_SOURCE[] =
" if (color.a < 0.5) { discard; return color; } \n" \
"#endif \n" \
"#ifdef PS_FOG_LINEAR \n" \
" float depth = input.position.z * input.position.w; \n" \
" float depth = input.position.w; \n" \
" float fog = saturate((fogEnd - depth) / fogEnd); \n" \
" color.rgb = lerp(fogColor, color.rgb, fog); \n" \
"#endif \n" \
"#ifdef PS_FOG_DENSITY \n" \
" float depth = input.position.z * input.position.w; \n" \
" float depth = input.position.w; \n" \
" float fog = saturate(exp(fogDensity * depth)); \n" \
" color.rgb = lerp(fogColor, color.rgb, fog); \n" \
"#endif \n" \

View File

@ -27,7 +27,7 @@ static void Camera_OnRawMovement(float deltaX, float deltaY) {
static void PerspectiveCamera_GetProjection(struct Matrix* proj) {
float fovy = Camera.Fov * MATH_DEG2RAD;
float aspectRatio = (float)Game.Width / (float)Game.Height;
Gfx_CalcPerspectiveMatrix(fovy, aspectRatio, (float)Game_ViewDistance, proj);
Gfx_CalcPerspectiveMatrix(proj, fovy, aspectRatio, (float)Game_ViewDistance);
}
static void PerspectiveCamera_GetView(struct Matrix* mat) {
@ -137,7 +137,8 @@ static void PerspectiveCamera_CalcViewBobbing(float t, float velTiltScale) {
static Vec2 FirstPersonCamera_GetOrientation(void) {
struct Entity* p = &LocalPlayer_Instance.Base;
Vec2 v;
v.X = p->Yaw * MATH_DEG2RAD; v.Y = p->Pitch * MATH_DEG2RAD;
v.X = p->Yaw * MATH_DEG2RAD;
v.Y = p->Pitch * MATH_DEG2RAD;
return v;
}
@ -173,7 +174,8 @@ static float dist_third = DEF_ZOOM, dist_forward = DEF_ZOOM;
static Vec2 ThirdPersonCamera_GetOrientation(void) {
struct Entity* p = &LocalPlayer_Instance.Base;
Vec2 v;
v.X = p->Yaw * MATH_DEG2RAD; v.Y = p->Pitch * MATH_DEG2RAD;
v.X = p->Yaw * MATH_DEG2RAD;
v.Y = p->Pitch * MATH_DEG2RAD;
if (cam_isForwardThird) { v.X += MATH_PI; v.Y = -v.Y; }
v.X += cam_rotOffset.X * MATH_DEG2RAD;

View File

@ -202,9 +202,9 @@ CC_API void Gfx_EnableTextureOffset(float x, float y);
CC_API void Gfx_DisableTextureOffset(void);
/* Calculates an orthographic projection matrix suitable with this backend. (usually for 2D) */
void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix);
void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar);
/* Calculates a perspective projection matrix suitable with this backend. (usually for 3D) */
void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix);
void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar);
/* NOTE: Projection matrix calculation is here because it can depend the graphics backend */
/* (e.g. OpenGL uses a Z clip space range of [-1, 1], whereas Direct3D9 uses [0, 1]) */

View File

@ -441,11 +441,11 @@ void Gfx_SetFogMode(FogFunc func) {
*#########################################################################################################################*/
static C3D_Mtx _view, _proj;
void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix) {
Mtx_OrthoTilt(matrix, 0.0f, width, height, 0.0f, ORTHO_NEAR, ORTHO_FAR, true);
void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) {
Mtx_OrthoTilt(matrix, 0.0f, width, height, 0.0f, zNear, zFar, true);
}
void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) {
void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) {
Mtx_PerspTilt(matrix, fov, aspect, 0.1f, zFar, true);
}

View File

@ -432,24 +432,40 @@ void Gfx_SetDynamicVbData(GfxResourceID vb, void* vertices, int vCount) {
/*########################################################################################################################*
*---------------------------------------------------------Matrices--------------------------------------------------------*
*#########################################################################################################################*/
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);
void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) {
// Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthooffcenterrh
// The simplified calculation below uses: L = 0, R = width, T = 0, B = height
// NOTE: This calculation is shared with Direct3D 9 backend
*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) {
/* With reversed z depth, near Z plane can be much closer (with sufficient depth buffer precision) */
/* This reduces clipping with high FOV without sacrificing depth precision for faraway objects */
/* However for low FOV, don't reduce near Z in order to gain a bit more depth precision */
if (depthBits < 24 || fov <= 70 * MATH_DEG2RAD) return 0.05f;
if (fov <= 100 * MATH_DEG2RAD) return 0.025f;
if (fov <= 150 * MATH_DEG2RAD) return 0.0125f;
return 0.00390625f;
}
static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); }
void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) {
// 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_ = Reversed_CalcZNear(fov, 24); // TODO don't always hardcode to 24 bits
void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) {
Matrix_PerspectiveFieldOfView(matrix, fov, aspect, CalcZNear(fov), zFar);
// Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh
// NOTE: This calculation is shared with Direct3D 9 backend
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;
}
//#####################z###################################################################################################
@ -874,7 +890,7 @@ static cc_bool gfx_depthTest, gfx_depthWrite;
static void OM_Clear(void) {
ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer, gfx_clearColor);
ID3D11DeviceContext_ClearDepthStencilView(context, depthbufferView, D3D11_CLEAR_DEPTH, 1.0f, 0);
ID3D11DeviceContext_ClearDepthStencilView(context, depthbufferView, D3D11_CLEAR_DEPTH, 0.0f, 0);
}
static void OM_InitTargets(void) {
@ -906,7 +922,7 @@ static void OM_InitTargets(void) {
static void OM_CreateDepthStates(void) {
D3D11_DEPTH_STENCIL_DESC desc = { 0 };
HRESULT hr;
desc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
desc.DepthFunc = D3D11_COMPARISON_GREATER_EQUAL;
for (int i = 0; i < Array_Elems(om_depthStates); i++) {
desc.DepthEnable = (i & 1) != 0;

View File

@ -716,11 +716,10 @@ void Gfx_DisableTextureOffset(void) {
IDirect3DDevice9_SetTransform(device, D3DTS_TEXTURE0, (const D3DMATRIX*)&Matrix_Identity);
}
void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix) {
void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) {
/* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthooffcenterrh */
/* The simplified calculation below uses: L = 0, R = width, T = 0, B = height */
/* NOTE: This calculation is shared with Direct3D 11 backend */
float zNear = ORTHO_NEAR, zFar = ORTHO_FAR;
*matrix = Matrix_Identity;
matrix->row1.X = 2.0f / width;
@ -733,7 +732,7 @@ void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix) {
}
static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); }
void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) {
void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) {
/* 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 */

View File

@ -188,12 +188,38 @@ void Gfx_SetDepthTest(cc_bool enabled) { GU_Toggle(GU_DEPTH_TEST); }
/*########################################################################################################################*
*---------------------------------------------------------Matrices--------------------------------------------------------*
*#########################################################################################################################*/
void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix) {
Matrix_Orthographic(matrix, 0.0f, width, 0.0f, height, ORTHO_NEAR, ORTHO_FAR);
void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) {
// Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glortho
// The simplified calculation below uses: L = 0, R = width, T = 0, B = height
// NOTE: Shared with OpenGL. might be wrong to do that though?
*matrix = Matrix_Identity;
matrix->row1.X = 2.0f / width;
matrix->row2.Y = -2.0f / height;
matrix->row3.Z = -2.0f / (zFar - zNear);
matrix->row4.X = -1.0f;
matrix->row4.Y = 1.0f;
matrix->row4.Z = -(zFar + zNear) / (zFar - zNear);
}
void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) {
static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); }
void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) {
float zNear = 0.1f;
Matrix_PerspectiveFieldOfView(matrix, fov, aspect, zNear, zFar);
float c = (float)Cotangent(0.5f * fov);
// 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
*matrix = Matrix_Identity;
matrix->row1.X = c / aspect;
matrix->row2.Y = c;
matrix->row3.Z = -(zFar + zNear) / (zFar - zNear);
matrix->row3.W = -1.0f;
matrix->row4.Z = -(2.0f * zFar * zNear) / (zFar - zNear);
matrix->row4.W = 0.0f;
}

View File

@ -93,7 +93,8 @@ static void SetBaseOffset(void) {
static void OnProjectionChanged(void* obj) {
float fov = 70.0f * MATH_DEG2RAD;
float aspectRatio = (float)Game.Width / (float)Game.Height;
Gfx_CalcPerspectiveMatrix(fov, aspectRatio, (float)Game_ViewDistance, &held_blockProjection);
Gfx_CalcPerspectiveMatrix(&held_blockProjection,
fov, aspectRatio, (float)Game_ViewDistance);
}
/* Based off incredible gifs from (Thanks goodlyay!)

View File

@ -166,37 +166,6 @@ void Matrix_Mul(struct Matrix* result, const struct Matrix* left, const struct M
result->row4.W = (((lM41 * rM14) + (lM42 * rM24)) + (lM43 * rM34)) + (lM44 * rM44);
}
void Matrix_Orthographic(struct Matrix* result, float left, float right, float top, float bottom, float zNear, float zFar) {
/* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glortho */
*result = Matrix_Identity;
result->row1.X = 2.0f / (right - left);
result->row2.Y = 2.0f / (top - bottom);
result->row3.Z = -2.0f / (zFar - zNear);
result->row4.X = -(right + left) / (right - left);
result->row4.Y = -(top + bottom) / (top - bottom);
result->row4.Z = -(zFar + zNear) / (zFar - zNear);
}
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 = (float)Tan_Simple(0.5f * fovy);
/* 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->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) {
struct Matrix rotX, rotY, trans;
Matrix_RotateX(&rotX, rot.Y);
@ -274,8 +243,8 @@ void FrustumCulling_CalcFrustumEquations(struct Matrix* projection, struct Matri
FrustumCulling_Normalise(&frustum30, &frustum31, &frustum32, &frustum33);
/* Extract the FAR plane (Different for each graphics backend) */
#if defined CC_BUILD_D3D9
/* OpenGL and Direct3D9 require slightly different behaviour for NEAR clipping planes */
#if defined CC_BUILD_D3D9 || defined CC_BUILD_D3D11
/* OpenGL and Direct3D require slightly different behaviour for NEAR clipping planes */
/* https://www.gamedevs.org/uploads/fast-extraction-viewing-frustum-planes-from-world-view-projection-matrix.pdf */
/* (and because reverse Z is used, 'NEAR' plane is actually the 'FAR' clipping plane) */
frustum40 = clip[2];

View File

@ -129,8 +129,6 @@ CC_API void Matrix_Scale(struct Matrix* result, float x, float y, float z);
/* NOTE: result can be the same pointer as left or right. */
CC_API void Matrix_Mul(struct Matrix* result, const struct Matrix* left, const struct Matrix* right);
void Matrix_Orthographic(struct Matrix* result, float left, float right, float top, float bottom, float zNear, float zFar);
void Matrix_PerspectiveFieldOfView(struct Matrix* result, float fovy, float aspect, float zNear, float zFar);
void Matrix_LookRot(struct Matrix* result, Vec3 pos, Vec2 rot);
cc_bool FrustumCulling_SphereInFrustum(float x, float y, float z, float radius);

View File

@ -201,12 +201,37 @@ void Gfx_SetDepthTest(cc_bool enabled) { gl_Toggle(GL_DEPTH_TEST); }
/*########################################################################################################################*
*---------------------------------------------------------Matrices--------------------------------------------------------*
*#########################################################################################################################*/
void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix) {
Matrix_Orthographic(matrix, 0.0f, width, 0.0f, height, ORTHO_NEAR, ORTHO_FAR);
void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) {
/* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glortho */
/* The simplified calculation below uses: L = 0, R = width, T = 0, B = height */
*matrix = Matrix_Identity;
matrix->row1.X = 2.0f / width;
matrix->row2.Y = -2.0f / height;
matrix->row3.Z = -2.0f / (zFar - zNear);
matrix->row4.X = -1.0f;
matrix->row4.Y = 1.0f;
matrix->row4.Z = -(zFar + zNear) / (zFar - zNear);
}
void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) {
static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); }
void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) {
float zNear = 0.1f;
Matrix_PerspectiveFieldOfView(matrix, fov, aspect, zNear, zFar);
float c = (float)Cotangent(0.5f * fov);
/* 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 */
*matrix = Matrix_Identity;
matrix->row1.X = c / aspect;
matrix->row2.Y = c;
matrix->row3.Z = -(zFar + zNear) / (zFar - zNear);
matrix->row3.W = -1.0f;
matrix->row4.Z = -(2.0f * zFar * zNear) / (zFar - zNear);
matrix->row4.W = 0.0f;
}

View File

@ -18,8 +18,6 @@ const cc_string Gfx_LowPerfMessage = String_FromConst("&eRunning in reduced perf
static const int strideSizes[2] = { SIZEOF_VERTEX_COLOURED, SIZEOF_VERTEX_TEXTURED };
/* Whether mipmaps must be created for all dimensions down to 1x1 or not */
static cc_bool customMipmapsLevels;
#define ORTHO_NEAR -10000.0f
#define ORTHO_FAR 10000.0f
static cc_bool gfx_vsync, gfx_fogEnabled;
static float gfx_minFrameMs;
@ -205,7 +203,8 @@ void Gfx_Make2DQuad(const struct Texture* tex, PackedCol color, struct VertexTex
static cc_bool gfx_hadFog;
void Gfx_Begin2D(int width, int height) {
struct Matrix ortho;
Gfx_CalcOrthoMatrix((float)width, (float)height, &ortho);
Gfx_CalcOrthoMatrix(&ortho,
(float)width, (float)height, -10000.0f, 10000.0f);
Gfx_LoadMatrix(MATRIX_PROJECTION, &ortho);
Gfx_LoadIdentityMatrix(MATRIX_VIEW);