Skip to content

Commit 0eefc86

Browse files
dsvenssonclaude
andcommitted
RENDERER: Infinite reverse-z.
Finish the dormant reverse-z support (glClipControl probing existed but was disabled) for both the classic (GLC) and modern (GLM) renderers, behind a new gl_reverse_z cvar (default on, applies on vid_restart, documented in help_variables.json). Reverse-z with a 32-bit float depth buffer distributes depth precision evenly in view distance, eliminating z-fighting on distant geometry. The projection now also uses an infinite far plane, so r_farclip no longer clips geometry: useful for mappers placing detail far outside the playable area, and for very large maps (e.g. surf maps) that exceed the conventional far plane. Reverse-z only activates when it actually pays off, i.e. when a float depth buffer will be used: requires vid_framebuffer 1 with vid_framebuffer_depthformat 0 (best) or 4 (32-bit float), plus GL 4.5 or GL_ARB_clip_control. Falls back to conventional depth otherwise (the default framebuffer only has fixed-point depth, where reverse-z brings no precision benefit), with a console notice explaining why. Details: - Clip control (GL_ZERO_TO_ONE) and glClearDepth(0) are now set once per context in GL_InitialiseReversedDepth() instead of per-framebuffer; FBO depth format prefers GL_DEPTH_COMPONENT32F when reversed. - R_Frustum: reversed branch uses the infinite-far projection (m[10]=0, m[14]=zNear; near->1, infinity->0). GLC loads the computed matrix via glLoadMatrixf instead of rebuilding a conventional one with glFrustum. - Depth range state translation fixed and enabled: logical [near,far] maps to window [1-far, 1-near], so the viewmodel range [0,0.3] becomes [0.7,1.0] (GLC path). Depth funcs were already inverted via the existing tables; also force-sync glDepthFunc at init so state-change elision can't leave the context at the GL default GL_LESS. - GLM viewmodel: replace the old clip-space scale (z *= 1/0.3, which near-clipped the gun e.g. when gibbed) with z' = 0.7*w + 0.3*z, the clip-space equivalent of glDepthRange(0.7, 1.0); it can never near-clip and works within batched alias-model draws where per-draw depth range isn't possible. - Fog: gl_FragCoord.z/gl_FragCoord.w inverts under reverse-z, so all fog call sites now use a fogFragDepth() macro that expands to 1.0/gl_FragCoord.w (exact eye distance) when reversed, legacy expression otherwise. EZQ_REVERSED_DEPTH is now injected globally into all shaders. - Polygon offsets are negated when reversed ("farther" = smaller depth). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 25c025f commit 0eefc86

21 files changed

Lines changed: 119 additions & 48 deletions

help_variables.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5564,6 +5564,13 @@
55645564
],
55655565
"type": "integer"
55665566
},
5567+
"gl_reverse_z": {
5568+
"default": "1",
5569+
"desc": "Enables reverse-z depth buffering with an infinite far plane suitable for maps of any size.",
5570+
"group-id": "35",
5571+
"remarks": "Takes effect on vid_restart. Only activates when a 32-bit float depth buffer is in use: requires vid_framebuffer 1, vid_framebuffer_depthformat 0 or 4, and OpenGL 4.5+ or the GL_ARB_clip_control extension. Falls back to conventional depth buffering otherwise.",
5572+
"type": "boolean"
5573+
},
55675574
"gl_rl_globe": {
55685575
"default": "0",
55695576
"desc": "Helps customize rocket light independent of gl_flashblend.",

src/gl_framebuffer.c

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -279,15 +279,38 @@ void GL_InitialiseFramebufferHandling(void)
279279
GL_LoadOptionalFunction(glNamedRenderbufferStorageMultisample);
280280
}
281281

282-
// meag: disabled (classic needs glFrustum replaced, modern needs non-rubbish viewweapon
283-
// depth hack, and near-plane clipping issues when the player is gibbed)
284-
/*if (GL_VersionAtLeast(4, 5) || SDL_GL_ExtensionSupported("GL_ARB_clip_control")) {
282+
if (GL_VersionAtLeast(4, 5) || SDL_GL_ExtensionSupported("GL_ARB_clip_control")) {
285283
GL_LoadOptionalFunction(glClipControl);
286-
}*/
284+
}
287285

288286
memset(framebuffer_data, 0, sizeof(framebuffer_data));
289287
}
290288

