Skip to content

Commit a331254

Browse files
committed
Use a piecewise function for distance -> fog alpha
The original Quake 3 fog code in principle used the equation alpha = sqrt(density * distance in fog). But the values were looked up in an image that had a resolution that for small distances was way too low to approximate the function well. Actually using sqrt at the low range is bad because sqrt has an infinite at zero. Use a linear ramp for the first bit which prevents hard edges caused by using pure sqrt and gives a similar result to the old code. This fixes color banding caused by the discrete fog texture. Also, it makes the thickness of the fog appear the same regardless of how far you are away from it.
1 parent efc492d commit a331254

File tree

4 files changed

+35
-81
lines changed

4 files changed

+35
-81
lines changed
Lines changed: 33 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,45 @@
11
/*
22
===========================================================================
3-
Copyright (C) 1999-2005 Id Software, Inc.
4-
Copyright (C) 2006-2011 Robert Beckebans <trebor_7@users.sourceforge.net>
53
6-
This file is part of Daemon source code.
4+
Daemon BSD Source Code
5+
Copyright (c) 2025 Daemon Developers
6+
All rights reserved.
7+
8+
This file is part of the Daemon BSD Source Code (Daemon Source Code).
9+
10+
Redistribution and use in source and binary forms, with or without
11+
modification, are permitted provided that the following conditions are met:
12+
* Redistributions of source code must retain the above copyright
13+
notice, this list of conditions and the following disclaimer.
14+
* Redistributions in binary form must reproduce the above copyright
15+
notice, this list of conditions and the following disclaimer in the
16+
documentation and/or other materials provided with the distribution.
17+
* Neither the name of the Daemon developers nor the
18+
names of its contributors may be used to endorse or promote products
19+
derived from this software without specific prior written permission.
20+
21+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24+
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY
25+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
731
8-
Daemon source code is free software; you can redistribute it
9-
and/or modify it under the terms of the GNU General Public License as
10-
published by the Free Software Foundation; either version 2 of the License,
11-
or (at your option) any later version.
12-
13-
Daemon source code is distributed in the hope that it will be
14-
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16-
GNU General Public License for more details.
17-
18-
You should have received a copy of the GNU General Public License
19-
along with Daemon source code; if not, write to the Free Software
20-
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2132
===========================================================================
2233
*/
2334

24-
float FogTable(float s)
25-
{
26-
return sqrt(floor(s * 255.0) / 255);
27-
}
28-
29-
float FogFactor(float s, float t)
30-
{
31-
s -= 1.0f / 512.0f;
32-
33-
if ( s < 0 )
34-
{
35-
return 0;
36-
}
37-
38-
if ( t < 1.0f / 32.0f )
39-
{
40-
return 0;
41-
}
42-
43-
if ( t < 31.0f / 32.0f )
44-
{
45-
s *= ( t - 1.0f / 32.0f ) / ( 30.0f / 32.0f );
46-
}
47-
48-
s *= 8;
49-
50-
if ( s > 1.0f )
51-
{
52-
s = 1.0f;
53-
}
54-
55-
return FogTable(s);
56-
}
57-
5835
float GetFogAlpha(float s, float t)
5936
{
60-
float sfloor = floor(s * 256 + 0.5) - 0.5;
61-
float sceil = sfloor + 1;
62-
sfloor = clamp(sfloor, 0.5, 255.5) / 256;
63-
sceil = clamp(sceil, 0.5, 255.5) / 256;
64-
float smix = sfloor < sceil ? (s - sfloor) * 256 : 0.5;
37+
t = clamp(t, 0.0, 1.0);
6538

66-
float tfloor = floor(t * 32 + 0.5) - 0.5;
67-
float tceil = tfloor + 1;
68-
tfloor = clamp(tfloor, 0.5, 31.5) / 32;
69-
tceil = clamp(tceil, 0.5, 31.5) / 32;
70-
float tmix = tfloor < tceil ? (t - tfloor) * 32 : 0.5;
39+
float x = min(1, s * t);
7140

72-
float f00 = FogFactor(sfloor, tfloor);
73-
float f01 = FogFactor(sfloor, tceil);
74-
float f10 = FogFactor(sceil, tfloor);
75-
float f11 = FogFactor(sceil, tceil);
76-
return mix(mix(f00, f01, tmix), mix(f10, f11, tmix), smix);
41+
// sqrt(x) is bad near 0 because it increases too quickly resulting in sharp edges.
42+
// x ≤ 1/32: √32 * x
43+
// x ≥ 1/32: √x
44+
return min(sqrt(32.0) * x, sqrt(x));
7745
}

src/engine/renderer/glsl_source/fogQuake3_fp.glsl

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ void main()
3939
#insert material_fp
4040

4141
float s = length(var_ScaledViewerOffset);
42-
43-
s += 1.0 / 512.0;
44-
4542
float t = step( 0, var_FogPlaneDistance );
4643

4744
if ( u_FogEyeT < 0 ) // eye outside fog
@@ -50,8 +47,6 @@ void main()
5047
t *= var_FogPlaneDistance / ( max( 0, var_FogPlaneDistance ) - u_FogEyeT );
5148
}
5249

53-
t = 1.0 / 32.0 + ( 30.0 / 32.0 ) * t;
54-
5550
vec4 color = vec4(1, 1, 1, GetFogAlpha(s, t));
5651

5752
color *= var_Color;

src/engine/renderer/glsl_source/fogQuake3_vp.glsl

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,7 @@ void main()
7979
var_ScaledViewerOffset = u_FogDensity * (position.xyz - u_ViewOrigin);
8080

8181
// calculate the length in fog
82-
float t = dot(position.xyz, u_FogDepthVector.xyz) + u_FogDepthVector.w;
83-
84-
// HACK: increase cutoff to avoid dark lines at the edge of BSP triangles that stop
85-
// right at the fog plane (when viewed from under fog)
86-
// But don't do it for models as this causes a doubly-fogged light band at the fog plane
87-
#if !defined(USE_VERTEX_ANIMATION) && !defined(USE_VERTEX_SKINNING)
88-
t += 1.5;
89-
#endif
90-
91-
var_FogPlaneDistance = t;
82+
var_FogPlaneDistance = dot(position.xyz, u_FogDepthVector.xyz) + u_FogDepthVector.w;
9283

9384
var_Color = color;
9485
}

src/engine/renderer/tr_bsp.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3335,7 +3335,7 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump )
33353335
out->color.SetAlpha( 1 );
33363336

33373337
d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque;
3338-
out->tcScale = 1.0f / ( d * 8 );
3338+
out->tcScale = 1.0f / d;
33393339

33403340
// ydnar: global fog sets clearcolor/zfar
33413341
if ( out->originalBrushNumber == -1 )

0 commit comments

Comments
 (0)