From ecb9f923dc85068ad8b87770755eee609f2c598a Mon Sep 17 00:00:00 2001 From: slipher Date: Sun, 1 Feb 2026 04:48:41 -0600 Subject: [PATCH 01/12] Unused field fog_t::fogParms --- src/engine/renderer/GeometryOptimiser.h | 1 - src/engine/renderer/tr_bsp.cpp | 2 -- src/engine/renderer/tr_local.h | 1 - 3 files changed, 4 deletions(-) diff --git a/src/engine/renderer/GeometryOptimiser.h b/src/engine/renderer/GeometryOptimiser.h index 3a24a972d9..cfce0db7e1 100644 --- a/src/engine/renderer/GeometryOptimiser.h +++ b/src/engine/renderer/GeometryOptimiser.h @@ -87,7 +87,6 @@ void MarkShaderBuildScreen( const shaderStage_t* pStage ); void MarkShaderBuildPortal( const shaderStage_t* pStage ); void MarkShaderBuildHeatHaze( const shaderStage_t* pStage ); void MarkShaderBuildLiquid( const shaderStage_t* pStage ); -void MarkShaderBuildFog( const shaderStage_t* pStage ); void MarkShaderBuildIQM( const IQModel_t* model ); void MarkShaderBuildMDV( const mdvModel_t* model ); diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index d03844d401..cae13f0349 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -3321,8 +3321,6 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) // of trying to create an implicit shader from an image... shader = R_FindShader( fogs->shader, RSF_3D ); - out->fogParms = shader->fogParms; - out->color = Color::Adapt( shader->fogParms.color ); if ( tr.worldLinearizeTexture ) diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 100f14228c..34f306d005 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1356,7 +1356,6 @@ enum Color::Color color; // in packed byte format float tcScale; // texture coordinate vector scales - fogParms_t fogParms; // for clipping distance in fog when outside bool hasSurface; From 69a309d81dc489fbbdb70a176791c10259940973 Mon Sep 17 00:00:00 2001 From: slipher Date: Fri, 6 Feb 2026 19:21:13 -0600 Subject: [PATCH 02/12] already unused fog defs --- src/engine/renderer/gl_shader.cpp | 1 - src/engine/renderer/tr_cmds.cpp | 4 ---- src/engine/renderer/tr_local.h | 5 ----- 3 files changed, 10 deletions(-) diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 0526d2c5aa..7e694b8cd7 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -2733,7 +2733,6 @@ GLShader_fogGlobal::GLShader_fogGlobal() : void GLShader_fogGlobal::SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) { - glUniform1i( glGetUniformLocation( shaderProgram->id, "u_ColorMap" ), 0 ); glUniform1i( glGetUniformLocation( shaderProgram->id, "u_DepthMap" ), 1 ); } diff --git a/src/engine/renderer/tr_cmds.cpp b/src/engine/renderer/tr_cmds.cpp index 0dd9c83371..34845afa65 100644 --- a/src/engine/renderer/tr_cmds.cpp +++ b/src/engine/renderer/tr_cmds.cpp @@ -74,10 +74,6 @@ void R_PerformanceCounters() { Log::Notice("viewcluster: %i", tr.visClusters[ tr.visIndex ] ); } - else if ( r_speeds->integer == Util::ordinal(renderSpeeds_t::RSPEEDS_FOG )) - { - Log::Notice("fog srf:%i batches:%i", backEnd.pc.c_fogSurfaces, backEnd.pc.c_fogBatches ); - } else if ( r_speeds->integer == Util::ordinal(renderSpeeds_t::RSPEEDS_NEAR_FAR )) { Log::Notice("zNear: %.0f zFar: %.0f", tr.viewParms.zNear, tr.viewParms.zFar ); diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 34f306d005..41d5aee4ad 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -350,7 +350,6 @@ enum class ssaoMode { RSPEEDS_GENERAL = 1, RSPEEDS_CULLING, RSPEEDS_VIEWCLUSTER, - RSPEEDS_FOG, RSPEEDS_CHC, RSPEEDS_NEAR_FAR, }; @@ -2121,7 +2120,6 @@ enum int c_leafs; }; -#define FOG_TABLE_SIZE 256 #define FUNCTABLE_SIZE 1024 #define FUNCTABLE_SIZE2 10 #define FUNCTABLE_MASK ( FUNCTABLE_SIZE - 1 ) @@ -2183,9 +2181,6 @@ enum int c_vboVertexes; int c_vboIndexes; - int c_fogSurfaces; - int c_fogBatches; - int c_multiDrawElements; int c_multiDrawPrimitives; int c_multiVboIndexes; From 27a6fc32bb33ef9bd0d2b7d34d919b109289336f Mon Sep 17 00:00:00 2001 From: slipher Date: Sun, 1 Feb 2026 05:21:48 -0600 Subject: [PATCH 03/12] Refactor: save fogparms only on shader, not in fog_t --- src/engine/renderer/Material.cpp | 4 ++-- src/engine/renderer/tr_backend.cpp | 4 ++-- src/engine/renderer/tr_bsp.cpp | 19 +------------------ src/engine/renderer/tr_local.h | 5 ++--- src/engine/renderer/tr_shade.cpp | 4 ++-- src/engine/renderer/tr_shader.cpp | 20 +++++++++++++------- 6 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/engine/renderer/Material.cpp b/src/engine/renderer/Material.cpp index a091109746..db4112daf9 100644 --- a/src/engine/renderer/Material.cpp +++ b/src/engine/renderer/Material.cpp @@ -1114,11 +1114,11 @@ void BindShaderFog( Material* material ) { // since fognum is grouped with the GL state stuff, segregating each fognum in a separate draw call. gl_fogQuake3ShaderMaterial->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - gl_fogQuake3ShaderMaterial->SetUniform_FogDensity( fog->tcScale ); + gl_fogQuake3ShaderMaterial->SetUniform_FogDensity( 1.0f / fog->shader->fogParms.depthForOpaque ); gl_fogQuake3ShaderMaterial->SetUniform_FogDepthVector( fogDepthVector ); gl_fogQuake3ShaderMaterial->SetUniform_FogEyeT( eyeT ); - gl_fogQuake3ShaderMaterial->SetUniform_ColorGlobal_Uint( fog->color ); + gl_fogQuake3ShaderMaterial->SetUniform_ColorGlobal_Uint( fog->shader->fogParms.color ); gl_fogQuake3ShaderMaterial->SetUniform_ModelMatrix( backEnd.orientation.transformMatrix ); gl_fogQuake3ShaderMaterial->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[glState.stackIndex] ); diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 18795fec41..774acefd3e 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1458,9 +1458,9 @@ void RB_RenderGlobalFog() GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); - gl_fogGlobalShader->SetUniform_FogDensity( fog->tcScale ); + gl_fogGlobalShader->SetUniform_FogDensity( 1.0f / fog->shader->fogParms.depthForOpaque ); gl_fogGlobalShader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - SetUniform_Color( gl_fogGlobalShader, fog->color ); + SetUniform_Color( gl_fogGlobalShader, fog->shader->fogParms.color ); } gl_fogGlobalShader->SetUniform_UnprojectMatrix( backEnd.viewParms.unprojectionMatrix ); diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index cae13f0349..f824649989 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -3217,8 +3217,6 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) int count, brushesCount, sidesCount; int sideNum; int planeNum; - shader_t *shader; - float d; int firstSide = 0; Log::Debug("...loading fogs" ); @@ -3316,24 +3314,9 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) out->bounds[ 1 ][ 2 ] = s_worldData.planes[ planeNum ].dist; } - // get information from the shader for fog parameters // it says RSF_3D but if there is no shader text found it should probably just error instead // of trying to create an implicit shader from an image... - shader = R_FindShader( fogs->shader, RSF_3D ); - - out->color = Color::Adapt( shader->fogParms.color ); - - if ( tr.worldLinearizeTexture ) - { - out->color = out->color.ConvertFromSRGB(); - } - - out->color *= tr.identityLight; - - out->color.SetAlpha( 1 ); - - d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque; - out->tcScale = 1.0f / d; + out->shader = R_FindShader( fogs->shader, RSF_3D ); // ydnar: global fog sets clearcolor/zfar if ( out->originalBrushNumber == -1 ) diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 41d5aee4ad..68b32eb873 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1123,7 +1123,7 @@ enum struct fogParms_t { - vec3_t color; + Color::Color color; float depthForOpaque; }; @@ -1353,8 +1353,7 @@ enum int originalBrushNumber; vec3_t bounds[ 2 ]; - Color::Color color; // in packed byte format - float tcScale; // texture coordinate vector scales + shader_t *shader; // has the fog parms // for clipping distance in fog when outside bool hasSurface; diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 7d1a1d5c15..702744c45b 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -1646,12 +1646,12 @@ void Render_fog( shaderStage_t* pStage ) gl_fogQuake3Shader->BindProgram(); gl_fogQuake3Shader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - gl_fogQuake3Shader->SetUniform_FogDensity( fog->tcScale ); + gl_fogQuake3Shader->SetUniform_FogDensity( 1.0f / fog->shader->fogParms.depthForOpaque ); gl_fogQuake3Shader->SetUniform_FogDepthVector( fogDepthVector ); gl_fogQuake3Shader->SetUniform_FogEyeT( eyeT ); // u_Color - SetUniform_ColorGlobal( gl_fogQuake3Shader, fog->color ); + SetUniform_ColorGlobal( gl_fogQuake3Shader, fog->shader->fogParms.color ); gl_fogQuake3Shader->SetUniform_ModelMatrix( backEnd.orientation.transformMatrix ); gl_fogQuake3Shader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index 7c622a26e4..618eb0c104 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -4586,17 +4586,23 @@ static bool ParseShader( const char *_text ) // fogParms else if ( !Q_stricmp( token, "fogParms" ) ) { - /* - Log::Warn("fogParms keyword not supported in shader '%s'", shader.name); - SkipRestOfLine(text); + vec3_t fogColor; - */ - - if ( !ParseVector( text, 3, shader.fogParms.color ) ) + if ( !ParseVector( text, 3, fogColor ) ) { return false; } + shader.fogParms.color = Color::Adapt( fogColor ); + + if ( tr.worldLinearizeTexture ) + { + shader.fogParms.color = shader.fogParms.color.ConvertFromSRGB(); + } + + shader.fogParms.color *= tr.identityLight; + shader.fogParms.color.SetAlpha( 1 ); + token = COM_ParseExt2( text, false ); if ( !token[ 0 ] ) @@ -4605,7 +4611,7 @@ static bool ParseShader( const char *_text ) continue; } - shader.fogParms.depthForOpaque = atof( token ); + shader.fogParms.depthForOpaque = std::max( 1.0, atof( token ) ); shader.sort = Util::ordinal(shaderSort_t::SS_FOG); From 1f328c48eaf1d327ec22c5623395cfa5123bf375 Mon Sep 17 00:00:00 2001 From: slipher Date: Fri, 6 Feb 2026 02:55:39 -0600 Subject: [PATCH 04/12] Introduce fogGradient shader keyword Add a shader keyword that can be used to configure a gradient for fog density, which tapers to 0 at the edge of the fog. The density of the fog is scaled by a factor of (1 - exp(-k * t)) where t is the distance under the fog plane and k a configurable constant. fogGradient expFalloff configures the exponential falloff such that the density is at 50% of the maximum at qu from the fog plane. fogGradient const does the same thing as the current default - no gradient; hard-edged fog. --- src/engine/renderer/Material.cpp | 3 +- src/engine/renderer/gl_shader.cpp | 4 +-- src/engine/renderer/gl_shader.h | 20 +++++++++-- .../renderer/glsl_source/fogEquation_fp.glsl | 30 ++++++++++++++-- .../renderer/glsl_source/fogGlobal_fp.glsl | 4 +-- .../renderer/glsl_source/fogQuake3_fp.glsl | 17 ++++++--- .../renderer/glsl_source/fogQuake3_vp.glsl | 5 ++- src/engine/renderer/tr_local.h | 1 + src/engine/renderer/tr_shade.cpp | 3 +- src/engine/renderer/tr_shader.cpp | 36 +++++++++++++++++++ 10 files changed, 104 insertions(+), 19 deletions(-) diff --git a/src/engine/renderer/Material.cpp b/src/engine/renderer/Material.cpp index db4112daf9..be630c1c36 100644 --- a/src/engine/renderer/Material.cpp +++ b/src/engine/renderer/Material.cpp @@ -1114,7 +1114,8 @@ void BindShaderFog( Material* material ) { // since fognum is grouped with the GL state stuff, segregating each fognum in a separate draw call. gl_fogQuake3ShaderMaterial->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - gl_fogQuake3ShaderMaterial->SetUniform_FogDensity( 1.0f / fog->shader->fogParms.depthForOpaque ); + gl_fogQuake3ShaderMaterial->SetUniform_FogGradient( + 1.0f / fog->shader->fogParms.depthForOpaque, fog->shader->fogParms.falloffExp ); gl_fogQuake3ShaderMaterial->SetUniform_FogDepthVector( fogDepthVector ); gl_fogQuake3ShaderMaterial->SetUniform_FogEyeT( eyeT ); diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 7e694b8cd7..46fd094969 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -2697,7 +2697,7 @@ GLShader_fogQuake3::GLShader_fogQuake3() : u_Bones( this ), u_VertexInterpolation( this ), u_ViewOrigin( this ), - u_FogDensity( this ), + u_FogGradient( this ), u_FogDepthVector( this ), u_FogEyeT( this ), GLDeformStage( this ), @@ -2713,7 +2713,7 @@ GLShader_fogQuake3Material::GLShader_fogQuake3Material() : u_ModelViewProjectionMatrix( this ), u_ColorGlobal_Uint( this ), u_ViewOrigin( this ), - u_FogDensity( this ), + u_FogGradient( this ), u_FogDepthVector( this ), u_FogEyeT( this ), GLDeformStage( this ) { diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index 8a62521d5f..d775c448f0 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -1859,6 +1859,22 @@ class u_FogDensity : } }; +class u_FogGradient : + GLUniform2f +{ +public: + u_FogGradient( GLShader *shader ) : + GLUniform2f( shader, "u_FogGradient", PUSH ) + { + } + + void SetUniform_FogGradient( float density, float falloffExp ) + { + vec2_t value{ density, falloffExp }; + this->SetValue( value ); + } +}; + class u_FogColor : GLUniform3f { @@ -3166,7 +3182,7 @@ class GLShader_fogQuake3 : public u_Bones, public u_VertexInterpolation, public u_ViewOrigin, - public u_FogDensity, + public u_FogGradient, public u_FogDepthVector, public u_FogEyeT, public GLDeformStage, @@ -3183,7 +3199,7 @@ class GLShader_fogQuake3Material : public u_ModelViewProjectionMatrix, public u_ColorGlobal_Uint, public u_ViewOrigin, - public u_FogDensity, + public u_FogGradient, public u_FogDepthVector, public u_FogEyeT, public GLDeformStage { diff --git a/src/engine/renderer/glsl_source/fogEquation_fp.glsl b/src/engine/renderer/glsl_source/fogEquation_fp.glsl index 48658cd6c0..2be37f45ad 100644 --- a/src/engine/renderer/glsl_source/fogEquation_fp.glsl +++ b/src/engine/renderer/glsl_source/fogEquation_fp.glsl @@ -32,11 +32,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================================== */ -float GetFogAlpha(float s, float t) +float FogGradientFunction(float k, float t) { - t = clamp(t, 0.0, 1.0); + return 1 - exp(-k * t); +} - float x = min(1, s * t); +float FogGradientAntiderivative(float k, float t) +{ + return t + exp(-k * t) / k; +} + +float GetFogGradientModifier(float k, float t0, float t1) +{ + t0 = max(0.0, t0); + t1 = max(0.0, t1); + + float deltaT = t1 - t0; + if (abs(deltaT) > 0.1) + { + return ( FogGradientAntiderivative(k, t1) - FogGradientAntiderivative(k, t0) ) / deltaT; + } + else + { + return FogGradientFunction(k, t0); + } +} + +float GetFogAlpha(float x) +{ + x = clamp(x, 0.0, 1.0); // sqrt(x) is bad near 0 because it increases too quickly resulting in sharp edges. // x ≤ 1/32: √32 * x diff --git a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl b/src/engine/renderer/glsl_source/fogGlobal_fp.glsl index 6097825900..b0052e82a6 100644 --- a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl +++ b/src/engine/renderer/glsl_source/fogGlobal_fp.glsl @@ -49,10 +49,10 @@ void main() vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depth, 1.0); P.xyz /= P.w; - // calculate the length in fog (t is always 1 if eye is in fog) + // calculate the length in fog float s = distance(u_ViewOrigin, P.xyz) * u_FogDensity; - vec4 color = vec4(1, 1, 1, GetFogAlpha(s, 1.0)); + vec4 color = vec4(1, 1, 1, GetFogAlpha(s)); outputColor = UnpackColor( u_Color ) * color; } diff --git a/src/engine/renderer/glsl_source/fogQuake3_fp.glsl b/src/engine/renderer/glsl_source/fogQuake3_fp.glsl index e76ea8c5a5..afb3630a00 100644 --- a/src/engine/renderer/glsl_source/fogQuake3_fp.glsl +++ b/src/engine/renderer/glsl_source/fogQuake3_fp.glsl @@ -26,10 +26,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define DEPTHMAP_GLSL +uniform vec2 u_FogGradient; uniform float u_FogEyeT; IN(smooth) float var_FogPlaneDistance; -IN(smooth) vec3 var_ScaledViewerOffset; +IN(smooth) vec3 var_ViewerOffset; IN(smooth) vec4 var_Color; DECLARE_OUTPUT(vec4) @@ -38,16 +39,22 @@ void main() { #insert material_fp - float s = length(var_ScaledViewerOffset); - float t = step( 0, var_FogPlaneDistance ); + if ( var_FogPlaneDistance < -0.01 ) + { + discard; + } + + float distInFog = length(var_ViewerOffset); if ( u_FogEyeT < 0 ) // eye outside fog { // fraction of the viewer-to-vertex ray which is inside fog - t *= var_FogPlaneDistance / ( max( 0, var_FogPlaneDistance ) - u_FogEyeT ); + distInFog *= var_FogPlaneDistance / ( max( 0, var_FogPlaneDistance ) - u_FogEyeT ); } - vec4 color = vec4(1, 1, 1, GetFogAlpha(s, t)); + float gradient = GetFogGradientModifier(u_FogGradient.y, u_FogEyeT, var_FogPlaneDistance); + + vec4 color = vec4(1, 1, 1, GetFogAlpha(gradient * u_FogGradient.x * distInFog)); color *= var_Color; diff --git a/src/engine/renderer/glsl_source/fogQuake3_vp.glsl b/src/engine/renderer/glsl_source/fogQuake3_vp.glsl index 8c4549fedb..2c8450cbc8 100644 --- a/src/engine/renderer/glsl_source/fogQuake3_vp.glsl +++ b/src/engine/renderer/glsl_source/fogQuake3_vp.glsl @@ -35,13 +35,12 @@ uniform mat4 u_ModelMatrix; uniform mat4 u_ModelViewProjectionMatrix; uniform vec3 u_ViewOrigin; -uniform float u_FogDensity; uniform vec4 u_FogDepthVector; // fog plane // how far the vertex is under the fog plane OUT(smooth) float var_FogPlaneDistance; -OUT(smooth) vec3 var_ScaledViewerOffset; +OUT(smooth) vec3 var_ViewerOffset; OUT(smooth) vec4 var_Color; void DeformVertex( inout vec4 pos, @@ -76,7 +75,7 @@ void main() position = u_ModelMatrix * position; position.xyz /= position.w; - var_ScaledViewerOffset = u_FogDensity * (position.xyz - u_ViewOrigin); + var_ViewerOffset = position.xyz - u_ViewOrigin; // calculate the length in fog var_FogPlaneDistance = dot(position.xyz, u_FogDepthVector.xyz) + u_FogDepthVector.w; diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 68b32eb873..086ba1649e 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1125,6 +1125,7 @@ enum { Color::Color color; float depthForOpaque; + float falloffExp; }; struct shader_t diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 702744c45b..579e8d806b 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -1646,7 +1646,8 @@ void Render_fog( shaderStage_t* pStage ) gl_fogQuake3Shader->BindProgram(); gl_fogQuake3Shader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - gl_fogQuake3Shader->SetUniform_FogDensity( 1.0f / fog->shader->fogParms.depthForOpaque ); + gl_fogQuake3Shader->SetUniform_FogGradient( + 1.0f / fog->shader->fogParms.depthForOpaque, fog->shader->fogParms.falloffExp ); gl_fogQuake3Shader->SetUniform_FogDepthVector( fogDepthVector ); gl_fogQuake3Shader->SetUniform_FogEyeT( eyeT ); diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index 618eb0c104..b55a31bf22 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -4619,6 +4619,37 @@ static bool ParseShader( const char *_text ) SkipRestOfLine( text ); continue; } + // fogGradient + else if ( !Q_stricmp( token, "fogGradient" ) ) + { + token = COM_ParseExt2( text, false ); + + if ( !Q_stricmp( token, "const" ) ) + { + shader.fogParms.falloffExp = 9999; + } + // fogGradient expFalloff + // scales density by 1 - exp(-k*t) where t is distance under fog plane + else if ( !Q_stricmp( token, "expFalloff" ) ) + { + token = COM_ParseExt2( text, false ); + float val = atof( token ); + + if ( val > 0.0f ) + { + shader.fogParms.falloffExp = M_LN2 / val; + } + else + { + Log::Warn( "bad expFalloff value in shader '%s'", shader.name ); + } + } + else + { + Log::Warn( "invalid fogGradient type '%s' in shader '%s'", token, shader.name ); + } + continue; + } // noFog else if ( !Q_stricmp( token, "noFog" ) ) { @@ -5974,6 +6005,11 @@ static shader_t *FinishShader() shader.portalRange = r_portalDefaultRange.Get(); } + if ( shader.fogParms.falloffExp == 0.0f ) + { + shader.fogParms.falloffExp = 9999; + } + numStages = MAX_SHADER_STAGES; GroupActiveStages(); From e003e018a33c441b6fddbf0c515e1fa5fa389ef9 Mon Sep 17 00:00:00 2001 From: slipher Date: Fri, 6 Feb 2026 03:23:04 -0600 Subject: [PATCH 05/12] For fogs use a small gradient by default Make the default gradient like `fogGradient expFalloff 5`. This makes the fogs have softer edges without changing the appearance too much. --- src/engine/renderer/tr_shader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index b55a31bf22..26f304b42e 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -4620,6 +4620,7 @@ static bool ParseShader( const char *_text ) continue; } // fogGradient + // Default: fogGradient expFalloff 5 else if ( !Q_stricmp( token, "fogGradient" ) ) { token = COM_ParseExt2( text, false ); @@ -6007,7 +6008,7 @@ static shader_t *FinishShader() if ( shader.fogParms.falloffExp == 0.0f ) { - shader.fogParms.falloffExp = 9999; + shader.fogParms.falloffExp = M_LN2 / 5; } numStages = MAX_SHADER_STAGES; From 276222ab65ef1d3e4498f01734c3740f2db0058f Mon Sep 17 00:00:00 2001 From: slipher Date: Sun, 1 Feb 2026 04:21:00 -0600 Subject: [PATCH 06/12] NUKE cvar r_wolfFog This cheat cvar controlled whether the Quake 3 fog shader is used (while leaving the ET global fog unaffected). Instead you can use r_noFog which toggles all fogs. --- src/engine/renderer/Material.cpp | 2 +- src/engine/renderer/tr_init.cpp | 2 -- src/engine/renderer/tr_local.h | 1 - src/engine/renderer/tr_shade.cpp | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/engine/renderer/Material.cpp b/src/engine/renderer/Material.cpp index be630c1c36..0eb6a68392 100644 --- a/src/engine/renderer/Material.cpp +++ b/src/engine/renderer/Material.cpp @@ -2097,7 +2097,7 @@ void MaterialSystem::RenderMaterial( Material& material, const uint32_t viewID ) } if( material.shaderBinder == BindShaderFog ) { - if ( r_noFog->integer || !r_wolfFog->integer || ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) { + if ( r_noFog->integer || ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) { return; } } diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 6d52b59b4b..6886210ca3 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -222,7 +222,6 @@ Cvar::Cvar r_rendererAPI( "r_rendererAPI", "Renderer API: 0: OpenGL, 1: Vul cvar_t *r_showImages; - cvar_t *r_wolfFog; cvar_t *r_noFog; Cvar::Range> r_forceAmbient( "r_forceAmbient", "Minimal light amount in lightGrid; -1 to use map value", @@ -1157,7 +1156,6 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p r_heatHaze = Cvar_Get( "r_heatHaze", "1", CVAR_LATCH | CVAR_ARCHIVE ); r_noMarksOnTrisurfs = Cvar_Get( "r_noMarksOnTrisurfs", "1", CVAR_CHEAT ); - r_wolfFog = Cvar_Get( "r_wolfFog", "1", CVAR_CHEAT ); r_noFog = Cvar_Get( "r_noFog", "0", CVAR_CHEAT ); Cvar::Latch( r_forceAmbient ); diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 086ba1649e..9ce31a8d76 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -2639,7 +2639,6 @@ enum extern cvar_t *r_lodBias; // push/pull LOD transitions extern cvar_t *r_lodScale; - extern cvar_t *r_wolfFog; extern cvar_t *r_noFog; extern Cvar::Range> r_forceAmbient; diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 579e8d806b..9ce2552f76 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -1615,7 +1615,7 @@ void Render_liquid( shaderStage_t *pStage ) void Render_fog( shaderStage_t* pStage ) { - if ( r_noFog->integer || !r_wolfFog->integer || ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) + if ( r_noFog->integer || ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) { return; } From 2e2055f6db59886eff82113f40a258fe8bad5bec Mon Sep 17 00:00:00 2001 From: slipher Date: Mon, 2 Feb 2026 00:05:06 -0600 Subject: [PATCH 07/12] Fix failing to generate depth shader with long name A shader with a name 58-63 chars long would fail to generate a depth pass shader due to a length check. Add some extra space in the name buffer so this can't happen. --- src/engine/renderer/tr_local.h | 4 +++- src/engine/renderer/tr_shader.cpp | 34 ++++--------------------------- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 9ce31a8d76..2c5f027be2 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1130,7 +1130,9 @@ enum struct shader_t { - char name[ MAX_QPATH ]; // game path, including extension + // max name length is MAX_QPATH - 1 but with room for automatically added suffixes + char name[ MAX_QPATH + 8 ]; + int registerFlags; // RSF_ int index; // this shader == tr.shaders[index] diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index 26f304b42e..e8454bcb7f 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -5818,17 +5818,6 @@ static void GeneratePermanentShaderTable( const float *values, int numValues ) shaderTableHashTable[ hash ] = newTable; } -bool CheckShaderNameLength( const char* func_err, const char* name, const char* suffix ) -{ - if ( strlen( name ) + strlen( suffix ) >= MAX_QPATH ) - { - Log::Warn("%s Shader name %s%s length longer than MAX_QPATH %d", func_err, name, suffix, MAX_QPATH ); - return false; - } - - return true; -} - static void ValidateStage( shaderStage_t *pStage ) { struct stageCheck_t { @@ -6078,24 +6067,7 @@ static shader_t *FinishShader() shader.noFog = true; shader.fogShader = nullptr; - const char* depthShaderSuffix = "$depth"; - - if ( !CheckShaderNameLength( "FinishShader", shader.name, depthShaderSuffix ) ) - { - ret->depthShader = nullptr; - - if ( glConfig.usingMaterialSystem && !tr.worldLoaded ) { - uint8_t maxStages = ret->lastStage - ret->stages; - - // Add 1 for potential fog stages - maxStages = PAD( maxStages + 1, 4 ); // Aligned to 4 components - materialSystem.maxStages = std::max( maxStages, materialSystem.maxStages ); - } - - return ret; - } - - strcat( shader.name, depthShaderSuffix ); + Q_strcat( shader.name, sizeof( shader.name ), "$depth" ); if( stages[0].stateBits & GLS_ATEST_BITS ) { // alpha test requires a custom depth shader @@ -6284,6 +6256,7 @@ shader_t *R_FindShader( const char *name, int flags ) ClearGlobalShader(); Q_strncpyz( shader.name, strippedName, sizeof( shader.name ) ); + ASSERT_LT( strlen( shader.name ), MAX_QPATH ); shader.registerFlags = flags; for ( i = 0; i < MAX_SHADER_STAGES; i++ ) @@ -6474,8 +6447,9 @@ qhandle_t RE_RegisterShader( const char *name, int flags ) { shader_t *sh; - if ( !CheckShaderNameLength( "RE_RegisterShader", name, "" ) ) + if ( strlen( name ) >= MAX_QPATH ) { + Log::Warn( "RE_RegisterShader: name '%s' is too long", name ); return 0; } From 39f2fe0c0721ad71f5edf158bf58c99dc4a6b55d Mon Sep 17 00:00:00 2001 From: slipher Date: Mon, 2 Feb 2026 05:47:27 -0600 Subject: [PATCH 08/12] Draw inside of fog with modified global fog shader When the viewer is inside a fog volume, render the fog with a single draw call instead of drawing a corresponding fog surface for every opaque surface. The fogGlobal GLSL shader is modified to use a vertex position so it can handle finite-sized fogs. Before: - Each opaque surface inside a fog volume is drawn a second time using the fog shader with GL_EQUAL depth test. - The underside of the fog plane is drawn with a normal depth test, to cover things outside the fog when the viewer is inside. After: - If the viewer is inside the fog, draw all inside faces of the fog brush, using the depth buffer to reduce the fog distance in the case that something is in front of the fog boundary. - If the viewer is outside the fog, do everything as before. For the moment this is only used when material system is disabled. Fixes seams at the edge of fog that appear when MSAA is enabled. --- src.cmake | 3 +- src/engine/renderer/VertexSpecification.h | 8 ++- src/engine/renderer/gl_shader.cpp | 8 +-- src/engine/renderer/gl_shader.h | 3 +- .../renderer/glsl_source/fogGlobal_fp.glsl | 26 +++++++++- .../renderer/glsl_source/fogGlobal_vp.glsl | 49 +++++++++++++++++++ src/engine/renderer/tr_backend.cpp | 37 ++++++-------- src/engine/renderer/tr_bsp.cpp | 47 ++++++++++++++++++ src/engine/renderer/tr_init.cpp | 2 + src/engine/renderer/tr_local.h | 32 +++++++----- src/engine/renderer/tr_main.cpp | 11 +++-- src/engine/renderer/tr_scene.cpp | 41 ++++++++++++++++ src/engine/renderer/tr_shade.cpp | 2 - src/engine/renderer/tr_shader.cpp | 23 +++++++++ 14 files changed, 245 insertions(+), 47 deletions(-) create mode 100644 src/engine/renderer/glsl_source/fogGlobal_vp.glsl diff --git a/src.cmake b/src.cmake index 7430a577b9..078d111a43 100644 --- a/src.cmake +++ b/src.cmake @@ -108,7 +108,6 @@ set(GLSL_EMBED_LIST blur_fp.glsl cameraEffects_fp.glsl contrast_fp.glsl - fogGlobal_fp.glsl fxaa_fp.glsl fxaa3_11_fp.glsl motionblur_fp.glsl @@ -130,6 +129,8 @@ set(GLSL_EMBED_LIST vertexSkinning_vp.glsl # Regular shaders + fogGlobal_vp.glsl + fogGlobal_fp.glsl fogQuake3_vp.glsl fogQuake3_fp.glsl generic_vp.glsl diff --git a/src/engine/renderer/VertexSpecification.h b/src/engine/renderer/VertexSpecification.h index 9c06d264a3..813c80fcb4 100644 --- a/src/engine/renderer/VertexSpecification.h +++ b/src/engine/renderer/VertexSpecification.h @@ -41,6 +41,9 @@ enum // GPU vertex animations ATTR_INDEX_POSITION2, ATTR_INDEX_QTANGENT2, + + ATTR_INDEX_FOG_SURFACE, + ATTR_INDEX_MAX }; @@ -53,7 +56,8 @@ static const char* const attributeNames[] = "attr_Color", "attr_BoneFactors", "attr_Position2", - "attr_QTangent2" + "attr_QTangent2", + "attr_FogSurface", }; enum @@ -69,6 +73,8 @@ enum ATTR_POSITION2 = BIT( ATTR_INDEX_POSITION2 ), ATTR_QTANGENT2 = BIT( ATTR_INDEX_QTANGENT2 ), + ATTR_FOG_SURFACE = BIT( ATTR_INDEX_FOG_SURFACE ), + ATTR_INTERP_BITS = ATTR_POSITION2 | ATTR_QTANGENT2, }; diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 46fd094969..5c61d687f8 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -2719,15 +2719,17 @@ GLShader_fogQuake3Material::GLShader_fogQuake3Material() : GLDeformStage( this ) { } +// TODO: rename GLShader_fogGlobal::GLShader_fogGlobal() : - GLShader( "fogGlobal", ATTR_POSITION, - false, "screenSpace", "fogGlobal" ), + GLShader( "fogGlobal", ATTR_POSITION | ATTR_FOG_SURFACE, + false, "fogGlobal", "fogGlobal" ), u_DepthMap( this ), + u_ModelViewProjectionMatrix( this ), u_UnprojectMatrix( this ), u_Color_Float( this ), u_Color_Uint( this ), u_ViewOrigin( this ), - u_FogDensity( this ) + u_FogGradient( this ) { } diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index d775c448f0..1d81f1ad2b 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -3210,11 +3210,12 @@ class GLShader_fogQuake3Material : class GLShader_fogGlobal : public GLShader, public u_DepthMap, + public u_ModelViewProjectionMatrix, public u_UnprojectMatrix, public u_Color_Float, public u_Color_Uint, public u_ViewOrigin, - public u_FogDensity + public u_FogGradient { public: GLShader_fogGlobal(); diff --git a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl b/src/engine/renderer/glsl_source/fogGlobal_fp.glsl index b0052e82a6..770354ec53 100644 --- a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl +++ b/src/engine/renderer/glsl_source/fogGlobal_fp.glsl @@ -27,16 +27,20 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define DEPTHMAP_GLSL +IN(smooth) vec3 var_Position; +IN(flat) vec4 var_FogSurface; + uniform sampler2D u_DepthMap; uniform colorPack u_Color; uniform vec3 u_ViewOrigin; -uniform float u_FogDensity; +uniform vec2 u_FogGradient; uniform mat4 u_UnprojectMatrix; DECLARE_OUTPUT(vec4) +// This shader can be used to draw a fog volume the viewer is inside of. void main() { #insert material_fp @@ -50,7 +54,25 @@ void main() P.xyz /= P.w; // calculate the length in fog - float s = distance(u_ViewOrigin, P.xyz) * u_FogDensity; + float depthDist = distance(u_ViewOrigin, P.xyz); + float fogBoundaryDist = distance(u_ViewOrigin, var_Position); + vec3 endPoint; + float distInFog; + if ( depthDist < fogBoundaryDist ) + { + endPoint = P.xyz; + distInFog = depthDist; + } + else + { + endPoint = var_Position; + distInFog = fogBoundaryDist; + } + + float t0 = dot(var_FogSurface.xyz, u_ViewOrigin) + var_FogSurface.w; + float t1 = dot(var_FogSurface.xyz, endPoint) + var_FogSurface.w; + + float s = distInFog * GetFogGradientModifier(u_FogGradient.y, t0, t1) * u_FogGradient.x; vec4 color = vec4(1, 1, 1, GetFogAlpha(s)); diff --git a/src/engine/renderer/glsl_source/fogGlobal_vp.glsl b/src/engine/renderer/glsl_source/fogGlobal_vp.glsl new file mode 100644 index 0000000000..ee2071e261 --- /dev/null +++ b/src/engine/renderer/glsl_source/fogGlobal_vp.glsl @@ -0,0 +1,49 @@ +/* +=========================================================================== + +Daemon BSD Source Code +Copyright (c) 2026 Daemon Developers +All rights reserved. + +This file is part of the Daemon BSD Source Code (Daemon Source Code). + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Daemon developers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +=========================================================================== +*/ + +uniform mat4 u_ModelViewProjectionMatrix; + +IN vec3 attr_Position; +IN vec4 attr_FogSurface; + +OUT(smooth) vec3 var_Position; +OUT(flat) vec4 var_FogSurface; + +void main() +{ + vec4 position = vec4(attr_Position, 1.0); + gl_Position = u_ModelViewProjectionMatrix * position; + var_Position = attr_Position; + var_FogSurface = attr_FogSurface; +} diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 774acefd3e..608f714048 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1423,7 +1423,8 @@ void RB_RenderPostDepthLightTile() GL_CheckErrors(); } -void RB_RenderGlobalFog() +// TODO: move with other Render_ functions +void Render_fogGlobal( shaderStage_t *stage ) { if ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) { @@ -1435,34 +1436,29 @@ void RB_RenderGlobalFog() return; } - if ( !tr.world || tr.world->globalFog < 0 ) - { - return; - } - GLIMP_LOGCOMMENT( "--- RB_RenderGlobalFog ---" ); RB_PrepareForSamplingDepthMap(); - GL_Cull( cullType_t::CT_TWO_SIDED ); + GL_Cull( cullType_t::CT_FRONT_SIDED ); gl_fogGlobalShader->BindProgram(); - // go back to the world modelview matrix - backEnd.orientation = backEnd.viewParms.world; - { - fog_t* fog = &tr.world->fogs[ tr.world->globalFog ]; - - GLIMP_LOGCOMMENT( "--- RB_RenderGlobalFog( fogNum = %i ) ---", tr.world->globalFog ); + GL_State( stage->stateBits ); - GL_State( GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); - - gl_fogGlobalShader->SetUniform_FogDensity( 1.0f / fog->shader->fogParms.depthForOpaque ); + gl_fogGlobalShader->SetUniform_FogGradient( + 1.0f / stage->shader->fogParms.depthForOpaque, stage->shader->fogParms.falloffExp ); gl_fogGlobalShader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - SetUniform_Color( gl_fogGlobalShader, fog->shader->fogParms.color ); + SetUniform_Color( gl_fogGlobalShader, stage->shader->fogParms.color ); } + // It's important to avoid far plane clipping + matrix_t projection, mvp; + MatrixPerspectiveProjectionFovXYInfiniteRH( projection, tr.refdef.fov_x, tr.refdef.fov_y, 1.0f ); + MatrixMultiply( projection, glState.modelViewMatrix[ glState.stackIndex ], mvp ); + + gl_fogGlobalShader->SetUniform_ModelViewProjectionMatrix( mvp ); gl_fogGlobalShader->SetUniform_UnprojectMatrix( backEnd.viewParms.unprojectionMatrix ); // bind u_DepthMap @@ -1470,7 +1466,9 @@ void RB_RenderGlobalFog() GL_BindToTMU( 1, tr.depthSamplerImage ) ); - Tess_InstantScreenSpaceQuad(); + gl_fogGlobalShader->SetRequiredVertexPointers(); + + Tess_DrawElements(); GL_CheckErrors(); } @@ -2801,9 +2799,6 @@ static void RB_RenderView( bool depthPass ) RB_RenderSSAO(); - // render global fog post process effect - RB_RenderGlobalFog(); - TransitionMainToMSAA( GL_COLOR_BUFFER_BIT ); // draw everything that is translucent diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index f824649989..e5a6c2138c 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -3262,6 +3262,14 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) sidesCount = sidesLump->filelen / sizeof( *sides ); + struct FogVert + { + float position[ 3 ]; + vec4_t surface; + }; + std::vector fogVerts(8 * count); + std::vector fogIndexes(36 * count); + for ( i = 0; i < count; i++, fogs++ ) { out->originalBrushNumber = LittleLong( fogs->brushNum ); @@ -3331,6 +3339,7 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) if ( sideNum < 0 || sideNum >= sidesCount ) { out->hasSurface = false; + Vector4Set( out->surface, 0.0f, 0.0f, 0.0f, -1.0e10f ); } else { @@ -3340,9 +3349,47 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) out->surface[ 3 ] = -s_worldData.planes[ planeNum ].dist; } + // add faces of fog brush for drawing fog from inside + for ( int p = 0; p < 8; p++ ) + { + fogVerts[ i * 8 + p ].position[ 0 ] = out->bounds[ p & 1 ][ 0 ]; + fogVerts[ i * 8 + p ].position[ 1 ] = out->bounds[ ( p >> 1 ) & 1 ][ 1 ]; + fogVerts[ i * 8 + p ].position[ 2 ] = out->bounds[ p >> 2 ][ 2 ]; + + VectorCopy( out->surface, fogVerts[ i * 8 + p ].surface ); + fogVerts[ i * 8 + p ].surface[ 3 ] = -out->surface[ 3 ]; + } + + constexpr int box[ 36 ] = { 2, 3, 0, 0, 3, 1, 0, 1, 4, 4, 1, 5, 2, 0, 6, 6, 0, 4, + 1, 3, 5, 5, 3, 7, 3, 2, 7, 7, 2, 6, 7, 6, 5, 5, 6, 4 }; + for ( int p = 0; p < 36; p++ ) + { + fogIndexes[ 36 * i + p ] = 8 * i + box[ p ]; + } + + // add draw surf for fog brush faces + out->surf.firstIndex = 36 * i; + out->surf.numTriangles = 12; + out->surf.surfaceType = surfaceType_t::SF_TRIANGLES; + out++; } + vertexAttributeSpec_t attributes[] { + { ATTR_INDEX_POSITION, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].position, 3, sizeof( fogVerts[ 0 ] ), 0 }, + { ATTR_INDEX_FOG_SURFACE, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].surface, 4, sizeof( fogVerts[ 0 ] ), 0 }, + }; + VBO_t *fogVBO = R_CreateStaticVBO( + "fogs VBO", std::begin( attributes ), std::end( attributes ), fogVerts.size() ); + IBO_t *fogIBO = R_CreateStaticIBO( "fogs IBO", &fogIndexes[ 0 ], fogIndexes.size() ); + SetupVAOBuffers( fogVBO, fogIBO, ATTR_POSITION | ATTR_FOG_SURFACE, &fogVBO->VAO ); + + for ( int j = 1; j < s_worldData.numFogs; j++ ) + { + s_worldData.fogs[ j ].surf.vbo = fogVBO; + s_worldData.fogs[ j ].surf.ibo = fogIBO; + } + Log::Debug("%i fog volumes loaded", s_worldData.numFogs ); } diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 6886210ca3..e4f76b5122 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -1541,6 +1541,8 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p gl_fogQuake3Shader->SetDeform( 0 ); gl_fogQuake3Shader->MarkProgramForBuilding(); } + + gl_fogGlobalShader->MarkProgramForBuilding(); } for ( int i = 0; i < tr.numModels; i++ ) { diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 2c5f027be2..d1a18c21a7 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -939,6 +939,7 @@ enum ST_HEATHAZEMAP, // heatHaze post process effect ST_LIQUIDMAP, ST_FOGMAP, + ST_FOGMAP_INNER, // a fog seen from inside ST_LIGHTMAP, ST_STYLELIGHTMAP, ST_STYLECOLORMAP, @@ -1202,6 +1203,7 @@ enum struct shader_t *depthShader; struct shader_t *fogShader; + struct shader_t *fogInnerShader; struct shader_t *next; }; @@ -1351,18 +1353,6 @@ enum //================================================================================= - struct fog_t - { - int originalBrushNumber; - vec3_t bounds[ 2 ]; - - shader_t *shader; // has the fog parms - - // for clipping distance in fog when outside - bool hasSurface; - float surface[ 4 ]; - }; - struct viewParms_t { orientationr_t orientation; @@ -1633,6 +1623,20 @@ enum IBO_t *ibo; }; + struct fog_t + { + int originalBrushNumber; + vec3_t bounds[ 2 ]; + + shader_t *shader; // has the fog parms + + // for clipping distance in fog when outside + bool hasSurface; + float surface[ 4 ]; + + srfGeneric_t surf; + }; + extern void ( *rb_surfaceTable[Util::ordinal(surfaceType_t::SF_NUM_SURFACE_TYPES)] )(void * ); void ValidateVertex( srfVert_t* vertex, int vertexID, shader_t* shader ); @@ -3257,6 +3261,7 @@ void GLimp_LogComment_( std::string comment ); void Render_heatHaze( shaderStage_t *pStage ); void Render_liquid( shaderStage_t *pStage ); void Render_fog( shaderStage_t* pStage ); + void Render_fogGlobal( shaderStage_t *pStage ); /* ============================================================ @@ -3348,6 +3353,9 @@ void GLimp_LogComment_( std::string comment ); void RE_AddPolyToSceneET( qhandle_t hShader, int numVerts, const polyVert_t *verts ); void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ); + bool R_InsideFog( int fognum ); + void R_AddInnerFogSurfaces(); + void RE_AddDynamicLightToSceneET( const vec3_t org, float radius, float intensity, float r, float g, float b, qhandle_t hShader, int flags ); void RE_AddDynamicLightToSceneQ3A( const vec3_t org, float intensity, float r, float g, float b ); diff --git a/src/engine/renderer/tr_main.cpp b/src/engine/renderer/tr_main.cpp index 8937479b04..306f80acde 100644 --- a/src/engine/renderer/tr_main.cpp +++ b/src/engine/renderer/tr_main.cpp @@ -1731,11 +1731,12 @@ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, i R_AddDrawSurf( surface, shader->depthShader, 0, 0, bspSurface ); } - // don't draw the global fog twice on opaque surfaces - // allow global fog with the fogLE shader although drawing the global fog with the - // volumetric (fogQuake3) shader looks kind of buggy + bool usingMaterial = glConfig.usingMaterialSystem && !r_materialSystemSkip.Get(); + + // don't fog surfaces that already were covered by globalFog if ( !shader->noFog && fogNum >= 1 && - ( fogNum != tr.world->globalFog || shader->fogPass == fogPass_t::FP_LE ) ) { + !( usingMaterial ? fogNum == tr.world->globalFog : R_InsideFog( fogNum ) ) ) + { R_AddDrawSurf( surface, shader->fogShader, 0, fogNum, bspSurface ); } } @@ -2085,6 +2086,8 @@ void R_RenderView( viewParms_t *parms ) // set camera frustum planes in world space again, but this time including the far plane tr.orientation = tr.viewParms.world; + R_AddInnerFogSurfaces(); + R_AddEntitySurfaces(); // Transform the blur vector in view space, FIXME for some we need reason invert its Z component diff --git a/src/engine/renderer/tr_scene.cpp b/src/engine/renderer/tr_scene.cpp index e14632ef18..85c2e3f1b4 100644 --- a/src/engine/renderer/tr_scene.cpp +++ b/src/engine/renderer/tr_scene.cpp @@ -248,6 +248,47 @@ void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *vert //================================================================================= +bool R_InsideFog( int fognum ) +{ + // TODO: with portals this should use the point at the view frustum near plane instead + const vec3_t &origin = tr.viewParms.orientation.origin; + + const auto &bounds = tr.world->fogs[ fognum ].bounds; + + for ( int i = 0; i < 3; i++ ) + { + if ( origin[ i ] < bounds[ 0 ][ i ] || origin[ i ] > bounds[ 1 ][ i ] ) + { + return false; + } + } + + return true; +} + +void R_AddInnerFogSurfaces() +{ + if ( glConfig.usingMaterialSystem && !r_materialSystemSkip.Get() ) + { + if ( tr.world->globalFog > 0 ) + { + const fog_t &fog = tr.world->fogs[ tr.world->globalFog ]; + R_AddDrawSurf( ( surfaceType_t *)&fog.surf, fog.shader->fogInnerShader, -1, 0 ); + } + } + else + { + for ( int i = 1; i < tr.world->numFogs; i++ ) + { + if ( R_InsideFog( i ) ) + { + const fog_t &fog = tr.world->fogs[ i ]; + R_AddDrawSurf( ( surfaceType_t *)&fog.surf, fog.shader->fogInnerShader, -1, 0 ); + } + } + } +} + /* ===================== RE_AddRefEntityToScene diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 9ce2552f76..123daec916 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -310,8 +310,6 @@ static void GLSL_InitGPUShadersOrError() // global fog post process effect gl_shaderManager.LoadShader( gl_fogGlobalShader ); - - gl_fogGlobalShader->MarkProgramForBuilding(); } if ( r_heatHaze->integer ) diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index e8454bcb7f..34dab5d9e2 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -5656,6 +5656,12 @@ static void SetStagesRenderers() &UpdateSurfaceDataFog, &BindShaderFog, &ProcessMaterialFog, }; break; + case stageType_t::ST_FOGMAP_INNER: + stageRendererOptions = { + &Render_fogGlobal, &MarkShaderBuildNOP, + &UpdateSurfaceDataNOP, &BindShaderNOP, &ProcessMaterialNOP, + }; + break; default: Log::Warn( "Missing renderer for stage type %d in shader %s, stage %d", Util::ordinal(stage->type), shader.name, s ); @@ -5839,6 +5845,7 @@ static void ValidateStage( shaderStage_t *pStage ) { stageType_t::ST_HEATHAZEMAP, { true, true, false, "heatHazeMap" } }, { stageType_t::ST_LIQUIDMAP, { true, true, false, "liquidMap" } }, { stageType_t::ST_FOGMAP, { true, false, false, "fogMap" } }, + { stageType_t::ST_FOGMAP_INNER, { true, false, false, "fogMapInner" } }, // The lightmap is fetched at render time. { stageType_t::ST_LIGHTMAP, { true, false, false, "light map" } }, // The lightmap is fetched at render time. @@ -6053,6 +6060,8 @@ static shader_t *FinishShader() } } + size_t nameLength = strlen( shader.name ); + // generate depth-only shader if necessary if( r_depthShaders.Get() && !shader.isSky && @@ -6114,6 +6123,20 @@ static shader_t *FinishShader() ret->depthShader = nullptr; } + if ( ret->fogParms.depthForOpaque != 0 ) + { + shader.name[ nameLength ] = '\0'; + Q_strcat( shader.name, sizeof( shader.name ), "$fogIn" ); + stages[1].active = false; + numStages = 1; + ResetStruct( stages[ 0 ] ); + stages[ 0 ].active = true; + stages[ 0 ].type = stageType_t::ST_FOGMAP_INNER; + stages[ 0 ].stateBits = GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + SetStagesRenderers(); + ret->fogInnerShader = MakeShaderPermanent(); + } + if ( glConfig.usingMaterialSystem && !tr.worldLoaded ) { uint8_t maxStages = ret->lastStage - ret->stages; From be786269f84d7b5b6b8de5a0c84bfbfb82167693 Mon Sep 17 00:00:00 2001 From: slipher Date: Thu, 5 Feb 2026 06:40:56 -0600 Subject: [PATCH 09/12] Use depth-based fog shader for all fogs Add a new macro variant of the 'fogGlobal' shader which can be used for fogs that the viewer is outside of. Now when material system is not active, this shader is used for all fogs. The "outside" version of the shader is more complex than the inside one, since it is necessary to trace against all planes of the fog to find the length in fog. This change removes the requirement that a fog be enclosed by opaque walls on all sides except one (the "surface plane"). Now fogs can be rendered (almost) correctly from any side. The exception is that if eye position is very close but not inside the fog, within about 5 qu, the inside fog shader has to be used instead of the outside one to avoid near plane culling. This causes an artifact when passing into the fog through a non-surface side which can be noticed if you look closely. --- src/engine/renderer/VertexSpecification.h | 6 ++ src/engine/renderer/gl_shader.cpp | 8 ++- src/engine/renderer/gl_shader.h | 38 ++++++++++- .../renderer/glsl_source/fogGlobal_fp.glsl | 47 +++++++++++-- .../renderer/glsl_source/fogGlobal_vp.glsl | 13 ++++ src/engine/renderer/tr_backend.cpp | 28 ++++++-- src/engine/renderer/tr_bsp.cpp | 66 ++++++++++++++----- src/engine/renderer/tr_init.cpp | 6 +- src/engine/renderer/tr_local.h | 5 +- src/engine/renderer/tr_main.cpp | 5 +- src/engine/renderer/tr_scene.cpp | 41 +++++++++--- src/engine/renderer/tr_shader.cpp | 12 ++++ 12 files changed, 230 insertions(+), 45 deletions(-) diff --git a/src/engine/renderer/VertexSpecification.h b/src/engine/renderer/VertexSpecification.h index 813c80fcb4..d3f774e846 100644 --- a/src/engine/renderer/VertexSpecification.h +++ b/src/engine/renderer/VertexSpecification.h @@ -44,6 +44,10 @@ enum ATTR_INDEX_FOG_SURFACE, + // This occupies 5 attribute slots + ATTR_INDEX_FOG_PLANES_0, + ATTR_INDEX_FOG_PLANES_LAST = ATTR_INDEX_FOG_PLANES_0 + 4, + ATTR_INDEX_MAX }; @@ -58,6 +62,7 @@ static const char* const attributeNames[] = "attr_Position2", "attr_QTangent2", "attr_FogSurface", + "attr_FogPlanes", nullptr, nullptr, nullptr, nullptr, }; enum @@ -74,6 +79,7 @@ enum ATTR_QTANGENT2 = BIT( ATTR_INDEX_QTANGENT2 ), ATTR_FOG_SURFACE = BIT( ATTR_INDEX_FOG_SURFACE ), + ATTR_FOG_PLANES = BIT( ATTR_INDEX_FOG_PLANES_0 ) * ( BIT( 5 ) - 1 ), ATTR_INTERP_BITS = ATTR_POSITION2 | ATTR_QTANGENT2, }; diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 5c61d687f8..56fce706af 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -2007,7 +2007,10 @@ void GLShaderManager::BindAttribLocations( GLuint program ) const { for ( uint32_t i = 0; i < ATTR_INDEX_MAX; i++ ) { - glBindAttribLocation( program, i, attributeNames[ i ] ); + if ( attributeNames[ i ] != nullptr ) + { + glBindAttribLocation( program, i, attributeNames[ i ] ); + } } } @@ -2729,7 +2732,8 @@ GLShader_fogGlobal::GLShader_fogGlobal() : u_Color_Float( this ), u_Color_Uint( this ), u_ViewOrigin( this ), - u_FogGradient( this ) + u_FogGradient( this ), + GLCompileMacro_OUTSIDE_FOG( this ) { } diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index 1d81f1ad2b..64fb38db96 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -919,6 +919,7 @@ class GLCompileMacro // It also works regardless of RTTI is enabled or not. enum EGLCompileMacro : unsigned { + OUTSIDE_FOG, USE_BSP_SURFACE, USE_VERTEX_SKINNING, USE_VERTEX_ANIMATION, @@ -985,6 +986,40 @@ class GLCompileMacro virtual ~GLCompileMacro() = default; }; +class GLCompileMacro_OUTSIDE_FOG : + GLCompileMacro +{ +public: + GLCompileMacro_OUTSIDE_FOG( GLShader *shader ) : + GLCompileMacro( shader ) + { + } + + const char *GetName() const override + { + return "OUTSIDE_FOG"; + } + + EGLCompileMacro GetType() const override + { + return EGLCompileMacro::OUTSIDE_FOG; + } + + int GetShaderTypes() const override { + return ShaderType::VERTEX | ShaderType::FRAGMENT; + } + + void SetOutsideFog( bool enable ) + { + SetMacro( enable ); + } + + uint32_t GetRequiredVertexAttributes() const override + { + return ATTR_FOG_PLANES; + } +}; + class GLCompileMacro_USE_BSP_SURFACE : GLCompileMacro { @@ -3215,7 +3250,8 @@ class GLShader_fogGlobal : public u_Color_Float, public u_Color_Uint, public u_ViewOrigin, - public u_FogGradient + public u_FogGradient, + public GLCompileMacro_OUTSIDE_FOG { public: GLShader_fogGlobal(); diff --git a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl b/src/engine/renderer/glsl_source/fogGlobal_fp.glsl index 770354ec53..fbaef1c897 100644 --- a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl +++ b/src/engine/renderer/glsl_source/fogGlobal_fp.glsl @@ -30,6 +30,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA IN(smooth) vec3 var_Position; IN(flat) vec4 var_FogSurface; +#ifdef OUTSIDE_FOG +#define NUM_PLANES 5 +IN(flat) vec4 var_FogPlanes[NUM_PLANES]; +#endif + uniform sampler2D u_DepthMap; uniform colorPack u_Color; @@ -40,7 +45,24 @@ uniform mat4 u_UnprojectMatrix; DECLARE_OUTPUT(vec4) -// This shader can be used to draw a fog volume the viewer is inside of. +#ifdef OUTSIDE_FOG +// Trace against the inner sides of the fog brush +float FogDistance(vec3 start, vec3 dir) +{ + vec4 start4 = vec4(-start, 1.0); + float minDist = 1.0e20; + for (int i = 0; i < NUM_PLANES; i++) + { + float dist = dot(start4, var_FogPlanes[i]) / dot(dir, var_FogPlanes[i].xyz) ; + if (dist >= 0.0) + { + minDist = min(minDist, dist); + } + } + return minDist < 1.0e20 ? minDist : 0.0; +} +#endif + void main() { #insert material_fp @@ -53,9 +75,17 @@ void main() vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depth, 1.0); P.xyz /= P.w; - // calculate the length in fog - float depthDist = distance(u_ViewOrigin, P.xyz); - float fogBoundaryDist = distance(u_ViewOrigin, var_Position); + #ifdef OUTSIDE_FOG + vec3 startPoint = var_Position; + vec3 viewDir = normalize(var_Position - u_ViewOrigin); + float fogBoundaryDist = FogDistance(var_Position, viewDir); + #else + vec3 startPoint = u_ViewOrigin; + float fogBoundaryDist = distance(u_ViewOrigin, var_Position); + #endif + + float depthDist = distance(startPoint, P.xyz); + vec3 endPoint; float distInFog; if ( depthDist < fogBoundaryDist ) @@ -65,11 +95,16 @@ void main() } else { - endPoint = var_Position; + #ifdef OUTSIDE_FOG + endPoint = var_Position + fogBoundaryDist * viewDir; + #else + endPoint = var_Position; + #endif + distInFog = fogBoundaryDist; } - float t0 = dot(var_FogSurface.xyz, u_ViewOrigin) + var_FogSurface.w; + float t0 = dot(var_FogSurface.xyz, startPoint) + var_FogSurface.w; float t1 = dot(var_FogSurface.xyz, endPoint) + var_FogSurface.w; float s = distInFog * GetFogGradientModifier(u_FogGradient.y, t0, t1) * u_FogGradient.x; diff --git a/src/engine/renderer/glsl_source/fogGlobal_vp.glsl b/src/engine/renderer/glsl_source/fogGlobal_vp.glsl index ee2071e261..c139b90eac 100644 --- a/src/engine/renderer/glsl_source/fogGlobal_vp.glsl +++ b/src/engine/renderer/glsl_source/fogGlobal_vp.glsl @@ -40,10 +40,23 @@ IN vec4 attr_FogSurface; OUT(smooth) vec3 var_Position; OUT(flat) vec4 var_FogSurface; +#ifdef OUTSIDE_FOG +#define NUM_PLANES 5 +IN vec4 attr_FogPlanes[NUM_PLANES]; +OUT(flat) vec4 var_FogPlanes[NUM_PLANES]; +#endif + void main() { vec4 position = vec4(attr_Position, 1.0); gl_Position = u_ModelViewProjectionMatrix * position; var_Position = attr_Position; var_FogSurface = attr_FogSurface; + + #ifdef OUTSIDE_FOG + for (int i = 0; i < NUM_PLANES; i++) + { + var_FogPlanes[i] = attr_FogPlanes[i]; + } + #endif } diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 608f714048..203de473e7 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1440,7 +1440,9 @@ void Render_fogGlobal( shaderStage_t *stage ) RB_PrepareForSamplingDepthMap(); - GL_Cull( cullType_t::CT_FRONT_SIDED ); + GL_Cull( stage->shader->cullType ); + + gl_fogGlobalShader->SetOutsideFog( stage->type == stageType_t::ST_FOGMAP_OUTER ); gl_fogGlobalShader->BindProgram(); @@ -1453,12 +1455,26 @@ void Render_fogGlobal( shaderStage_t *stage ) SetUniform_Color( gl_fogGlobalShader, stage->shader->fogParms.color ); } - // It's important to avoid far plane clipping - matrix_t projection, mvp; - MatrixPerspectiveProjectionFovXYInfiniteRH( projection, tr.refdef.fov_x, tr.refdef.fov_y, 1.0f ); - MatrixMultiply( projection, glState.modelViewMatrix[ glState.stackIndex ], mvp ); + switch ( stage->type ) + { + case stageType_t::ST_FOGMAP_INNER: + { + // It's important to avoid far plane clipping + matrix_t projection, mvp; + MatrixPerspectiveProjectionFovXYInfiniteRH( projection, tr.refdef.fov_x, tr.refdef.fov_y, 1.0f ); + MatrixMultiply( projection, glState.modelViewMatrix[ glState.stackIndex ], mvp ); + gl_fogGlobalShader->SetUniform_ModelViewProjectionMatrix( mvp ); + break; + } + case stageType_t::ST_FOGMAP_OUTER: + { + gl_fogGlobalShader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); + break; + } + default: + ASSERT_UNREACHABLE(); + } - gl_fogGlobalShader->SetUniform_ModelViewProjectionMatrix( mvp ); gl_fogGlobalShader->SetUniform_UnprojectMatrix( backEnd.viewParms.unprojectionMatrix ); // bind u_DepthMap diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index e5a6c2138c..d802c5aa63 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -3264,10 +3264,11 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) struct FogVert { - float position[ 3 ]; + vec3_t position; vec4_t surface; + vec4_t planes[ 5 ]; // faces other than the current triangle's }; - std::vector fogVerts(8 * count); + std::vector fogVerts(24 * count); std::vector fogIndexes(36 * count); for ( i = 0; i < count; i++, fogs++ ) @@ -3349,22 +3350,50 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) out->surface[ 3 ] = -s_worldData.planes[ planeNum ].dist; } - // add faces of fog brush for drawing fog from inside - for ( int p = 0; p < 8; p++ ) + // add faces of fog brush with information about the other planes + // TODO: allow non-axis-aligned boxes. The GLSL can draw any brush with up to 6 faces. + constexpr int faces[ 6 ][ 4 ] { - fogVerts[ i * 8 + p ].position[ 0 ] = out->bounds[ p & 1 ][ 0 ]; - fogVerts[ i * 8 + p ].position[ 1 ] = out->bounds[ ( p >> 1 ) & 1 ][ 1 ]; - fogVerts[ i * 8 + p ].position[ 2 ] = out->bounds[ p >> 2 ][ 2 ]; + { 2, 0, 6, 4 }, + { 1, 3, 5, 7 }, + { 0, 1, 4, 5 }, + { 3, 2, 7, 6 }, + { 2, 3, 0, 1 }, + { 7, 6, 5, 4 } + }; + + for ( int face = 0; face < 6; face++ ) + { + for ( int v = 0; v < 4; v++ ) + { + FogVert &vert = fogVerts[ i * 24 + face * 4 + v ]; + int p = faces[ face ][ v ]; + vert.position[ 0 ] = out->bounds[ p & 1 ][ 0 ]; + vert.position[ 1 ] = out->bounds[ ( p >> 1 ) & 1 ][ 1 ]; + vert.position[ 2 ] = out->bounds[ p >> 2 ][ 2 ]; - VectorCopy( out->surface, fogVerts[ i * 8 + p ].surface ); - fogVerts[ i * 8 + p ].surface[ 3 ] = -out->surface[ 3 ]; - } + VectorCopy( out->surface, vert.surface ); + vert.surface[ 3 ] = -out->surface[ 3 ]; - constexpr int box[ 36 ] = { 2, 3, 0, 0, 3, 1, 0, 1, 4, 4, 1, 5, 2, 0, 6, 6, 0, 4, - 1, 3, 5, 5, 3, 7, 3, 2, 7, 7, 2, 6, 7, 6, 5, 5, 6, 4 }; - for ( int p = 0; p < 36; p++ ) - { - fogIndexes[ 36 * i + p ] = 8 * i + box[ p ]; + vec4_t *plane = vert.planes; + for ( int otherFace = 0; otherFace < 6; otherFace++ ) + { + if ( face != otherFace ) + { + planeNum = LittleLong( sides[ firstSide + otherFace ].planeNum ); + VectorCopy( s_worldData.planes[ planeNum ].normal, *plane ); + ( *plane )[ 3 ] = s_worldData.planes[ planeNum ].dist; + ++plane; + } + } + } + + fogIndexes[ 36 * i + 6 * face + 0 ] = i * 24 + face * 4 + 0; + fogIndexes[ 36 * i + 6 * face + 1 ] = i * 24 + face * 4 + 1; + fogIndexes[ 36 * i + 6 * face + 2 ] = i * 24 + face * 4 + 2; + fogIndexes[ 36 * i + 6 * face + 3 ] = i * 24 + face * 4 + 2; + fogIndexes[ 36 * i + 6 * face + 4 ] = i * 24 + face * 4 + 1; + fogIndexes[ 36 * i + 6 * face + 5 ] = i * 24 + face * 4 + 3; } // add draw surf for fog brush faces @@ -3378,11 +3407,16 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) vertexAttributeSpec_t attributes[] { { ATTR_INDEX_POSITION, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].position, 3, sizeof( fogVerts[ 0 ] ), 0 }, { ATTR_INDEX_FOG_SURFACE, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].surface, 4, sizeof( fogVerts[ 0 ] ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 0, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 0 ], 4, sizeof( FogVert ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 1, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 1 ], 4, sizeof( FogVert ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 2, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 2 ], 4, sizeof( FogVert ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 3, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 3 ], 4, sizeof( FogVert ), 0 }, + { ATTR_INDEX_FOG_PLANES_0 + 4, GL_FLOAT, GL_FLOAT, &fogVerts[ 0 ].planes[ 4 ], 4, sizeof( FogVert ), 0 }, }; VBO_t *fogVBO = R_CreateStaticVBO( "fogs VBO", std::begin( attributes ), std::end( attributes ), fogVerts.size() ); IBO_t *fogIBO = R_CreateStaticIBO( "fogs IBO", &fogIndexes[ 0 ], fogIndexes.size() ); - SetupVAOBuffers( fogVBO, fogIBO, ATTR_POSITION | ATTR_FOG_SURFACE, &fogVBO->VAO ); + SetupVAOBuffers( fogVBO, fogIBO, ATTR_POSITION | ATTR_FOG_SURFACE | ATTR_FOG_PLANES, &fogVBO->VAO ); for ( int j = 1; j < s_worldData.numFogs; j++ ) { diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index e4f76b5122..e8001901f1 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -1542,7 +1542,11 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p gl_fogQuake3Shader->MarkProgramForBuilding(); } - gl_fogGlobalShader->MarkProgramForBuilding(); + for ( bool outer : { false, true } ) + { + gl_fogGlobalShader->SetOutsideFog( outer ); + gl_fogGlobalShader->MarkProgramForBuilding(); + } } for ( int i = 0; i < tr.numModels; i++ ) { diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index d1a18c21a7..06d124a60f 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -940,6 +940,7 @@ enum ST_LIQUIDMAP, ST_FOGMAP, ST_FOGMAP_INNER, // a fog seen from inside + ST_FOGMAP_OUTER, // a fog seen from outside ST_LIGHTMAP, ST_STYLELIGHTMAP, ST_STYLECOLORMAP, @@ -1204,6 +1205,7 @@ enum struct shader_t *depthShader; struct shader_t *fogShader; struct shader_t *fogInnerShader; + struct shader_t *fogOuterShader; struct shader_t *next; }; @@ -3353,8 +3355,7 @@ void GLimp_LogComment_( std::string comment ); void RE_AddPolyToSceneET( qhandle_t hShader, int numVerts, const polyVert_t *verts ); void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ); - bool R_InsideFog( int fognum ); - void R_AddInnerFogSurfaces(); + void R_AddFogBrushSurfaces(); void RE_AddDynamicLightToSceneET( const vec3_t org, float radius, float intensity, float r, float g, float b, qhandle_t hShader, int flags ); void RE_AddDynamicLightToSceneQ3A( const vec3_t org, float intensity, float r, float g, float b ); diff --git a/src/engine/renderer/tr_main.cpp b/src/engine/renderer/tr_main.cpp index 306f80acde..25db5ae959 100644 --- a/src/engine/renderer/tr_main.cpp +++ b/src/engine/renderer/tr_main.cpp @@ -1734,8 +1734,7 @@ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, i bool usingMaterial = glConfig.usingMaterialSystem && !r_materialSystemSkip.Get(); // don't fog surfaces that already were covered by globalFog - if ( !shader->noFog && fogNum >= 1 && - !( usingMaterial ? fogNum == tr.world->globalFog : R_InsideFog( fogNum ) ) ) + if ( usingMaterial && !shader->noFog && fogNum >= 1 && fogNum != tr.world->globalFog ) { R_AddDrawSurf( surface, shader->fogShader, 0, fogNum, bspSurface ); } @@ -2086,7 +2085,7 @@ void R_RenderView( viewParms_t *parms ) // set camera frustum planes in world space again, but this time including the far plane tr.orientation = tr.viewParms.world; - R_AddInnerFogSurfaces(); + R_AddFogBrushSurfaces(); R_AddEntitySurfaces(); diff --git a/src/engine/renderer/tr_scene.cpp b/src/engine/renderer/tr_scene.cpp index 85c2e3f1b4..8fb7ffdb1d 100644 --- a/src/engine/renderer/tr_scene.cpp +++ b/src/engine/renderer/tr_scene.cpp @@ -248,16 +248,40 @@ void RE_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *vert //================================================================================= -bool R_InsideFog( int fognum ) +// TODO don't recalc this every time, OR check if the near plane actually intersects the fog? +static float NearPlaneCornerDist() +{ + plane_t frustum[ 6 ]; + for ( int i = 0; i < 6; i++ ) + { + VectorCopy( tr.viewParms.frustum[ i ].normal, frustum[ i ].normal ); + frustum[ i ].dist = tr.viewParms.frustum[ i ].dist; + } + + vec3_t intersection; + PlanesGetIntersectionPoint( + frustum[ FRUSTUM_NEAR ], frustum[ FRUSTUM_LEFT ], frustum[ FRUSTUM_TOP ], intersection ); + + return Distance( tr.viewParms.orientation.origin, intersection ); +} + +// Returns true also if the view origin is slightly outside, but close enough that the view +// frustum's near plane might intersect. This causes some slight artifacts - the fog grows +// by ~5 qu when the view origin crosses the tolerance boundary. However it's not an +// issue on the surface plane, since the GLSL calcs chop off overhang there. Note that +// with original Q3 you are required to construct the fog so that it is impossible to pass +// through non-surface planes at all. +static bool R_InsideFog( int fognum ) { // TODO: with portals this should use the point at the view frustum near plane instead const vec3_t &origin = tr.viewParms.orientation.origin; const auto &bounds = tr.world->fogs[ fognum ].bounds; + float tol = NearPlaneCornerDist() + 0.1f; for ( int i = 0; i < 3; i++ ) { - if ( origin[ i ] < bounds[ 0 ][ i ] || origin[ i ] > bounds[ 1 ][ i ] ) + if ( origin[ i ] + tol < bounds[ 0 ][ i ] || origin[ i ] - tol > bounds[ 1 ][ i ] ) { return false; } @@ -266,7 +290,7 @@ bool R_InsideFog( int fognum ) return true; } -void R_AddInnerFogSurfaces() +void R_AddFogBrushSurfaces() { if ( glConfig.usingMaterialSystem && !r_materialSystemSkip.Get() ) { @@ -278,13 +302,14 @@ void R_AddInnerFogSurfaces() } else { + // TODO: incorporate them in the BSP? for ( int i = 1; i < tr.world->numFogs; i++ ) { - if ( R_InsideFog( i ) ) - { - const fog_t &fog = tr.world->fogs[ i ]; - R_AddDrawSurf( ( surfaceType_t *)&fog.surf, fog.shader->fogInnerShader, -1, 0 ); - } + const fog_t &fog = tr.world->fogs[ i ]; + shader_t *shader = R_InsideFog( i ) + ? fog.shader->fogInnerShader + : fog.shader->fogOuterShader; + R_AddDrawSurf( ( surfaceType_t *)&fog.surf, shader, -1, 0 ); } } } diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index 34dab5d9e2..b0949c45e0 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -4621,6 +4621,7 @@ static bool ParseShader( const char *_text ) } // fogGradient // Default: fogGradient expFalloff 5 + // TODO: make the gradient plane configurable else if ( !Q_stricmp( token, "fogGradient" ) ) { token = COM_ParseExt2( text, false ); @@ -5657,6 +5658,7 @@ static void SetStagesRenderers() }; break; case stageType_t::ST_FOGMAP_INNER: + case stageType_t::ST_FOGMAP_OUTER: stageRendererOptions = { &Render_fogGlobal, &MarkShaderBuildNOP, &UpdateSurfaceDataNOP, &BindShaderNOP, &ProcessMaterialNOP, @@ -5846,6 +5848,7 @@ static void ValidateStage( shaderStage_t *pStage ) { stageType_t::ST_LIQUIDMAP, { true, true, false, "liquidMap" } }, { stageType_t::ST_FOGMAP, { true, false, false, "fogMap" } }, { stageType_t::ST_FOGMAP_INNER, { true, false, false, "fogMapInner" } }, + { stageType_t::ST_FOGMAP_OUTER, { true, false, false, "fogMapOuter" } }, // The lightmap is fetched at render time. { stageType_t::ST_LIGHTMAP, { true, false, false, "light map" } }, // The lightmap is fetched at render time. @@ -6133,8 +6136,17 @@ static shader_t *FinishShader() stages[ 0 ].active = true; stages[ 0 ].type = stageType_t::ST_FOGMAP_INNER; stages[ 0 ].stateBits = GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + shader.cullType = cullType_t::CT_FRONT_SIDED; SetStagesRenderers(); ret->fogInnerShader = MakeShaderPermanent(); + + shader.name[ nameLength ] = '\0'; + Q_strcat( shader.name, sizeof( shader.name ), "$fogOut" ); + stages[ 0 ].type = stageType_t::ST_FOGMAP_OUTER; + stages[ 0 ].stateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + shader.cullType = cullType_t::CT_BACK_SIDED; + SetStagesRenderers(); + ret->fogOuterShader = MakeShaderPermanent(); } if ( glConfig.usingMaterialSystem && !tr.worldLoaded ) { From 7c68c06622fa303d31d62141bd13f9d9712bb21e Mon Sep 17 00:00:00 2001 From: slipher Date: Fri, 6 Feb 2026 19:55:14 -0600 Subject: [PATCH 10/12] Use new depth-based fog shaders in material system Use the depth-based fog shaders (currently called "fogGlobal") when the material system is enabled as well. For now they are always run with the 'core' rendering loop. NUKE the fogQuake3 shader and all the code for propagating fog nums with surfaces. Fixes #1798 (marks drawn over fog with material system) by virtue of not drawing fogs with the material system. Also brings improvements described for the core renderer in previous commits: - MSAA works with fog without artifacts - Fog volumes can be open on any side --- src.cmake | 2 - src/engine/renderer/GeometryOptimiser.cpp | 14 +-- src/engine/renderer/Material.cpp | 79 +------------ src/engine/renderer/Material.h | 10 +- src/engine/renderer/gl_shader.cpp | 34 ------ src/engine/renderer/gl_shader.h | 66 ----------- .../renderer/glsl_source/fogQuake3_fp.glsl | 62 ----------- .../renderer/glsl_source/fogQuake3_vp.glsl | 84 -------------- src/engine/renderer/tr_animation.cpp | 8 +- src/engine/renderer/tr_backend.cpp | 55 +++++---- src/engine/renderer/tr_bsp.cpp | 16 --- src/engine/renderer/tr_init.cpp | 8 -- src/engine/renderer/tr_local.h | 28 +---- src/engine/renderer/tr_main.cpp | 105 +----------------- src/engine/renderer/tr_mesh.cpp | 8 +- src/engine/renderer/tr_model_iqm.cpp | 6 +- src/engine/renderer/tr_scene.cpp | 73 ++---------- src/engine/renderer/tr_shade.cpp | 92 +-------------- src/engine/renderer/tr_shader.cpp | 54 +-------- src/engine/renderer/tr_surface.cpp | 10 +- src/engine/renderer/tr_world.cpp | 11 +- 21 files changed, 72 insertions(+), 753 deletions(-) delete mode 100644 src/engine/renderer/glsl_source/fogQuake3_fp.glsl delete mode 100644 src/engine/renderer/glsl_source/fogQuake3_vp.glsl diff --git a/src.cmake b/src.cmake index 078d111a43..3644a0740b 100644 --- a/src.cmake +++ b/src.cmake @@ -131,8 +131,6 @@ set(GLSL_EMBED_LIST # Regular shaders fogGlobal_vp.glsl fogGlobal_fp.glsl - fogQuake3_vp.glsl - fogQuake3_fp.glsl generic_vp.glsl generic_fp.glsl heatHaze_vp.glsl diff --git a/src/engine/renderer/GeometryOptimiser.cpp b/src/engine/renderer/GeometryOptimiser.cpp index d802a8c642..b151ddd988 100644 --- a/src/engine/renderer/GeometryOptimiser.cpp +++ b/src/engine/renderer/GeometryOptimiser.cpp @@ -54,12 +54,6 @@ static int LeafSurfaceCompare( const void* a, const void* b ) { return 1; } - if ( aa->fogIndex < bb->fogIndex ) { - return -1; - } else if ( aa->fogIndex > bb->fogIndex ) { - return 1; - } - // sort by leaf if ( aa->scratch2 < bb->scratch2 ) { return -1; @@ -168,7 +162,6 @@ void OptimiseMapGeometryCore( world_t* world, bspSurface_t** rendererSurfaces, i shader_t* shader1 = surf1->shader; - int fogIndex1 = surf1->fogIndex; int lightMapNum1 = surf1->lightmapNum; surf1->viewCount = surf1 - world->surfaces; surf1->scratch1 = j; @@ -187,9 +180,8 @@ void OptimiseMapGeometryCore( world_t* world, bspSurface_t** rendererSurfaces, i } shader_t* shader2 = surf2->shader; - int fogIndex2 = surf2->fogIndex; int lightMapNum2 = surf2->lightmapNum; - if ( shader1 != shader2 || fogIndex1 != fogIndex2 || lightMapNum1 != lightMapNum2 ) { + if ( shader1 != shader2 || lightMapNum1 != lightMapNum2 ) { continue; } @@ -315,7 +307,6 @@ void MergeLeafSurfacesCore( world_t* world, bspSurface_t** rendererSurfaces, int SphereFromBounds( vboSurf->bounds[0], vboSurf->bounds[1], vboSurf->origin, &vboSurf->radius ); mergedSurf->data = ( surfaceType_t* ) vboSurf; - mergedSurf->fogIndex = surf1->fogIndex; mergedSurf->shader = surf1->shader; mergedSurf->lightmapNum = surf1->lightmapNum; mergedSurf->viewCount = -1; @@ -557,7 +548,6 @@ std::vector OptimiseMapGeometryMaterial( world_t* world, bspSur srf.skyBrush = surface->skyBrush; srf.lightMapNum = surface->lightmapNum; - srf.fog = surface->fogIndex; srf.portalNum = surface->portalNum; srf.firstIndex = ( ( srfGeneric_t* ) surface->data )->firstIndex; @@ -568,7 +558,7 @@ std::vector OptimiseMapGeometryMaterial( world_t* world, bspSur VectorCopy( ( ( srfGeneric_t* ) surface->data )->origin, srf.origin ); srf.radius = ( ( srfGeneric_t* ) surface->data )->radius; - materialSystem.GenerateMaterial( &srf, world->globalFog ); + materialSystem.GenerateMaterial( &srf ); static const float MAX_NORMAL_SURFACE_DISTANCE = 65536.0f * sqrtf( 2.0f ); if ( VectorLength( ( ( srfGeneric_t* ) surface->data )->bounds[0] ) > MAX_NORMAL_SURFACE_DISTANCE diff --git a/src/engine/renderer/Material.cpp b/src/engine/renderer/Material.cpp index 0eb6a68392..0909b96ada 100644 --- a/src/engine/renderer/Material.cpp +++ b/src/engine/renderer/Material.cpp @@ -334,14 +334,6 @@ void UpdateSurfaceDataLiquid( uint32_t* materials, shaderStage_t* pStage, bool, gl_liquidShaderMaterial->WriteUniformsToBuffer( materials, GLShader::MATERIAL ); } -void UpdateSurfaceDataFog( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ) { - // shader_t* shader = pStage->shader; - - materials += pStage->bufferOffset; - - gl_fogQuake3ShaderMaterial->WriteUniformsToBuffer( materials, GLShader::MATERIAL ); -} - /* * Buffer layout: * // Static surfaces data: @@ -746,10 +738,10 @@ class ListMaterialsCmd : public Cmd::StaticCmd { shaderSortName.at( materialPack.fromSort ), shaderSortName.at( materialPack.toSort ) ); for ( const Material& material : materialPack.materials ) { Print( "id: %u, sync: %5s, stateBits: %10x, GLShader: %s, GLProgramID: %u," - " deform: %i, fog: %i, drawCmdCount: %u", + " deform: %i, drawCmdCount: %u", material.id, material.useSync, material.stateBits, material.shader->_name, material.program, - material.deformIndex, material.fog, material.drawCommandCount ); + material.deformIndex, material.drawCommandCount ); } } } @@ -762,7 +754,6 @@ static std::string GetStageInfo( const shaderStage_t* pStage, const uint32_t dyn { BindShaderGeneric3D, "genericMaterial " }, { BindShaderLightMapping, "lightMappingMaterial" }, { BindShaderHeatHaze, "heatHazeMaterial " }, - { BindShaderFog, "fogQuake3Material " }, { BindShaderLiquid, "liquidMaterial " }, { BindShaderScreen, "screenMaterial " }, { BindShaderSkybox, "skyboxMaterial " }, @@ -1090,43 +1081,6 @@ void BindShaderLiquid( Material* material ) { gl_liquidShaderMaterial->SetUniform_PortalMapBindless( GL_BindToTMU( 1, tr.portalRenderImage ) ); } -void BindShaderFog( Material* material ) { - // Bind shader program. - gl_fogQuake3ShaderMaterial->SetDeform( material->deformIndex ); - gl_fogQuake3ShaderMaterial->BindProgram(); - - // Set shader uniforms. - const fog_t* fog = tr.world->fogs + material->fog; - - // rotate the gradient vector for this orientation - float eyeT; - vec4_t fogDepthVector; - if ( fog->hasSurface ) { - VectorCopy( fog->surface, fogDepthVector ); - fogDepthVector[ 3 ] = -fog->surface[ 3 ]; - eyeT = DotProduct( backEnd.viewParms.orientation.origin, fogDepthVector ) + fogDepthVector[ 3 ]; - } else { - Vector4Set( fogDepthVector, 0, 0, 0, 1 ); - eyeT = 1; // non-surface fog always has eye inside - } - - // Note: things that seemingly should be per-shader or per-surface can be set as global uniforms - // since fognum is grouped with the GL state stuff, segregating each fognum in a separate draw call. - - gl_fogQuake3ShaderMaterial->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - gl_fogQuake3ShaderMaterial->SetUniform_FogGradient( - 1.0f / fog->shader->fogParms.depthForOpaque, fog->shader->fogParms.falloffExp ); - gl_fogQuake3ShaderMaterial->SetUniform_FogDepthVector( fogDepthVector ); - gl_fogQuake3ShaderMaterial->SetUniform_FogEyeT( eyeT ); - - gl_fogQuake3ShaderMaterial->SetUniform_ColorGlobal_Uint( fog->shader->fogParms.color ); - - gl_fogQuake3ShaderMaterial->SetUniform_ModelMatrix( backEnd.orientation.transformMatrix ); - gl_fogQuake3ShaderMaterial->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[glState.stackIndex] ); - - gl_fogQuake3ShaderMaterial->SetUniform_Time( backEnd.refdef.floatTime - backEnd.currentEntity->e.shaderTime ); -} - void ProcessMaterialNONE( Material*, shaderStage_t*, MaterialSurface* ) { ASSERT_UNREACHABLE(); } @@ -1264,15 +1218,6 @@ void ProcessMaterialLiquid( Material* material, shaderStage_t* pStage, MaterialS material->program = gl_liquidShaderMaterial->GetProgram( materialSystem.buildOneShader ); } -void ProcessMaterialFog( Material* material, shaderStage_t* pStage, MaterialSurface* surface ) { - material->shader = gl_fogQuake3ShaderMaterial; - material->fog = surface->fog; - - gl_fogQuake3ShaderMaterial->SetDeform( pStage->deformIndex ); - - material->program = gl_fogQuake3ShaderMaterial->GetProgram( materialSystem.buildOneShader ); -} - void MaterialSystem::AddStage( MaterialSurface* surface, shaderStage_t* pStage, uint32_t stage, const bool mayUseVertexOverbright, const bool vertexLit, const bool fullbright ) { const int variant = ( mayUseVertexOverbright ? ShaderStageVariant::VERTEX_OVERBRIGHT : 0 ) @@ -1496,7 +1441,7 @@ void MaterialSystem::ProcessStage( MaterialSurface* surface, shaderStage_t* pSta /* This will only generate a material itself A material represents a distinct global OpenGL state (e. g. blend function, depth test, depth write etc.) Materials can have a dependency on other materials to make sure that consecutive stages are rendered in the proper order */ -void MaterialSystem::GenerateMaterial( MaterialSurface* surface, int globalFog ) { +void MaterialSystem::GenerateMaterial( MaterialSurface* surface ) { uint32_t stage = 0; uint32_t previousMaterialID = 0; @@ -1516,13 +1461,6 @@ void MaterialSystem::GenerateMaterial( MaterialSurface* surface, int globalFog ) surface->stages++; } - - if ( !surface->shader->noFog && surface->fog >= 1 && surface->fog != globalFog ) { - uint32_t unused; - ProcessStage( surface, surface->shader->fogShader->stages, surface->shader->fogShader, packIDs, stage, unused, true ); - - surface->stages++; - } } void MaterialSystem::SetWorldBounds( vec3_t bounds[2] ) { @@ -1918,7 +1856,6 @@ bool MaterialSystem::AddPortalSurface( uint32_t viewID, PortalSurface* portalSur { drawSurf.bspSurface = portalSurfaces[portalSurface->drawSurfID].bspSurface; drawSurf.entity = &tr.worldEntity; - drawSurf.fog = portalSurfaces[portalSurface->drawSurfID].fog; drawSurf.portalNum = portalSurfaces[portalSurface->drawSurfID].portalNum; drawSurf.shader = portalSurfaces[portalSurface->drawSurfID].shader; drawSurf.surface = portalSurfaces[portalSurface->drawSurfID].surface; @@ -1994,7 +1931,7 @@ void MaterialSystem::AddAutospriteSurfaces() { for ( const bspSurface_t* surface : autospriteSurfaces ) { R_AddDrawSurf( surface->data, surface->shader, - surface->lightmapNum, surface->fogIndex, true ); + surface->lightmapNum, true ); } } @@ -2046,7 +1983,7 @@ void MaterialSystem::RenderMaterials( const shaderSort_t fromSort, const shaderS } tr.drawingSky = true; - Tess_Begin( Tess_StageIteratorSky, skyShader, false, -1, 0, false ); + Tess_Begin( Tess_StageIteratorSky, skyShader, false, -1, false ); Tess_End(); } } @@ -2096,12 +2033,6 @@ void MaterialSystem::RenderMaterial( Material& material, const uint32_t viewID ) stateBits &= ~( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ); } - if( material.shaderBinder == BindShaderFog ) { - if ( r_noFog->integer || ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) { - return; - } - } - backEnd.currentEntity = &tr.worldEntity; GL_State( stateBits ); diff --git a/src/engine/renderer/Material.h b/src/engine/renderer/Material.h index 46ec632eb8..0147ac030b 100644 --- a/src/engine/renderer/Material.h +++ b/src/engine/renderer/Material.h @@ -85,7 +85,6 @@ struct MaterialSurface { bool skyBrush; int16_t lightMapNum; - int fog; int portalNum = -1; GLuint firstIndex; @@ -148,15 +147,13 @@ struct Material { bool usePolygonOffset = false; - int fog = 0; - uint32_t drawCommandCount = 0; bool texturesResident = false; std::vector textures; bool operator==( const Material& other ) { return program == other.program && stateBits == other.stateBits - && fog == other.fog && cullType == other.cullType && usePolygonOffset == other.usePolygonOffset; + && cullType == other.cullType && usePolygonOffset == other.usePolygonOffset; } void AddTexture( Texture* texture ) { @@ -381,7 +378,7 @@ class MaterialSystem { const bool mayUseVertexOverbright, const bool vertexLit, const bool fullbright ); void ProcessStage( MaterialSurface* surface, shaderStage_t* pStage, shader_t* shader, uint32_t* packIDs, uint32_t& stage, uint32_t& previousMaterialID, bool skipStageSync = false ); - void GenerateMaterial( MaterialSurface* surface, int globalFog ); + void GenerateMaterial( MaterialSurface* surface ); void GenerateWorldMaterialsBuffer(); void GenerateWorldCommandBuffer( std::vector& surfaces ); void GeneratePortalBoundingSpheres(); @@ -462,7 +459,6 @@ void UpdateSurfaceDataSkybox( uint32_t* materials, shaderStage_t* pStage, bool, void UpdateSurfaceDataScreen( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ); void UpdateSurfaceDataHeatHaze( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ); void UpdateSurfaceDataLiquid( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ); -void UpdateSurfaceDataFog( uint32_t* materials, shaderStage_t* pStage, bool, bool, bool ); void BindShaderNONE( Material* ); void BindShaderNOP( Material* ); @@ -473,7 +469,6 @@ void BindShaderSkybox( Material* material ); void BindShaderScreen( Material* material ); void BindShaderHeatHaze( Material* material ); void BindShaderLiquid( Material* material ); -void BindShaderFog( Material* material ); void ProcessMaterialNONE( Material*, shaderStage_t*, MaterialSurface* ); void ProcessMaterialNOP( Material*, shaderStage_t*, MaterialSurface* ); @@ -484,6 +479,5 @@ void ProcessMaterialSkybox( Material* material, shaderStage_t* pStage, MaterialS void ProcessMaterialScreen( Material* material, shaderStage_t* pStage, MaterialSurface* /* surface */ ); void ProcessMaterialHeatHaze( Material* material, shaderStage_t* pStage, MaterialSurface* surface ); void ProcessMaterialLiquid( Material* material, shaderStage_t* pStage, MaterialSurface* /* surface */ ); -void ProcessMaterialFog( Material* material, shaderStage_t* pStage, MaterialSurface* surface ); #endif // MATERIAL_H diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 56fce706af..88161094aa 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -68,8 +68,6 @@ GLShader_generic *gl_genericShader = nullptr; GLShader_genericMaterial *gl_genericShaderMaterial = nullptr; GLShader_lightMapping *gl_lightMappingShader = nullptr; GLShader_lightMappingMaterial *gl_lightMappingShaderMaterial = nullptr; -GLShader_fogQuake3 *gl_fogQuake3Shader = nullptr; -GLShader_fogQuake3Material *gl_fogQuake3ShaderMaterial = nullptr; GLShader_heatHaze *gl_heatHazeShader = nullptr; GLShader_heatHazeMaterial *gl_heatHazeShaderMaterial = nullptr; GLShader_liquid *gl_liquidShader = nullptr; @@ -2690,38 +2688,6 @@ GLShader_skyboxMaterial::GLShader_skyboxMaterial() : u_ModelViewProjectionMatrix( this ) {} -GLShader_fogQuake3::GLShader_fogQuake3() : - GLShader( "fogQuake3", ATTR_POSITION | ATTR_QTANGENT, - false, "fogQuake3", "fogQuake3" ), - u_ModelMatrix( this ), - u_ModelViewProjectionMatrix( this ), - u_ColorGlobal_Float( this ), - u_ColorGlobal_Uint( this ), - u_Bones( this ), - u_VertexInterpolation( this ), - u_ViewOrigin( this ), - u_FogGradient( this ), - u_FogDepthVector( this ), - u_FogEyeT( this ), - GLDeformStage( this ), - GLCompileMacro_USE_VERTEX_SKINNING( this ), - GLCompileMacro_USE_VERTEX_ANIMATION( this ) -{ -} - -GLShader_fogQuake3Material::GLShader_fogQuake3Material() : - GLShader( "fogQuake3Material", ATTR_POSITION | ATTR_QTANGENT, - true, "fogQuake3", "fogQuake3" ), - u_ModelMatrix( this ), - u_ModelViewProjectionMatrix( this ), - u_ColorGlobal_Uint( this ), - u_ViewOrigin( this ), - u_FogGradient( this ), - u_FogDepthVector( this ), - u_FogEyeT( this ), - GLDeformStage( this ) { -} - // TODO: rename GLShader_fogGlobal::GLShader_fogGlobal() : GLShader( "fogGlobal", ATTR_POSITION | ATTR_FOG_SURFACE, diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index 64fb38db96..244f526ec6 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -2696,36 +2696,6 @@ template void SetUniform_ColorModulateColorGen( } } -class u_FogDepthVector : - GLUniform4f -{ -public: - u_FogDepthVector( GLShader *shader ) : - GLUniform4f( shader, "u_FogDepthVector", PUSH ) - { - } - - void SetUniform_FogDepthVector( const vec4_t v ) - { - this->SetValue( v ); - } -}; - -class u_FogEyeT : - GLUniform1f -{ -public: - u_FogEyeT( GLShader *shader ) : - GLUniform1f( shader, "u_FogEyeT", PUSH ) - { - } - - void SetUniform_FogEyeT( float value ) - { - this->SetValue( value ); - } -}; - class u_DeformEnable : GLUniform1f { public: @@ -3208,40 +3178,6 @@ class GLShader_skyboxMaterial : GLShader_skyboxMaterial(); }; -class GLShader_fogQuake3 : - public GLShader, - public u_ModelMatrix, - public u_ModelViewProjectionMatrix, - public u_ColorGlobal_Float, - public u_ColorGlobal_Uint, - public u_Bones, - public u_VertexInterpolation, - public u_ViewOrigin, - public u_FogGradient, - public u_FogDepthVector, - public u_FogEyeT, - public GLDeformStage, - public GLCompileMacro_USE_VERTEX_SKINNING, - public GLCompileMacro_USE_VERTEX_ANIMATION -{ -public: - GLShader_fogQuake3(); -}; - -class GLShader_fogQuake3Material : - public GLShader, - public u_ModelMatrix, - public u_ModelViewProjectionMatrix, - public u_ColorGlobal_Uint, - public u_ViewOrigin, - public u_FogGradient, - public u_FogDepthVector, - public u_FogEyeT, - public GLDeformStage { - public: - GLShader_fogQuake3Material(); -}; - class GLShader_fogGlobal : public GLShader, public u_DepthMap, @@ -3605,8 +3541,6 @@ extern GLShader_generic *gl_genericShader; extern GLShader_genericMaterial *gl_genericShaderMaterial; extern GLShader_lightMapping *gl_lightMappingShader; extern GLShader_lightMappingMaterial *gl_lightMappingShaderMaterial; -extern GLShader_fogQuake3 *gl_fogQuake3Shader; -extern GLShader_fogQuake3Material *gl_fogQuake3ShaderMaterial; extern GLShader_heatHaze *gl_heatHazeShader; extern GLShader_heatHazeMaterial *gl_heatHazeShaderMaterial; extern GLShader_liquid *gl_liquidShader; diff --git a/src/engine/renderer/glsl_source/fogQuake3_fp.glsl b/src/engine/renderer/glsl_source/fogQuake3_fp.glsl deleted file mode 100644 index afb3630a00..0000000000 --- a/src/engine/renderer/glsl_source/fogQuake3_fp.glsl +++ /dev/null @@ -1,62 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2011 Robert Beckebans - -This file is part of XreaL source code. - -XreaL source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -XreaL source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with XreaL source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/* fogQuake3_fp.glsl */ - -#insert fogEquation_fp - -#define DEPTHMAP_GLSL - -uniform vec2 u_FogGradient; -uniform float u_FogEyeT; - -IN(smooth) float var_FogPlaneDistance; -IN(smooth) vec3 var_ViewerOffset; -IN(smooth) vec4 var_Color; - -DECLARE_OUTPUT(vec4) - -void main() -{ - #insert material_fp - - if ( var_FogPlaneDistance < -0.01 ) - { - discard; - } - - float distInFog = length(var_ViewerOffset); - - if ( u_FogEyeT < 0 ) // eye outside fog - { - // fraction of the viewer-to-vertex ray which is inside fog - distInFog *= var_FogPlaneDistance / ( max( 0, var_FogPlaneDistance ) - u_FogEyeT ); - } - - float gradient = GetFogGradientModifier(u_FogGradient.y, u_FogEyeT, var_FogPlaneDistance); - - vec4 color = vec4(1, 1, 1, GetFogAlpha(gradient * u_FogGradient.x * distInFog)); - - color *= var_Color; - - outputColor = color; -} diff --git a/src/engine/renderer/glsl_source/fogQuake3_vp.glsl b/src/engine/renderer/glsl_source/fogQuake3_vp.glsl deleted file mode 100644 index 2c8450cbc8..0000000000 --- a/src/engine/renderer/glsl_source/fogQuake3_vp.glsl +++ /dev/null @@ -1,84 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2011 Robert Beckebans - -This file is part of XreaL source code. - -XreaL source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -XreaL source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with XreaL source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -/* fogQuake3_vp.glsl */ - -#insert common -#insert vertexSimple_vp -#insert vertexSkinning_vp -#insert vertexAnimation_vp - -uniform float u_Time; - -uniform colorPack u_ColorGlobal; - -uniform mat4 u_ModelMatrix; -uniform mat4 u_ModelViewProjectionMatrix; -uniform vec3 u_ViewOrigin; - -uniform vec4 u_FogDepthVector; // fog plane - -// how far the vertex is under the fog plane -OUT(smooth) float var_FogPlaneDistance; - -OUT(smooth) vec3 var_ViewerOffset; -OUT(smooth) vec4 var_Color; - -void DeformVertex( inout vec4 pos, - inout vec3 normal, - inout vec2 st, - inout vec4 color, - in float time ); - -void main() -{ - #insert material_vp - - vec4 position; - localBasis LB; - vec2 texCoord, lmCoord; - vec4 color; - - VertexFetch( position, LB, color, texCoord, lmCoord ); - - color = UnpackColor( u_ColorGlobal ); - - DeformVertex( position, - LB.normal, - texCoord, - color, - u_Time ); - - // transform vertex position into homogenous clip-space - gl_Position = u_ModelViewProjectionMatrix * position; - - // transform position into world space - position = u_ModelMatrix * position; - position.xyz /= position.w; - - var_ViewerOffset = position.xyz - u_ViewOrigin; - - // calculate the length in fog - var_FogPlaneDistance = dot(position.xyz, u_FogDepthVector.xyz) + u_FogDepthVector.w; - - var_Color = color; -} diff --git a/src/engine/renderer/tr_animation.cpp b/src/engine/renderer/tr_animation.cpp index ea4e57160a..f0dd4670b5 100644 --- a/src/engine/renderer/tr_animation.cpp +++ b/src/engine/renderer/tr_animation.cpp @@ -661,7 +661,6 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) md5Model_t *model; md5Surface_t *surface; bool personalModel; - int fogNum; model = tr.currentModel->md5; @@ -676,9 +675,6 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) return; } - // see if we are in a fog volume - fogNum = R_FogWorldBox( ent->worldBounds ); - if ( !r_vboModels.Get() || !model->numVBOSurfaces || ( !glConfig.vboVertexSkinningAvailable && ent->e.skeleton.type == refSkeletonType_t::SK_ABSOLUTE ) ) { @@ -732,7 +728,7 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) // don't add third_person objects if not viewing through a portal if ( !personalModel ) { - R_AddDrawSurf( (surfaceType_t*) surface, shader, -1, fogNum ); + R_AddDrawSurf( (surfaceType_t*) surface, shader, -1 ); } } } @@ -781,7 +777,7 @@ void R_AddMD5Surfaces( trRefEntity_t *ent ) // don't add third_person objects if not viewing through a portal if ( !personalModel ) { - R_AddDrawSurf( (surfaceType_t*) vboSurface, shader, -1, fogNum ); + R_AddDrawSurf( (surfaceType_t*) vboSurface, shader, -1 ); } } } diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 203de473e7..e1149d80ed 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -993,7 +993,6 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, trRefEntity_t *entity, *oldEntity; shader_t *shader, *oldShader; int lightmapNum, oldLightmapNum; - int fogNum, oldFogNum; bool bspSurface; bool depthRange, oldDepthRange; int i; @@ -1006,7 +1005,6 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, oldEntity = nullptr; oldShader = nullptr; oldLightmapNum = -1; - oldFogNum = -1; oldDepthRange = false; depthRange = false; @@ -1025,7 +1023,6 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, entity = drawSurf->entity; shader = drawSurf->shader; lightmapNum = drawSurf->lightmapNum(); - fogNum = drawSurf->fog; bspSurface = drawSurf->bspSurface; if( entity == &tr.worldEntity ) { @@ -1039,7 +1036,7 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, continue; } - if ( entity == oldEntity && shader == oldShader && lightmapNum == oldLightmapNum && fogNum == oldFogNum ) + if ( entity == oldEntity && shader == oldShader && lightmapNum == oldLightmapNum ) { // fast path, same as previous sort rb_surfaceTable[Util::ordinal(*drawSurf->surface)](drawSurf->surface ); @@ -1049,18 +1046,17 @@ static void RB_RenderDrawSurfaces( shaderSort_t fromSort, shaderSort_t toSort, // change the tess parameters if needed // an "entityMergable" shader is a shader that can have surfaces from separate // entities merged into a single batch, like smoke and blood puff sprites - if ( shader != oldShader || lightmapNum != oldLightmapNum || fogNum != oldFogNum || ( entity != oldEntity && !shader->entityMergable ) ) + if ( shader != oldShader || lightmapNum != oldLightmapNum || ( entity != oldEntity && !shader->entityMergable ) ) { if ( oldShader != nullptr ) { Tess_End(); } - Tess_Begin( Tess_StageIteratorColor, shader, false, lightmapNum, fogNum, bspSurface ); + Tess_Begin( Tess_StageIteratorColor, shader, false, lightmapNum, bspSurface ); oldShader = shader; oldLightmapNum = lightmapNum; - oldFogNum = fogNum; } // change the modelview matrix if needed @@ -1841,7 +1837,7 @@ static void RB_RenderDebugUtils() GL_LoadModelViewMatrix( backEnd.orientation.modelViewMatrix ); gl_genericShader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); if ( r_showEntityBounds.Get() == 2) { @@ -1958,7 +1954,7 @@ static void RB_RenderDebugUtils() static vec3_t worldOrigins[ MAX_BONES ]; GL_State( GLS_POLYMODE_LINE | GLS_DEPTHTEST_DISABLE ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); for ( j = 0; j < skel->numBones; j++ ) { @@ -2062,7 +2058,7 @@ static void RB_RenderDebugUtils() } Tess_End(); - Tess_Begin( Tess_StageIteratorDebug, shader, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, shader, true, -1 ); gl_genericShader->SetUniform_ColorMapBindless( GL_BindToTMU( 0, shader->stages[ 0 ].bundle[ TB_COLORMAP ].image[ 0 ] ) ); @@ -2125,7 +2121,7 @@ static void RB_RenderDebugUtils() cubemapProbe_t* cubeProbe = &tr.cubeProbes[tr.cubeProbeGrid( x, y, z )]; - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); gl_reflectionShader->SetUniform_CameraPosition( position ); @@ -2141,7 +2137,7 @@ static void RB_RenderDebugUtils() } else { for ( const cubemapProbe_t& cubeProbe : tr.cubeProbes ) { - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); gl_reflectionShader->SetUniform_CameraPosition( cubeProbe.origin ); @@ -2195,7 +2191,7 @@ static void RB_RenderDebugUtils() vec3_t gridPoints[2]; R_GetNearestCubeMaps( backEnd.viewParms.orientation.origin, probes, trilerp, 2, gridPoints ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); vec3_t position; if ( r_showCubeProbes.Get() == Util::ordinal( showCubeProbesMode::GRID ) ) { @@ -2269,7 +2265,7 @@ static void RB_RenderDebugUtils() ); gl_genericShader->SetUniform_TextureMatrix( matrixIdentity ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); GL_CheckErrors(); for ( z = 0; z < tr.world->lightGridBounds[ 2 ]; z++ ) { @@ -2457,7 +2453,7 @@ static void RB_RenderDebugUtils() R_CalcFrustumNearCorners( splitFrustum, nearCorners ); R_CalcFrustumFarCorners( splitFrustum, farCorners ); - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); // draw outer surfaces for ( j = 0; j < 4; j++ ) @@ -2554,7 +2550,7 @@ static void RB_RenderDebugUtils() GL_PolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); } - Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1 ); Tess_AddCube( vec3_origin, node->mins, node->maxs, Color::White ); Tess_End(); @@ -2811,6 +2807,7 @@ static void RB_RenderView( bool depthPass ) } // Here so we don't do an extra MSAA resolve if both SSAO and global fog are present + // FIXME: global fog is done in a different place now, should this change? TransitionMSAAToMain( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); RB_RenderSSAO(); @@ -3077,11 +3074,11 @@ const RenderCommand *StretchPicCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } if( !tess.indexes ) { - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( 4, 6 ); @@ -3163,7 +3160,7 @@ const RenderCommand *Poly2dCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( numverts, ( numverts - 2 ) * 3 ); @@ -3217,11 +3214,11 @@ const RenderCommand *Poly2dIndexedCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } if( !tess.verts ) { - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( numverts, numIndexes ); @@ -3313,11 +3310,11 @@ const RenderCommand *RotatedPicCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } if( !tess.indexes ) { - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( 4, 6 ); @@ -3402,11 +3399,11 @@ const RenderCommand *GradientPicCommand::ExecuteSelf( ) const } backEnd.currentEntity = &backEnd.entity2D; - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } if( !tess.indexes ) { - Tess_Begin( Tess_StageIteratorColor, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorColor, shader, false, -1 ); } Tess_CheckOverflow( 4, 6 ); @@ -3612,7 +3609,7 @@ const RenderCommand *PreparePortalCommand::ExecuteSelf( ) const GL_State( GLS_COLORMASK_BITS ); glState.glStateBitsMask = GLS_COLORMASK_BITS; - Tess_Begin( Tess_StageIteratorPortal, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorPortal, shader, false, -1 ); rb_surfaceTable[Util::ordinal(*(surface->surface))](surface->surface ); Tess_End(); @@ -3627,7 +3624,7 @@ const RenderCommand *PreparePortalCommand::ExecuteSelf( ) const GL_State( GLS_DEPTHMASK_TRUE | GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS); glState.glStateBitsMask = GLS_DEPTHMASK_TRUE | GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS; - Tess_Begin( Tess_StageIteratorPortal, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorPortal, shader, false, -1 ); rb_surfaceTable[Util::ordinal(*(surface->surface))](surface->surface ); Tess_End(); @@ -3678,7 +3675,7 @@ const RenderCommand *FinalisePortalCommand::ExecuteSelf( ) const glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); Tess_Begin( Tess_StageIteratorColor, shader, - false, surface->lightmapNum(), surface->fog, surface->bspSurface ); + false, surface->lightmapNum(), surface->bspSurface ); rb_surfaceTable[Util::ordinal( *( surface->surface ) )]( surface->surface ); Tess_End(); @@ -3687,7 +3684,7 @@ const RenderCommand *FinalisePortalCommand::ExecuteSelf( ) const GL_State( GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS); glState.glStateBitsMask = GLS_COLORMASK_BITS | GLS_DEPTHFUNC_ALWAYS; - Tess_Begin( Tess_StageIteratorPortal, shader, false, -1, 0 ); + Tess_Begin( Tess_StageIteratorPortal, shader, false, -1 ); rb_surfaceTable[Util::ordinal(*(surface->surface))](surface->surface ); Tess_End(); diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index d802c5aa63..81cf6b4302 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -952,7 +952,6 @@ static void ParseTriangleSurface( dsurface_t* ds, drawVert_t* verts, bspSurface_ surf->lightmapNum /= 2; } - surf->fogIndex = LittleLong( ds->fogNum ) + 1; surf->shader = ShaderForShaderNum( ds->shaderNum ); if ( r_singleShader->integer && !surf->shader->isSky ) { @@ -1185,9 +1184,6 @@ static void ParseMesh( dsurface_t *ds, drawVert_t *verts, bspSurface_t *surf ) surf->lightmapNum /= 2; } - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - // get shader value surf->shader = ShaderForShaderNum( ds->shaderNum ); @@ -2720,7 +2716,6 @@ static void R_CreateWorldVBO() { srf.surface = surface->data; srf.bspSurface = true; srf.lightMapNum = surface->lightmapNum; - srf.fog = surface->fogIndex; srf.portalNum = surface->portalNum; srf.firstIndex = ( ( srfGeneric_t* ) surface->data )->firstIndex; @@ -3235,9 +3230,6 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) s_worldData.fogs = (fog_t*) ri.Hunk_Alloc( s_worldData.numFogs * sizeof( *out ), ha_pref::h_low ); out = s_worldData.fogs + 1; - // ydnar: reset global fog - s_worldData.globalFog = -1; - if ( !count ) { Log::Debug("no fog volumes loaded" ); @@ -3327,24 +3319,16 @@ static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) // of trying to create an implicit shader from an image... out->shader = R_FindShader( fogs->shader, RSF_3D ); - // ydnar: global fog sets clearcolor/zfar - if ( out->originalBrushNumber == -1 ) - { - s_worldData.globalFog = i + 1; - } - // set the gradient vector sideNum = LittleLong( fogs->visibleSide ); // ydnar: made this check a little more strenuous (was sideNum == -1) if ( sideNum < 0 || sideNum >= sidesCount ) { - out->hasSurface = false; Vector4Set( out->surface, 0.0f, 0.0f, 0.0f, -1.0e10f ); } else { - out->hasSurface = true; planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum ); VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface ); out->surface[ 3 ] = -s_worldData.planes[ planeNum ].dist; diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index e8001901f1..5026bc7f62 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -1534,14 +1534,6 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p if ( r_lazyShaders.Get() == 1 ) { if ( tr.world->numFogs > 0 ) { - // Fog is applied dynamically based on the surface's BBox, so build all of the q3 fog shaders here - for ( uint32_t i = 0; i < 3; i++ ) { - gl_fogQuake3Shader->SetVertexSkinning( i & 1 ); - gl_fogQuake3Shader->SetVertexAnimation( i & 2 ); - gl_fogQuake3Shader->SetDeform( 0 ); - gl_fogQuake3Shader->MarkProgramForBuilding(); - } - for ( bool outer : { false, true } ) { gl_fogGlobalShader->SetOutsideFog( outer ); diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 06d124a60f..00ac0873b2 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -938,7 +938,6 @@ enum ST_PORTALMAP, ST_HEATHAZEMAP, // heatHaze post process effect ST_LIQUIDMAP, - ST_FOGMAP, ST_FOGMAP_INNER, // a fog seen from inside ST_FOGMAP_OUTER, // a fog seen from outside ST_LIGHTMAP, @@ -1110,13 +1109,6 @@ enum // reverse the cull operation # define ReverseCull(c) Util::enum_cast(2 - (c)) - enum class fogPass_t - { - FP_NONE, // surface is translucent and will just be adjusted properly - FP_EQUAL, // surface is opaque but possibly alpha tested - FP_LE // surface is translucent, but still needs a fog pass (fog surface) - }; - struct skyParms_t { float cloudHeight; @@ -1154,7 +1146,8 @@ enum bool entityMergable; // merge across entites optimizable (smoke, blood) fogParms_t fogParms; - fogPass_t fogPass; // draw a blended pass, possibly with depth test equals + // FIXME: does this ever do anything useful? Support for opaque surfaces was + // removed and translucent stuff is mostly drawn on top of fog anyway. bool noFog; bool disableReliefMapping; // disable relief mapping for this material even if it's available @@ -1203,7 +1196,6 @@ enum } altShader[ MAX_ALTSHADERS ]; // state-based remapping; note that index 0 is unused struct shader_t *depthShader; - struct shader_t *fogShader; struct shader_t *fogInnerShader; struct shader_t *fogOuterShader; struct shader_t *next; @@ -1480,7 +1472,6 @@ enum shader_t *shader; uint64_t sort; bool bspSurface; - int fog; int portalNum = -1; inline int index() const { @@ -1516,7 +1507,6 @@ enum surfaceType_t surfaceType; qhandle_t hShader; int16_t numVerts; - int16_t fogIndex; polyVert_t *verts; }; @@ -1633,7 +1623,6 @@ enum shader_t *shader; // has the fog parms // for clipping distance in fog when outside - bool hasSurface; float surface[ 4 ]; srfGeneric_t surf; @@ -1657,7 +1646,6 @@ enum struct shader_t *shader; int16_t lightmapNum; // -1 = no lightmap - int16_t fogIndex; int portalNum; bool renderable = false; @@ -1757,8 +1745,6 @@ enum int numFogs; fog_t *fogs; - int globalFog; // Arnout: index of global fog - vec3_t lightGridOrigin; vec3_t lightGridSize; vec3_t lightGridInverseSize; @@ -2506,8 +2492,6 @@ enum // internal shaders shader_t *defaultShader; - shader_t *fogEqualShader; - shader_t *fogLEShader; std::vector lightmaps; std::vector deluxemaps; @@ -2834,7 +2818,7 @@ inline bool checkGLErrors() void R_AddPolygonSurfaces(); - void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface = false, int portalNum = -1 ); + void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, bool bspSurface = false, int portalNum = -1 ); void R_LocalNormalToWorld( const vec3_t local, vec3_t world ); void R_LocalPointToWorld( const vec3_t local, vec3_t world ); @@ -2844,8 +2828,6 @@ inline bool checkGLErrors() cullResult_t R_CullLocalPointAndRadius( vec3_t origin, float radius ); cullResult_t R_CullPointAndRadius( vec3_t origin, float radius ); - int R_FogWorldBox( vec3_t bounds[ 2 ] ); - void R_SetupEntityWorldBounds( trRefEntity_t *ent ); void R_RotateEntityForViewParms( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t *orien ); @@ -3137,7 +3119,6 @@ void GLimp_LogComment_( std::string comment ); // some drawing parameters from drawSurf_t int16_t lightmapNum; - int16_t fogNum; bool bspSurface; // Signals that ATTR_QTANGENT will not be needed, so functions that generate vertexes @@ -3195,7 +3176,6 @@ void GLimp_LogComment_( std::string comment ); shader_t *surfaceShader, bool skipTangents, int lightmapNum, - int fogNum, bool bspSurface = false ); // *INDENT-ON* @@ -3249,7 +3229,6 @@ void GLimp_LogComment_( std::string comment ); void ProcessShaderReflection( const shaderStage_t* pStage ); void ProcessShaderHeatHaze( const shaderStage_t* ); void ProcessShaderLiquid( const shaderStage_t* pStage ); - void ProcessShaderFog( const shaderStage_t* ); void Render_NONE( shaderStage_t *pStage ); void Render_NOP( shaderStage_t *pStage ); @@ -3262,7 +3241,6 @@ void GLimp_LogComment_( std::string comment ); void Render_portal( shaderStage_t *pStage ); void Render_heatHaze( shaderStage_t *pStage ); void Render_liquid( shaderStage_t *pStage ); - void Render_fog( shaderStage_t* pStage ); void Render_fogGlobal( shaderStage_t *pStage ); /* diff --git a/src/engine/renderer/tr_main.cpp b/src/engine/renderer/tr_main.cpp index 25db5ae959..69d851549c 100644 --- a/src/engine/renderer/tr_main.cpp +++ b/src/engine/renderer/tr_main.cpp @@ -456,47 +456,6 @@ cullResult_t R_CullPointAndRadius( vec3_t pt, float radius ) return cullResult_t::CULL_IN; // completely inside frustum } -/* -================= -R_FogWorldBox -================= -*/ -int R_FogWorldBox( vec3_t bounds[ 2 ] ) -{ - int i, j; - fog_t *fog; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) - { - return 0; - } - - for ( i = 1; i < tr.world->numFogs; i++ ) - { - fog = &tr.world->fogs[ i ]; - - for ( j = 0; j < 3; j++ ) - { - if ( bounds[ 0 ][ j ] >= fog->bounds[ 1 ][ j ] ) - { - break; - } - - if ( bounds[ 1 ][ j ] <= fog->bounds[ 0 ][ j ] ) - { - break; - } - } - - if ( j == 3 ) - { - return i; - } - } - - return 0; -} - /* ================= R_LocalNormalToWorld @@ -1641,55 +1600,12 @@ bool R_MirrorViewBySurface(drawSurf_t *drawSurf) return true; } -/* -================= -R_SpriteFogNum - -See if a sprite is inside a fog volume -================= -*/ -int R_SpriteFogNum( trRefEntity_t *ent ) -{ - int i, j; - fog_t *fog; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) - { - return 0; - } - - for ( i = 1; i < tr.world->numFogs; i++ ) - { - fog = &tr.world->fogs[ i ]; - - for ( j = 0; j < 3; j++ ) - { - if ( ent->e.origin[ j ] - ent->e.radius >= fog->bounds[ 1 ][ j ] ) - { - break; - } - - if ( ent->e.origin[ j ] + ent->e.radius <= fog->bounds[ 0 ][ j ] ) - { - break; - } - } - - if ( j == 3 ) - { - return i; - } - } - - return 0; -} - /* ================= R_AddDrawSurf ================= */ -void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, int fogNum, bool bspSurface, int portalNum ) +void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, bool bspSurface, int portalNum ) { // instead of checking for overflow, we just mask the index // so it wraps around @@ -1702,9 +1618,6 @@ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, i drawSurf->surface = surface; drawSurf->shader = shader; drawSurf->bspSurface = bspSurface; - /* Allow the renderer backend to merge main surfaces that have fog, ignoring the fogNum, - as it only matters for the emitted fog surfaces */ - drawSurf->fog = ( shader == tr.fogEqualShader || shader == tr.fogLEShader ) ? fogNum : 0; drawSurf->portalNum = portalNum; int entityNum; @@ -1728,15 +1641,7 @@ void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int lightmapNum, i tr.refdef.numDrawSurfs++; if ( shader->depthShader != nullptr ) { - R_AddDrawSurf( surface, shader->depthShader, 0, 0, bspSurface ); - } - - bool usingMaterial = glConfig.usingMaterialSystem && !r_materialSystemSkip.Get(); - - // don't fog surfaces that already were covered by globalFog - if ( usingMaterial && !shader->noFog && fogNum >= 1 && fogNum != tr.world->globalFog ) - { - R_AddDrawSurf( surface, shader->fogShader, 0, fogNum, bspSurface ); + R_AddDrawSurf( surface, shader->depthShader, 0, bspSurface ); } } @@ -1882,7 +1787,7 @@ void R_AddEntitySurfaces() } shader = R_GetShaderByHandle( ent->e.customShader ); - R_AddDrawSurf( &entitySurface, shader, -1, R_SpriteFogNum( ent ) ); + R_AddDrawSurf( &entitySurface, shader, -1 ); break; case refEntityType_t::RT_MODEL: @@ -1893,7 +1798,7 @@ void R_AddEntitySurfaces() if ( !tr.currentModel ) { - R_AddDrawSurf( &entitySurface, tr.defaultShader, -1, 0 ); + R_AddDrawSurf( &entitySurface, tr.defaultShader, -1 ); } else { @@ -1927,7 +1832,7 @@ void R_AddEntitySurfaces() VectorClear( ent->worldBounds[ 0 ] ); VectorClear( ent->worldBounds[ 1 ] ); shader = R_GetShaderByHandle( ent->e.customShader ); - R_AddDrawSurf( &entitySurface, tr.defaultShader, -1, 0 ); + R_AddDrawSurf( &entitySurface, tr.defaultShader, -1 ); break; default: diff --git a/src/engine/renderer/tr_mesh.cpp b/src/engine/renderer/tr_mesh.cpp index be367a213b..e4e5975f91 100644 --- a/src/engine/renderer/tr_mesh.cpp +++ b/src/engine/renderer/tr_mesh.cpp @@ -250,7 +250,6 @@ void R_AddMDVSurfaces( trRefEntity_t *ent ) mdvSurface_t *mdvSurface = nullptr; int lod; bool personalModel; - int fogNum; // don't add third_person objects if not in a portal personalModel = ( ent->e.renderfx & RF_THIRD_PERSON ) && @@ -294,9 +293,6 @@ void R_AddMDVSurfaces( trRefEntity_t *ent ) return; } - // see if we are in a fog volume - fogNum = R_FogWorldBox( ent->worldBounds ); - // draw all surfaces if ( r_vboModels.Get() && model->numVBOSurfaces ) { @@ -312,7 +308,7 @@ void R_AddMDVSurfaces( trRefEntity_t *ent ) // don't add third_person objects if not viewing through a portal if ( !personalModel ) { - R_AddDrawSurf( ( surfaceType_t * ) vboSurface, shader, -1, fogNum ); + R_AddDrawSurf( ( surfaceType_t * ) vboSurface, shader, -1 ); } } } @@ -328,7 +324,7 @@ void R_AddMDVSurfaces( trRefEntity_t *ent ) // don't add third_person objects if not viewing through a portal if ( !personalModel ) { - R_AddDrawSurf( ( surfaceType_t * ) mdvSurface, shader, -1, fogNum ); + R_AddDrawSurf( ( surfaceType_t * ) mdvSurface, shader, -1 ); } } } diff --git a/src/engine/renderer/tr_model_iqm.cpp b/src/engine/renderer/tr_model_iqm.cpp index 938b3d5089..eb7feb11b2 100644 --- a/src/engine/renderer/tr_model_iqm.cpp +++ b/src/engine/renderer/tr_model_iqm.cpp @@ -990,7 +990,6 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { srfIQModel_t *surface; int i, j; bool personalModel; - int fogNum; shader_t *shader; skin_t *skin; @@ -1008,9 +1007,6 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { return; } - // see if we are in a fog volume - fogNum = R_FogWorldBox( ent->worldBounds ); - for ( i = 0 ; i < IQModel->num_surfaces ; i++ ) { if(ent->e.customShader) shader = R_GetShaderByHandle( ent->e.customShader ); @@ -1048,7 +1044,7 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { // we will add shadows even if the main object isn't visible in the view if( !personalModel ) { - R_AddDrawSurf( ( surfaceType_t *)surface, shader, -1, fogNum ); + R_AddDrawSurf( ( surfaceType_t *)surface, shader, -1 ); } surface++; diff --git a/src/engine/renderer/tr_scene.cpp b/src/engine/renderer/tr_scene.cpp index 8fb7ffdb1d..f8841587aa 100644 --- a/src/engine/renderer/tr_scene.cpp +++ b/src/engine/renderer/tr_scene.cpp @@ -126,7 +126,7 @@ void R_AddPolygonSurfaces() for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys; i++, poly++ ) { sh = R_GetShaderByHandle( poly->hShader ); - R_AddDrawSurf( ( surfaceType_t * ) poly, sh, -1, poly->fogIndex ); + R_AddDrawSurf( ( surfaceType_t * ) poly, sh, -1 ); } } @@ -138,10 +138,7 @@ R_AddPolysToScene static void R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { srfPoly_t *poly; - int i, j; - int fogIndex; - fog_t *fog; - vec3_t bounds[ 2 ]; + int j; if ( !tr.registered ) { @@ -184,45 +181,6 @@ static void R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t // done. r_numPolys++; r_numPolyVerts += numVerts; - - // if no world is loaded - if ( tr.world == nullptr ) - { - fogIndex = 0; - } - // see if it is in a fog volume - else if ( tr.world->numFogs == 1 ) - { - fogIndex = 0; - } - else - { - // find which fog volume the poly is in - VectorCopy( poly->verts[ 0 ].xyz, bounds[ 0 ] ); - VectorCopy( poly->verts[ 0 ].xyz, bounds[ 1 ] ); - - for ( i = 1; i < poly->numVerts; i++ ) - { - AddPointToBounds( poly->verts[ i ].xyz, bounds[ 0 ], bounds[ 1 ] ); - } - - for ( fogIndex = 1; fogIndex < tr.world->numFogs; fogIndex++ ) - { - fog = &tr.world->fogs[ fogIndex ]; - - if ( BoundsIntersect( bounds[ 0 ], bounds[ 1 ], fog->bounds[ 0 ], fog->bounds[ 1 ] ) ) - { - break; - } - } - - if ( fogIndex == tr.world->numFogs ) - { - fogIndex = 0; - } - } - - poly->fogIndex = fogIndex; } } @@ -292,25 +250,14 @@ static bool R_InsideFog( int fognum ) void R_AddFogBrushSurfaces() { - if ( glConfig.usingMaterialSystem && !r_materialSystemSkip.Get() ) - { - if ( tr.world->globalFog > 0 ) - { - const fog_t &fog = tr.world->fogs[ tr.world->globalFog ]; - R_AddDrawSurf( ( surfaceType_t *)&fog.surf, fog.shader->fogInnerShader, -1, 0 ); - } - } - else - { - // TODO: incorporate them in the BSP? - for ( int i = 1; i < tr.world->numFogs; i++ ) - { - const fog_t &fog = tr.world->fogs[ i ]; - shader_t *shader = R_InsideFog( i ) - ? fog.shader->fogInnerShader - : fog.shader->fogOuterShader; - R_AddDrawSurf( ( surfaceType_t *)&fog.surf, shader, -1, 0 ); - } + // TODO: incorporate them in the BSP? + for ( int i = 1; i < tr.world->numFogs; i++ ) + { + const fog_t &fog = tr.world->fogs[ i ]; + shader_t *shader = R_InsideFog( i ) + ? fog.shader->fogInnerShader + : fog.shader->fogOuterShader; + R_AddDrawSurf( ( surfaceType_t *)&fog.surf, shader, -1 ); } } diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 123daec916..b2724c4ef7 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -299,18 +299,7 @@ static void GLSL_InitGPUShadersOrError() } // Fog GLSL is always loaded and built because disabling fog is cheat. - { - // Q3A volumetric fog - gl_shaderManager.LoadShader( gl_fogQuake3Shader ); - - if ( glConfig.usingMaterialSystem ) - { - gl_shaderManager.LoadShader( gl_fogQuake3ShaderMaterial ); - } - - // global fog post process effect - gl_shaderManager.LoadShader( gl_fogGlobalShader ); - } + gl_shaderManager.LoadShader( gl_fogGlobalShader ); if ( r_heatHaze->integer ) { @@ -489,8 +478,6 @@ void GLSL_ShutdownGPUShaders() gl_reflectionShaderMaterial = nullptr; gl_skyboxShader = nullptr; gl_skyboxShaderMaterial = nullptr; - gl_fogQuake3Shader = nullptr; - gl_fogQuake3ShaderMaterial = nullptr; gl_fogGlobalShader = nullptr; gl_heatHazeShader = nullptr; gl_heatHazeShaderMaterial = nullptr; @@ -715,7 +702,6 @@ void Tess_Begin( void ( *stageIteratorFunc )(), shader_t *surfaceShader, bool skipTangents, int lightmapNum, - int fogNum, bool bspSurface ) { if ( tess.numIndexes || tess.numVertexes || tess.multiDrawPrimitives ) @@ -734,7 +720,6 @@ void Tess_Begin( void ( *stageIteratorFunc )(), tess.skipTangents = skipTangents; tess.lightmapNum = lightmapNum; - tess.fogNum = fogNum; tess.bspSurface = bspSurface; // materials are optional (some debug drawing code doesn't use them) @@ -767,9 +752,9 @@ void Tess_Begin( void ( *stageIteratorFunc )(), } GLIMP_LOGCOMMENT( "--- Tess_Begin( surfaceShader = %s, " - "skipTangents = %i, lightmapNum = %i, fogNum = %i) ---", + "skipTangents = %i, lightmapNum = %i) ---", tess.surfaceShader ? tess.surfaceShader->name : "NULL", - tess.skipTangents, tess.lightmapNum, tess.fogNum ); + tess.skipTangents, tess.lightmapNum ); } void SetNormalScale( const shaderStage_t *pStage, vec3_t normalScale ) @@ -870,12 +855,6 @@ void ProcessShaderLiquid( const shaderStage_t* pStage ) { gl_liquidShader->SetGridLighting( lightMode == lightMode_t::GRID ); } -void ProcessShaderFog( const shaderStage_t* pStage ) { - gl_fogQuake3Shader->SetVertexSkinning( glConfig.vboVertexSkinningAvailable && tess.vboVertexSkinning ); - gl_fogQuake3Shader->SetVertexAnimation( tess.vboVertexAnimation ); - gl_fogQuake3Shader->SetDeform( pStage->deformIndex ); -} - void Render_NONE( shaderStage_t * ) { ASSERT_UNREACHABLE(); @@ -1611,71 +1590,6 @@ void Render_liquid( shaderStage_t *pStage ) GL_CheckErrors(); } -void Render_fog( shaderStage_t* pStage ) -{ - if ( r_noFog->integer || ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) - { - return; - } - - const fog_t* fog = tr.world->fogs + tess.fogNum; - - GLIMP_LOGCOMMENT( "--- Render_fog( fogNum = %i, originalBrushNumber = %i ) ---", - tess.fogNum, fog->originalBrushNumber ); - - // rotate the gradient vector for this orientation - float eyeT; - vec4_t fogDepthVector; - if ( fog->hasSurface ) - { - VectorCopy( fog->surface, fogDepthVector ); - fogDepthVector[ 3 ] = -fog->surface[ 3 ]; - eyeT = DotProduct( backEnd.viewParms.orientation.origin, fogDepthVector ) + fogDepthVector[ 3 ]; - } - else - { - Vector4Set( fogDepthVector, 0, 0, 0, 1 ); - eyeT = 1; // non-surface fog always has eye inside - } - - GL_State( pStage->stateBits ); - - ProcessShaderFog( pStage ); - gl_fogQuake3Shader->BindProgram(); - - gl_fogQuake3Shader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - gl_fogQuake3Shader->SetUniform_FogGradient( - 1.0f / fog->shader->fogParms.depthForOpaque, fog->shader->fogParms.falloffExp ); - gl_fogQuake3Shader->SetUniform_FogDepthVector( fogDepthVector ); - gl_fogQuake3Shader->SetUniform_FogEyeT( eyeT ); - - // u_Color - SetUniform_ColorGlobal( gl_fogQuake3Shader, fog->shader->fogParms.color ); - - gl_fogQuake3Shader->SetUniform_ModelMatrix( backEnd.orientation.transformMatrix ); - gl_fogQuake3Shader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); - - // u_Bones - if ( glConfig.vboVertexSkinningAvailable && tess.vboVertexSkinning ) - { - gl_fogQuake3Shader->SetUniform_Bones( tess.numBones, tess.bones ); - } - - // u_VertexInterpolation - if ( tess.vboVertexAnimation ) - { - gl_fogQuake3Shader->SetUniform_VertexInterpolation( glState.vertexAttribsInterpolation ); - } - - gl_fogQuake3Shader->SetUniform_Time( backEnd.refdef.floatTime - backEnd.currentEntity->e.shaderTime ); - - gl_fogQuake3Shader->SetRequiredVertexPointers(); - - Tess_DrawElements(); - - GL_CheckErrors(); -} - /* =============== Tess_ComputeColor diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index b0949c45e0..a4d18270fa 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -5650,13 +5650,6 @@ static void SetStagesRenderers() &UpdateSurfaceDataLiquid, &BindShaderLiquid, &ProcessMaterialLiquid, }; break; - case stageType_t::ST_FOGMAP: - stageRendererOptions = { - // All q3 fog shaders are built in RE_EndRegistration because they're assigned to surfaces dynamically - &Render_fog, &MarkShaderBuildNOP, - &UpdateSurfaceDataFog, &BindShaderFog, &ProcessMaterialFog, - }; - break; case stageType_t::ST_FOGMAP_INNER: case stageType_t::ST_FOGMAP_OUTER: stageRendererOptions = { @@ -5846,7 +5839,6 @@ static void ValidateStage( shaderStage_t *pStage ) { stageType_t::ST_SPECULARMAP, { true, true, false, "specularMap" } }, { stageType_t::ST_HEATHAZEMAP, { true, true, false, "heatHazeMap" } }, { stageType_t::ST_LIQUIDMAP, { true, true, false, "liquidMap" } }, - { stageType_t::ST_FOGMAP, { true, false, false, "fogMap" } }, { stageType_t::ST_FOGMAP_INNER, { true, false, false, "fogMapInner" } }, { stageType_t::ST_FOGMAP_OUTER, { true, false, false, "fogMapOuter" } }, // The lightmap is fetched at render time. @@ -5962,7 +5954,7 @@ static float DetermineShaderSort() return Util::ordinal(shaderSort_t::SS_BLEND0); } - // determine sort order and fog color adjustment + // determine sort order if ( ( stages[ stage ].stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) && ( stages[ 0 ].stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) ) { @@ -6035,14 +6027,6 @@ static shader_t *FinishShader() shader.sort = DetermineShaderSort(); - if ( shader.sort <= Util::ordinal( shaderSort_t::SS_OPAQUE ) ) { - shader.fogPass = fogPass_t::FP_EQUAL; - } else if ( shader.contentFlags & CONTENTS_FOG ) { - shader.fogPass = fogPass_t::FP_LE; - } - - shader.noFog = shader.noFog || shader.fogPass == fogPass_t::FP_NONE; - // look for multitexture potential CollapseStages(); @@ -6055,14 +6039,6 @@ static shader_t *FinishShader() // Copy the current global shader to a newly allocated shader. shader_t *ret = MakeShaderPermanent(); - if ( !shader.noFog ) { - if ( shader.fogPass == fogPass_t::FP_EQUAL ) { - ret->fogShader = tr.fogEqualShader; - } else { - ret->fogShader = tr.fogLEShader; - } - } - size_t nameLength = strlen( shader.name ); // generate depth-only shader if necessary @@ -6077,7 +6053,6 @@ static shader_t *FinishShader() stages[1].active = false; numStages = 1; shader.noFog = true; - shader.fogShader = nullptr; Q_strcat( shader.name, sizeof( shader.name ), "$depth" ); @@ -6153,8 +6128,7 @@ static shader_t *FinishShader() uint8_t maxStages = ret->lastStage - ret->stages; // Add 1 for potential depth stages - // Add 1 for potential fog stages - maxStages = PAD( maxStages + 2, 4 ); // Aligned to 4 components + maxStages = PAD( maxStages + 1, 4 ); // Aligned to 4 components materialSystem.maxStages = std::max( maxStages, materialSystem.maxStages ); } @@ -6588,7 +6562,6 @@ class ListShadersCmd : public Cmd::StaticCmd { stageType_t::ST_PORTALMAP, "PORTALMAP" }, { stageType_t::ST_HEATHAZEMAP, "HEATHAZEMAP" }, { stageType_t::ST_LIQUIDMAP, "LIQUIDMAP" }, - { stageType_t::ST_FOGMAP, "FOGMAP" }, { stageType_t::ST_LIGHTMAP, "LIGHTMAP" }, { stageType_t::ST_STYLELIGHTMAP, "STYLELIGHTMAP" }, { stageType_t::ST_STYLECOLORMAP, "STYLECOLORMAP" }, @@ -7010,34 +6983,11 @@ static void CreateInternalShaders() Q_strncpyz( shader.name, "", sizeof( shader.name ) ); shader.noFog = true; - shader.fogShader = nullptr; stages[ 0 ].type = stageType_t::ST_DIFFUSEMAP; stages[ 0 ].bundle[ 0 ].image[ 0 ] = tr.defaultImage; stages[ 0 ].active = true; stages[ 0 ].stateBits = GLS_DEFAULT; tr.defaultShader = FinishShader(); - - Q_strncpyz( shader.name, "", sizeof( shader.name ) ); - - shader.sort = Util::ordinal( shaderSort_t::SS_FOG ); - stages[0].type = stageType_t::ST_FOGMAP; - for ( int i = 0; i < 5; i++ ) { - stages[0].bundle[i].image[0] = nullptr; - } - stages[0].active = true; - stages[0].stateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL; - tr.fogEqualShader = FinishShader(); - - Q_strncpyz( shader.name, "", sizeof( shader.name ) ); - - shader.sort = Util::ordinal( shaderSort_t::SS_FOG ); - stages[0].type = stageType_t::ST_FOGMAP; - for ( int i = 0; i < 5; i++ ) { - stages[0].bundle[i].image[0] = nullptr; - } - stages[0].active = true; - stages[0].stateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - tr.fogLEShader = FinishShader(); } /* diff --git a/src/engine/renderer/tr_surface.cpp b/src/engine/renderer/tr_surface.cpp index d12279d07f..12981a768e 100644 --- a/src/engine/renderer/tr_surface.cpp +++ b/src/engine/renderer/tr_surface.cpp @@ -53,7 +53,7 @@ void Tess_EndBegin() { Tess_End(); Tess_Begin( tess.stageIteratorFunc, tess.surfaceShader, tess.skipTangents, - tess.lightmapNum, tess.fogNum, tess.bspSurface ); + tess.lightmapNum, tess.bspSurface ); } /* @@ -114,7 +114,7 @@ void Tess_CheckOverflow( int verts, int indexes ) } Tess_Begin( tess.stageIteratorFunc, tess.surfaceShader, tess.skipTangents, - tess.lightmapNum, tess.fogNum, tess.bspSurface ); + tess.lightmapNum, tess.bspSurface ); } /* @@ -534,14 +534,14 @@ void Tess_InstantScreenSpaceQuad() { if ( glConfig.gpuShader4Available ) { tr.skipVBO = true; - Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1 ); rb_surfaceTable[Util::ordinal( *( tr.genericTriangle->surface ) )]( tr.genericTriangle->surface ); Tess_DrawElements(); tr.skipVBO = false; } else { - Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1 ); rb_surfaceTable[Util::ordinal( *( tr.genericQuad->surface ) )]( tr.genericQuad->surface ); GL_VertexAttribsState( ATTR_POSITION ); Tess_DrawElements(); @@ -556,7 +556,7 @@ void Tess_InstantQuad( u_ModelViewProjectionMatrix &shader, const float x, const { GLIMP_LOGCOMMENT( "--- Tess_InstantQuad ---" ); - Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1, 0 ); + Tess_Begin( Tess_StageIteratorDummy, nullptr, true, -1 ); /* We don't use x, y, width, height directly to make it compatible with R_InitGenericVBOs() in tr_vbo.cpp. diff --git a/src/engine/renderer/tr_world.cpp b/src/engine/renderer/tr_world.cpp index a8bac11e42..b02cb35d49 100644 --- a/src/engine/renderer/tr_world.cpp +++ b/src/engine/renderer/tr_world.cpp @@ -130,7 +130,7 @@ static bool R_CullSurface( surfaceType_t *surface, shader_t *shader, int planeBi R_AddWorldSurface ====================== */ -static bool R_AddWorldSurface( bspSurface_t *surf, int fogIndex, int portalNum, int planeBits ) +static bool R_AddWorldSurface( bspSurface_t *surf, int portalNum, int planeBits ) { if ( surf->viewCount == tr.viewCountNoReset ) { @@ -145,7 +145,7 @@ static bool R_AddWorldSurface( bspSurface_t *surf, int fogIndex, int portalNum, return true; } - R_AddDrawSurf( surf->data, surf->shader, surf->lightmapNum, fogIndex, true, portalNum ); + R_AddDrawSurf( surf->data, surf->shader, surf->lightmapNum, true, portalNum ); return true; } @@ -168,7 +168,6 @@ void R_AddBSPModelSurfaces( trRefEntity_t *ent ) model_t *pModel; unsigned int i; vec3_t boundsCenter; - int fogNum; pModel = R_GetModelByHandle( ent->e.hModel ); bspModel = pModel->bsp; @@ -190,11 +189,9 @@ void R_AddBSPModelSurfaces( trRefEntity_t *ent ) return; } - fogNum = R_FogWorldBox( ent->worldBounds ); - for ( i = 0; i < bspModel->numSurfaces; i++ ) { - R_AddWorldSurface( bspModel->firstSurface + i, fogNum, -1, FRUSTUM_CLIPALL ); + R_AddWorldSurface( bspModel->firstSurface + i, -1, FRUSTUM_CLIPALL ); } } @@ -254,7 +251,7 @@ static void R_AddLeafSurfaces( bspNode_t *node, int planeBits ) { // the surface may have already been added if it // spans multiple leafs - R_AddWorldSurface( *view, ( *view )->fogIndex, ( *mark )->portalNum, planeBits); + R_AddWorldSurface( *view, ( *mark )->portalNum, planeBits); ( *mark )->viewCount = tr.viewCountNoReset; From d6ef9b5716740be6c4e10dc04c20f03dba4aa92b Mon Sep 17 00:00:00 2001 From: slipher Date: Sat, 7 Feb 2026 06:52:07 -0600 Subject: [PATCH 11/12] Cleanup: rename GLSL shader fogGlobal -> fog Also move the Render_fog function with its friends. --- src.cmake | 4 +- src/engine/renderer/gl_shader.cpp | 11 ++- src/engine/renderer/gl_shader.h | 6 +- .../{fogGlobal_fp.glsl => fog_fp.glsl} | 2 +- .../{fogGlobal_vp.glsl => fog_vp.glsl} | 0 src/engine/renderer/tr_backend.cpp | 66 ------------------ src/engine/renderer/tr_init.cpp | 4 +- src/engine/renderer/tr_local.h | 2 +- src/engine/renderer/tr_shade.cpp | 67 ++++++++++++++++++- src/engine/renderer/tr_shader.cpp | 2 +- 10 files changed, 80 insertions(+), 84 deletions(-) rename src/engine/renderer/glsl_source/{fogGlobal_fp.glsl => fog_fp.glsl} (99%) rename src/engine/renderer/glsl_source/{fogGlobal_vp.glsl => fog_vp.glsl} (100%) diff --git a/src.cmake b/src.cmake index 3644a0740b..6c43cc7363 100644 --- a/src.cmake +++ b/src.cmake @@ -129,8 +129,8 @@ set(GLSL_EMBED_LIST vertexSkinning_vp.glsl # Regular shaders - fogGlobal_vp.glsl - fogGlobal_fp.glsl + fog_vp.glsl + fog_fp.glsl generic_vp.glsl generic_fp.glsl heatHaze_vp.glsl diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 88161094aa..1ab26b620f 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -55,7 +55,7 @@ GLShader_processSurfaces *gl_processSurfacesShader = nullptr; GLShader_blur *gl_blurShader = nullptr; GLShader_cameraEffects *gl_cameraEffectsShader = nullptr; GLShader_contrast *gl_contrastShader = nullptr; -GLShader_fogGlobal *gl_fogGlobalShader = nullptr; +GLShader_fog *gl_fogShader = nullptr; GLShader_fxaa *gl_fxaaShader = nullptr; GLShader_motionblur *gl_motionblurShader = nullptr; GLShader_ssao *gl_ssaoShader = nullptr; @@ -2688,10 +2688,9 @@ GLShader_skyboxMaterial::GLShader_skyboxMaterial() : u_ModelViewProjectionMatrix( this ) {} -// TODO: rename -GLShader_fogGlobal::GLShader_fogGlobal() : - GLShader( "fogGlobal", ATTR_POSITION | ATTR_FOG_SURFACE, - false, "fogGlobal", "fogGlobal" ), +GLShader_fog::GLShader_fog() : + GLShader( "fog", ATTR_POSITION | ATTR_FOG_SURFACE, + false, "fog", "fog" ), u_DepthMap( this ), u_ModelViewProjectionMatrix( this ), u_UnprojectMatrix( this ), @@ -2703,7 +2702,7 @@ GLShader_fogGlobal::GLShader_fogGlobal() : { } -void GLShader_fogGlobal::SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) +void GLShader_fog::SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) { glUniform1i( glGetUniformLocation( shaderProgram->id, "u_DepthMap" ), 1 ); } diff --git a/src/engine/renderer/gl_shader.h b/src/engine/renderer/gl_shader.h index 244f526ec6..cef9c7df2c 100644 --- a/src/engine/renderer/gl_shader.h +++ b/src/engine/renderer/gl_shader.h @@ -3178,7 +3178,7 @@ class GLShader_skyboxMaterial : GLShader_skyboxMaterial(); }; -class GLShader_fogGlobal : +class GLShader_fog : public GLShader, public u_DepthMap, public u_ModelViewProjectionMatrix, @@ -3190,7 +3190,7 @@ class GLShader_fogGlobal : public GLCompileMacro_OUTSIDE_FOG { public: - GLShader_fogGlobal(); + GLShader_fog(); void SetShaderProgramUniforms( ShaderProgramDescriptor *shaderProgram ) override; }; @@ -3528,7 +3528,7 @@ extern GLShader_processSurfaces *gl_processSurfacesShader; extern GLShader_blur *gl_blurShader; extern GLShader_cameraEffects *gl_cameraEffectsShader; extern GLShader_contrast *gl_contrastShader; -extern GLShader_fogGlobal *gl_fogGlobalShader; +extern GLShader_fog *gl_fogShader; extern GLShader_fxaa *gl_fxaaShader; extern GLShader_motionblur *gl_motionblurShader; extern GLShader_ssao *gl_ssaoShader; diff --git a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl b/src/engine/renderer/glsl_source/fog_fp.glsl similarity index 99% rename from src/engine/renderer/glsl_source/fogGlobal_fp.glsl rename to src/engine/renderer/glsl_source/fog_fp.glsl index fbaef1c897..b64e765860 100644 --- a/src/engine/renderer/glsl_source/fogGlobal_fp.glsl +++ b/src/engine/renderer/glsl_source/fog_fp.glsl @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ -/* fogGlobal_fp.glsl */ +/* fog_fp.glsl */ #insert common #insert fogEquation_fp diff --git a/src/engine/renderer/glsl_source/fogGlobal_vp.glsl b/src/engine/renderer/glsl_source/fog_vp.glsl similarity index 100% rename from src/engine/renderer/glsl_source/fogGlobal_vp.glsl rename to src/engine/renderer/glsl_source/fog_vp.glsl diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index e1149d80ed..5e7319d852 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -1419,72 +1419,6 @@ void RB_RenderPostDepthLightTile() GL_CheckErrors(); } -// TODO: move with other Render_ functions -void Render_fogGlobal( shaderStage_t *stage ) -{ - if ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) - { - return; - } - - if ( r_noFog->integer ) - { - return; - } - - GLIMP_LOGCOMMENT( "--- RB_RenderGlobalFog ---" ); - - RB_PrepareForSamplingDepthMap(); - - GL_Cull( stage->shader->cullType ); - - gl_fogGlobalShader->SetOutsideFog( stage->type == stageType_t::ST_FOGMAP_OUTER ); - - gl_fogGlobalShader->BindProgram(); - - { - GL_State( stage->stateBits ); - - gl_fogGlobalShader->SetUniform_FogGradient( - 1.0f / stage->shader->fogParms.depthForOpaque, stage->shader->fogParms.falloffExp ); - gl_fogGlobalShader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); - SetUniform_Color( gl_fogGlobalShader, stage->shader->fogParms.color ); - } - - switch ( stage->type ) - { - case stageType_t::ST_FOGMAP_INNER: - { - // It's important to avoid far plane clipping - matrix_t projection, mvp; - MatrixPerspectiveProjectionFovXYInfiniteRH( projection, tr.refdef.fov_x, tr.refdef.fov_y, 1.0f ); - MatrixMultiply( projection, glState.modelViewMatrix[ glState.stackIndex ], mvp ); - gl_fogGlobalShader->SetUniform_ModelViewProjectionMatrix( mvp ); - break; - } - case stageType_t::ST_FOGMAP_OUTER: - { - gl_fogGlobalShader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); - break; - } - default: - ASSERT_UNREACHABLE(); - } - - gl_fogGlobalShader->SetUniform_UnprojectMatrix( backEnd.viewParms.unprojectionMatrix ); - - // bind u_DepthMap - gl_fogGlobalShader->SetUniform_DepthMapBindless( - GL_BindToTMU( 1, tr.depthSamplerImage ) - ); - - gl_fogGlobalShader->SetRequiredVertexPointers(); - - Tess_DrawElements(); - - GL_CheckErrors(); -} - void RB_RenderBloom() { if ( ( backEnd.refdef.rdflags & ( RDF_NOWORLDMODEL | RDF_NOBLOOM ) ) diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 5026bc7f62..13b65030a5 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -1536,8 +1536,8 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p { for ( bool outer : { false, true } ) { - gl_fogGlobalShader->SetOutsideFog( outer ); - gl_fogGlobalShader->MarkProgramForBuilding(); + gl_fogShader->SetOutsideFog( outer ); + gl_fogShader->MarkProgramForBuilding(); } } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 00ac0873b2..1222eb9e65 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -3241,7 +3241,7 @@ void GLimp_LogComment_( std::string comment ); void Render_portal( shaderStage_t *pStage ); void Render_heatHaze( shaderStage_t *pStage ); void Render_liquid( shaderStage_t *pStage ); - void Render_fogGlobal( shaderStage_t *pStage ); + void Render_fog( shaderStage_t *pStage ); /* ============================================================ diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index b2724c4ef7..cff047758d 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -299,7 +299,7 @@ static void GLSL_InitGPUShadersOrError() } // Fog GLSL is always loaded and built because disabling fog is cheat. - gl_shaderManager.LoadShader( gl_fogGlobalShader ); + gl_shaderManager.LoadShader( gl_fogShader ); if ( r_heatHaze->integer ) { @@ -478,7 +478,7 @@ void GLSL_ShutdownGPUShaders() gl_reflectionShaderMaterial = nullptr; gl_skyboxShader = nullptr; gl_skyboxShaderMaterial = nullptr; - gl_fogGlobalShader = nullptr; + gl_fogShader = nullptr; gl_heatHazeShader = nullptr; gl_heatHazeShaderMaterial = nullptr; gl_screenShader = nullptr; @@ -1590,6 +1590,69 @@ void Render_liquid( shaderStage_t *pStage ) GL_CheckErrors(); } +void Render_fog( shaderStage_t *stage ) +{ + if ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) + { + return; + } + + if ( r_noFog->integer ) + { + return; + } + + GLIMP_LOGCOMMENT( "--- Render_fog ---" ); + + RB_PrepareForSamplingDepthMap(); + + GL_Cull( stage->shader->cullType ); + + gl_fogShader->SetOutsideFog( stage->type == stageType_t::ST_FOGMAP_OUTER ); + + gl_fogShader->BindProgram(); + + GL_State( stage->stateBits ); + + gl_fogShader->SetUniform_FogGradient( + 1.0f / stage->shader->fogParms.depthForOpaque, stage->shader->fogParms.falloffExp ); + gl_fogShader->SetUniform_ViewOrigin( backEnd.viewParms.orientation.origin ); + SetUniform_Color( gl_fogShader, stage->shader->fogParms.color ); + + switch ( stage->type ) + { + case stageType_t::ST_FOGMAP_INNER: + { + // It's important to avoid far plane clipping + matrix_t projection, mvp; + MatrixPerspectiveProjectionFovXYInfiniteRH( projection, tr.refdef.fov_x, tr.refdef.fov_y, 1.0f ); + MatrixMultiply( projection, glState.modelViewMatrix[ glState.stackIndex ], mvp ); + gl_fogShader->SetUniform_ModelViewProjectionMatrix( mvp ); + break; + } + case stageType_t::ST_FOGMAP_OUTER: + { + gl_fogShader->SetUniform_ModelViewProjectionMatrix( glState.modelViewProjectionMatrix[ glState.stackIndex ] ); + break; + } + default: + ASSERT_UNREACHABLE(); + } + + gl_fogShader->SetUniform_UnprojectMatrix( backEnd.viewParms.unprojectionMatrix ); + + // bind u_DepthMap + gl_fogShader->SetUniform_DepthMapBindless( + GL_BindToTMU( 1, tr.depthSamplerImage ) + ); + + gl_fogShader->SetRequiredVertexPointers(); + + Tess_DrawElements(); + + GL_CheckErrors(); +} + /* =============== Tess_ComputeColor diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index a4d18270fa..5d54851263 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -5653,7 +5653,7 @@ static void SetStagesRenderers() case stageType_t::ST_FOGMAP_INNER: case stageType_t::ST_FOGMAP_OUTER: stageRendererOptions = { - &Render_fogGlobal, &MarkShaderBuildNOP, + &Render_fog, &MarkShaderBuildNOP, &UpdateSurfaceDataNOP, &BindShaderNOP, &ProcessMaterialNOP, }; break; From cc477b4ce468aa09c41c3a46483e4dd99b30dd26 Mon Sep 17 00:00:00 2001 From: slipher Date: Sat, 7 Feb 2026 10:35:04 -0600 Subject: [PATCH 12/12] Frustum cull fogs Use CPU-side frustum culling to avoid drawing fogs when possible. Also don't recalculate the near plane distance (for checking if fogs are too close to the camera) every iteration when looping fogs. --- src/engine/renderer/tr_local.h | 2 +- src/engine/renderer/tr_main.cpp | 13 +++++++++---- src/engine/renderer/tr_scene.cpp | 14 ++++++++++---- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 1222eb9e65..e736d33d1b 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -2823,7 +2823,7 @@ inline bool checkGLErrors() void R_LocalNormalToWorld( const vec3_t local, vec3_t world ); void R_LocalPointToWorld( const vec3_t local, vec3_t world ); - cullResult_t R_CullBox( vec3_t worldBounds[ 2 ] ); + cullResult_t R_CullBox( const vec3_t worldBounds[ 2 ], int lastPlane = FRUSTUM_NEAR ); cullResult_t R_CullLocalBox( vec3_t bounds[ 2 ] ); cullResult_t R_CullLocalPointAndRadius( vec3_t origin, float radius ); cullResult_t R_CullPointAndRadius( vec3_t origin, float radius ); diff --git a/src/engine/renderer/tr_main.cpp b/src/engine/renderer/tr_main.cpp index 69d851549c..8afd91fc3f 100644 --- a/src/engine/renderer/tr_main.cpp +++ b/src/engine/renderer/tr_main.cpp @@ -341,7 +341,7 @@ R_CullBox Returns CULL_IN, CULL_CLIP, or CULL_OUT ================= */ -cullResult_t R_CullBox( vec3_t worldBounds[ 2 ] ) +cullResult_t R_CullBox( const vec3_t worldBounds[ 2 ], int lastPlane ) { bool anyClip; cplane_t *frust; @@ -355,7 +355,7 @@ cullResult_t R_CullBox( vec3_t worldBounds[ 2 ] ) // check against frustum planes anyClip = false; - for ( i = 0; i < FRUSTUM_PLANES; i++ ) + for ( i = 0; i <= lastPlane; i++ ) { frust = &tr.viewParms.frustum[ i ]; @@ -837,7 +837,7 @@ static void R_SetupFrustum() MatrixAffineInverse(tr.viewParms.world.viewMatrix, invTransform); //transform planes back to world space for culling - for (int i = 0; i <= FRUSTUM_NEAR; i++) + for (int i = 0; i < FRUSTUM_PLANES; i++) { plane_t plane; VectorCopy(tr.viewParms.portalFrustum[i].normal, plane.normal); @@ -881,13 +881,18 @@ static void R_SetupFrustum() SetPlaneSignbits( &tr.viewParms.frustum[ i ] ); } - // Tr3B: set extra near plane which is required by the dynamic occlusion culling tr.viewParms.frustum[ FRUSTUM_NEAR ].type = PLANE_NON_AXIAL; VectorCopy( tr.viewParms.orientation.axis[ 0 ], tr.viewParms.frustum[ FRUSTUM_NEAR ].normal ); VectorMA( tr.viewParms.orientation.origin, r_znear->value, tr.viewParms.frustum[ FRUSTUM_NEAR ].normal, planeOrigin ); tr.viewParms.frustum[ FRUSTUM_NEAR ].dist = DotProduct( planeOrigin, tr.viewParms.frustum[ FRUSTUM_NEAR ].normal ); SetPlaneSignbits( &tr.viewParms.frustum[ FRUSTUM_NEAR ] ); + + tr.viewParms.frustum[ FRUSTUM_FAR ].type = PLANE_NON_AXIAL; + VectorCopy( tr.viewParms.orientation.axis[ 0 ], tr.viewParms.frustum[ FRUSTUM_FAR ].normal ); + tr.viewParms.frustum[ FRUSTUM_FAR ].dist = tr.viewParms.zFar + + DotProduct( tr.viewParms.orientation.origin, tr.viewParms.frustum[ FRUSTUM_FAR ].normal ); + SetPlaneSignbits( &tr.viewParms.frustum[ FRUSTUM_FAR ] ); } } diff --git a/src/engine/renderer/tr_scene.cpp b/src/engine/renderer/tr_scene.cpp index f8841587aa..981f1872be 100644 --- a/src/engine/renderer/tr_scene.cpp +++ b/src/engine/renderer/tr_scene.cpp @@ -229,13 +229,12 @@ static float NearPlaneCornerDist() // issue on the surface plane, since the GLSL calcs chop off overhang there. Note that // with original Q3 you are required to construct the fog so that it is impossible to pass // through non-surface planes at all. -static bool R_InsideFog( int fognum ) +static bool R_InsideFog( int fognum, float tol ) { // TODO: with portals this should use the point at the view frustum near plane instead const vec3_t &origin = tr.viewParms.orientation.origin; const auto &bounds = tr.world->fogs[ fognum ].bounds; - float tol = NearPlaneCornerDist() + 0.1f; for ( int i = 0; i < 3; i++ ) { @@ -250,11 +249,18 @@ static bool R_InsideFog( int fognum ) void R_AddFogBrushSurfaces() { - // TODO: incorporate them in the BSP? + float tol = NearPlaneCornerDist() + 0.1f; + for ( int i = 1; i < tr.world->numFogs; i++ ) { const fog_t &fog = tr.world->fogs[ i ]; - shader_t *shader = R_InsideFog( i ) + + if ( R_CullBox( fog.bounds, FRUSTUM_FAR ) == cullResult_t::CULL_OUT ) + { + continue; + } + + shader_t *shader = R_InsideFog( i, tol ) ? fog.shader->fogInnerShader : fog.shader->fogOuterShader; R_AddDrawSurf( ( surfaceType_t *)&fog.surf, shader, -1 );