289+
void GL_InitialiseReversedDepth(void)
290+
{
291+
extern cvar_t gl_reverse_z;
292+
int depthformat = bound(0, vid_framebuffer_depthformat.integer, r_depthformat_count - 1);
293+
qbool high_res_depth;
294+
295+
// reverse-z only pays off with a floating-point depth buffer, which requires
296+
// rendering 3D to a framebuffer object (the default framebuffer is fixed-point)
297+
high_res_depth = vid_framebuffer.integer && GL_Supported(R_SUPPORT_DEPTH32F) &&
298+
(depthformat == r_depthformat_best || depthformat == r_depthformat_32bit_float);
299+
300+
glConfig.reversed_depth = (gl_reverse_z.integer && GL_Available(glClipControl) && high_res_depth);
301+
302+
if (glConfig.reversed_depth) {
303+
// near plane maps to depth 1, infinity to 0, clear depth 0
304+
GL_Procedure(glClipControl, GL_LOWER_LEFT, GL_ZERO_TO_ONE);
305+
glClearDepth(0.0f);
306+
}
307+
else {
308+
// fresh contexts default to GL_NEGATIVE_ONE_TO_ONE; don't call clipControl
309+
// here as the pointer may be NULL
310+
glClearDepth(1.0f);
311+
}
312+
}
313+
291314
void GL_FramebufferSetFiltering(qbool linear)
292315
{
293316
texture_ref tex = framebuffer_data[framebuffer_std].texture[fbtex_standard];
@@ -381,7 +404,7 @@ qbool GL_FramebufferCreate(framebuffer_id id, int width, int height)
381404
if (framebuffer_depth_buffer[id]) {
382405
GLenum depthFormat = glDepthFormats[bound(0, vid_framebuffer_depthformat.integer, r_depthformat_count - 1)];
383406
if (depthFormat == 0) {
384-
depthFormat = GL_Available(glClipControl) ? GL_DEPTH_COMPONENT32F : GL_DEPTH_COMPONENT32;
407+
depthFormat = glConfig.reversed_depth ? GL_DEPTH_COMPONENT32F : GL_DEPTH_COMPONENT32;
385408
}
386409
if (depthFormat == GL_DEPTH_COMPONENT32F && !GL_Supported(R_SUPPORT_DEPTH32F)) {
387410
depthFormat = GL_DEPTH_COMPONENT32;
@@ -411,23 +434,6 @@ qbool GL_FramebufferCreate(framebuffer_id id, int width, int height)
411434

412435
GL_FramebufferTexture(fb->glref, GL_DEPTH_ATTACHMENT, GL_TextureNameFromReference(fb->texture[fbtex_depth]), 0);
413436
#endif
414-
415-
if (GL_Available(glClipControl)) {
416-
if (depthFormat == GL_DEPTH_COMPONENT32F) {
417-
GL_Procedure(glClipControl, GL_LOWER_LEFT, GL_ZERO_TO_ONE);
418-
glClearDepth(0.0f);
419-
glConfig.reversed_depth = true;
420-
}
421-
else {
422-
GL_Procedure(glClipControl, GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
423-
glClearDepth(1.0f);
424-
glConfig.reversed_depth = false;
425-
}
426-
}
427-
else {
428-
glConfig.reversed_depth = false;
429-
glClearDepth(1.0f);
430-
}
431437
}
432438

433439
GL_FramebufferTexture(fb->glref, GL_COLOR_ATTACHMENT0, GL_TextureNameFromReference(fb->texture[fbtex_standard]), 0);

src/gl_framebuffer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ typedef enum {
4343
} fbtex_id;
4444

4545
void GL_InitialiseFramebufferHandling(void);
46+
void GL_InitialiseReversedDepth(void);
4647
qbool GL_FramebufferCreate(framebuffer_id id, int width, int height);
4748
void GL_FramebufferDelete(framebuffer_id id);
4849
void GL_FramebufferStartUsing(framebuffer_id id);

src/gl_program.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,20 @@ static void GL_BuildCoreDefinitions(void)
14981498
memset(macro_definitions, 0, sizeof(macro_definitions));
14991499
strlcpy(macro_definitions, (R_UseModernOpenGL() ? "#define EZ_MODERN_GL\n" : "#define EZ_LEGACY_GL\n"), sizeof(macro_definitions));
15001500

1501+
// Reverse-z define + fog depth helper (reaches every GLM and GLC shader).
1502+
// gl_FragCoord.z/gl_FragCoord.w inverts under reverse-z (fog would vanish);
1503+
// 1.0/gl_FragCoord.w = w_clip = eye distance, depth-convention independent.
1504+
if (glConfig.reversed_depth) {
1505+
strlcat(macro_definitions, "#define EZQ_REVERSED_DEPTH\n", sizeof(macro_definitions));
1506+
}
1507+
strlcat(macro_definitions,
1508+
"#ifdef EZQ_REVERSED_DEPTH\n"
1509+
"#define fogFragDepth() (1.0 / gl_FragCoord.w)\n"
1510+
"#else\n"
1511+
"#define fogFragDepth() (gl_FragCoord.z / gl_FragCoord.w)\n"
1512+
"#endif\n",
1513+
sizeof(macro_definitions));
1514+
15011515
#ifdef RENDERER_OPTION_MODERN_OPENGL
15021516
if (R_UseModernOpenGL()) {
15031517
if (GL_VersionAtLeast(4, 3)) {

src/gl_state.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,8 @@ void GL_ApplyRenderingState(r_state_id id)
365365
extern cvar_t gl_brush_polygonoffset;
366366
rendering_state_t* current = &opengl.rendering_state;
367367
float zRange[2] = {
368-
glConfig.reversed_depth && false ? 1.0f - state->depth.nearRange : state->depth.nearRange,
369-
glConfig.reversed_depth && false ? 1.0f - state->depth.farRange : state->depth.farRange,
368+
glConfig.reversed_depth ? 1.0f - state->depth.farRange : state->depth.nearRange,
369+
glConfig.reversed_depth ? 1.0f - state->depth.nearRange : state->depth.farRange,
370370
};
371371

372372
R_TraceEnterRegion(va("GL_ApplyRenderingState(%s)", state->name), true);
@@ -589,6 +589,17 @@ void GL_InitialiseState(void)
589589
{
590590
R_InitRenderingState(r_state_default_opengl, false, "opengl", vao_none);
591591
opengl.rendering_state = states[r_state_default_opengl];
592+
593+
// The state tracker seeds depth.func without issuing GL calls; GL's boot default
594+
// is GL_LESS, so under reverse-z change-elision would strand the real state at
595+
// GL_LESS. Force one explicit glDepthFunc for the seeded func.
596+
if (glConfig.reversed_depth) {
597+
glDepthFunc(glReversedDepthFunctions[opengl.rendering_state.depth.func]);
598+
}
599+
else {
600+
glDepthFunc(glDepthFunctions[opengl.rendering_state.depth.func]);
601+
}
602+
592603
R_InitialiseStates();
593604

594605
R_SetIdentityMatrix(R_ProjectionMatrix());
@@ -955,6 +966,12 @@ void R_CustomPolygonOffset(r_polygonoffset_t mode)
955966
float units = (mode == r_polygonoffset_standard ? bound(0, gl_brush_polygonoffset.value, 2) : 1);
956967
qbool enabled = (mode == r_polygonoffset_standard || mode == r_polygonoffset_outlines) && units != 0;
957968

969+
if (glConfig.reversed_depth) {
970+
// "farther" = smaller depth under reverse-z; negation preserves non-zero
971+
factor = -factor;
972+
units = -units;
973+
}
974+
958975
if (enabled) {
959976
if (!current->polygonOffset.fillEnabled) {
960977
glEnable(GL_POLYGON_OFFSET_FILL);

src/glm_aliasmodel.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ extern float r_framelerp;
108108

109109
#define DRAW_DETAIL_TEXTURES (1 << 0)
110110
#define DRAW_CAUSTIC_TEXTURES (1 << 1)
111-
#define DRAW_REVERSED_DEPTH (1 << 2)
112111
#define DRAW_LERP_MUZZLEHACK (1 << 3)
113112
#define DRAW_FLAT_SHADING (1 << 4)
114113
#define DRAW_INSTANCED (1 << 5)
@@ -153,7 +152,6 @@ qbool GLM_CompileAliasModelProgram(void)
153152

154153
unsigned int drawAlias_desiredOptions =
155154
(r_refdef2.drawCaustics ? DRAW_CAUSTIC_TEXTURES : 0) |
156-
(glConfig.reversed_depth ? DRAW_REVERSED_DEPTH : 0) |
157155
(r_lerpmuzzlehack.integer ? DRAW_LERP_MUZZLEHACK : 0) |
158156
(gl_smoothmodels.integer ? 0 : DRAW_FLAT_SHADING) |
159157
(GL_Supported(R_SUPPORT_INSTANCED_RENDERING) ? DRAW_INSTANCED : 0);
@@ -179,9 +177,6 @@ qbool GLM_CompileAliasModelProgram(void)
179177
}
180178

181179
strlcat(included_definitions, va("#define SAMPLER_COUNT %d\n", material_samplers_max), sizeof(included_definitions));
182-
if (drawAlias_desiredOptions & DRAW_REVERSED_DEPTH) {
183-
strlcat(included_definitions, "#define EZQ_REVERSED_DEPTH\n", sizeof(included_definitions));
184-
}
185180
if (drawAlias_desiredOptions & DRAW_LERP_MUZZLEHACK) {
186181
strlcat(included_definitions, "#define EZQ_ALIASMODEL_MUZZLEHACK\n", sizeof(included_definitions));
187182
}

src/glsl/draw_aliasmodel.fragment.glsl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ void main()
8989

9090
frag_colour = color1 * tex + color2 * altTex;
9191
#ifdef DRAW_FOG
92-
frag_colour = applyFogBlend(frag_colour, gl_FragCoord.z / gl_FragCoord.w);
92+
frag_colour = applyFogBlend(frag_colour, fogFragDepth());
9393
#endif
9494
frag_colour *= shell_alpha;
9595
}
@@ -105,13 +105,13 @@ void main()
105105
}
106106
#endif
107107
#ifdef DRAW_FOG
108-
frag_colour = applyFog(frag_colour, gl_FragCoord.z / gl_FragCoord.w);
108+
frag_colour = applyFog(frag_colour, fogFragDepth());
109109
#endif
110110
frag_colour *= fsBaseColor.a;
111111
}
112112
} else {
113113
#ifdef DRAW_FOG
114-
frag_colour = applyFog(frag_colour, gl_FragCoord.z / gl_FragCoord.w);
114+
frag_colour = applyFog(frag_colour, fogFragDepth());
115115
#endif
116116
}
117117
}

src/glsl/draw_aliasmodel.vertex.glsl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ void main()
9191

9292
if ((fsFlags & AMF_WEAPONMODEL) != 0) {
9393
#ifdef EZQ_REVERSED_DEPTH
94-
gl_Position.z *= 1 / 0.3;
94+
// compress depth to the nearest 30% of the window range, equivalent to
95+
// glDepthRange(0.7, 1.0): window z becomes 0.7 + 0.3 * z_ndc, and clip
96+
// z stays within [0.7w, w] so the near plane can never clip it
97+
gl_Position.z = 0.7 * gl_Position.w + 0.3 * gl_Position.z;
9598
#else
9699
gl_Position.z *= 0.3;
97100
#endif

src/glsl/draw_sprites.fragment.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ void main()
1919
}
2020

2121
#ifdef DRAW_FOG
22-
frag_color = applyFogBlend(frag_color, gl_FragCoord.z / gl_FragCoord.w);
22+
frag_color = applyFogBlend(frag_color, fogFragDepth());
2323
#endif
2424
}

src/glsl/draw_world.fragment.glsl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ void main()
167167
frag_colour = vec4(FlatColor, 1);
168168
}
169169
#ifdef DRAW_FOG
170-
frag_colour = applyFog(frag_colour, gl_FragCoord.z / gl_FragCoord.w);
170+
frag_colour = applyFog(frag_colour, fogFragDepth());
171171
#endif
172172
#ifdef DRAW_ALPHATEST_ENABLED
173173
frag_colour *= alpha;
@@ -212,7 +212,7 @@ void main()
212212
frag_colour = vec4(lmColor.rgb, 1) * frag_colour;
213213
}
214214
#ifdef DRAW_FOG
215-
frag_colour = applyFog(frag_colour, gl_FragCoord.z / gl_FragCoord.w);
215+
frag_colour = applyFog(frag_colour, fogFragDepth());
216216
#endif
217217
#ifdef DRAW_ALPHATEST_ENABLED
218218
frag_colour *= alpha;
@@ -262,7 +262,7 @@ void main()
262262
#endif
263263

264264
#ifdef DRAW_FOG
265-
frag_colour = applyFog(frag_colour, gl_FragCoord.z / gl_FragCoord.w);
265+
frag_colour = applyFog(frag_colour, fogFragDepth());
266266
#endif
267267

268268
#ifdef DRAW_ALPHATEST_ENABLED

0 commit comments

Comments
 (0)