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/Material.cpp b/src/engine/renderer/Material.cpp index a091109746..be630c1c36 100644 --- a/src/engine/renderer/Material.cpp +++ b/src/engine/renderer/Material.cpp @@ -1114,11 +1114,12 @@ 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_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->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/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index 0526d2c5aa..78078a3857 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_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 d03844d401..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,26 +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->fogParms = shader->fogParms; - - 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 bc54905438..9bbb2926a0 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1116,8 +1116,9 @@ enum struct fogParms_t { - vec3_t color; + Color::Color color; float depthForOpaque; + float falloffExp; }; struct shader_t @@ -1340,9 +1341,7 @@ enum int originalBrushNumber; vec3_t bounds[ 2 ]; - Color::Color color; // in packed byte format - float tcScale; // texture coordinate vector scales - fogParms_t fogParms; + 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 713934f9d1..28c5aa5ce9 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -1646,12 +1646,13 @@ 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_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->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 647eae66c6..3fba2fcb1d 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -4398,17 +4398,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 ] ) @@ -4417,7 +4423,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); @@ -4425,6 +4431,38 @@ static bool ParseShader( const char *_text ) SkipRestOfLine( text ); continue; } + // fogGradient + // Default: fogGradient expFalloff 5 + 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" ) ) { @@ -5780,6 +5818,11 @@ static shader_t *FinishShader() shader.portalRange = r_portalDefaultRange.Get(); } + if ( shader.fogParms.falloffExp == 0.0f ) + { + shader.fogParms.falloffExp = M_LN2 / 5; + } + numStages = MAX_SHADER_STAGES; GroupActiveStages();