From 02444add2a115abfc9c03d5cf29e192e42a504e5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 14 Mar 2020 16:39:32 +0400 Subject: [PATCH] Support for radial fog (feature #4708) --- CHANGELOG.md | 1 + apps/opencs/model/world/data.cpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 5 ++++- .../reference/modding/settings/shaders.rst | 11 ++++++++++ files/settings-default.cfg | 4 ++++ files/shaders/objects_fragment.glsl | 12 ++++++---- files/shaders/objects_vertex.glsl | 7 ++++-- files/shaders/terrain_fragment.glsl | 11 +++++++--- files/shaders/terrain_vertex.glsl | 6 +++-- files/shaders/water_fragment.glsl | 22 ++++++++++++++----- files/shaders/water_vertex.glsl | 4 ++-- 11 files changed, 64 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d44ab19dc..32255262e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -225,6 +225,7 @@ Feature #4544: Actors movement deceleration Feature #4673: Weapon sheathing Feature #4675: Support for NiRollController + Feature #4708: Radial fog support Feature #4730: Native animated containers support Feature #4784: Launcher: Duplicate Content Lists Feature #4812: Support NiSwitchNode diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 23ea1a5a97..b45df35b1a 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -82,6 +82,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat defines["forcePPL"] = "0"; // Don't force per-pixel lighting defines["clamp"] = "1"; // Clamp lighting defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind + defines["radialFog"] = "0"; for (const auto& define : shadowDefines) defines[define.first] = define.second; mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index dd570f5584..0ab7567474 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -217,7 +217,9 @@ namespace MWRender { resourceSystem->getSceneManager()->setParticleSystemMask(SceneUtil::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); - resourceSystem->getSceneManager()->setForceShaders(Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows")); // Shadows have problems with fixed-function mode + // Shadows and radial fog have problems with fixed-function mode + bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows"); + resourceSystem->getSceneManager()->setForceShaders(forceShaders); // FIXME: calling dummy method because terrain needs to know whether lighting is clamped resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders")); resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "Shaders")); @@ -254,6 +256,7 @@ namespace MWRender globalDefines["forcePPL"] = Settings::Manager::getBool("force per pixel lighting", "Shaders") ? "1" : "0"; globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0"; globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0"; + globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0"; // It is unnecessary to stop/start the viewer as no frames are being rendered yet. mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines); diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index bc676d22f6..e23cc3d548 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -135,3 +135,14 @@ apply lighting to environment maps Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. Affected objects will use shaders. + +radial fog +---------- + +:Type: boolean +:Range: True/False +:Default: False + +By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen. +This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV. +Note that the rendering will act as if you have 'force shaders' option enabled with this on, which means that shaders will be used to render all objects and the terrain. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c6f7abfba0..61ac5366ea 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -350,6 +350,10 @@ terrain specular map pattern = _diffusespec # Affected objects use shaders. apply lighting to environment maps = false +# Determine fog intensity based on the distance from the eye point. +# This makes fogging independent from the viewing angle. Shaders will be used to render all objects. +radial fog = false + [Input] # Capture control of the cursor prevent movement outside the window. diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index a222d9c7bd..31e929a90b 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -49,7 +49,8 @@ uniform vec2 envMapLumaBias; uniform mat2 bumpMapMatrix; #endif -varying float depth; +varying float euclideanDepth; +varying float linearDepth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -146,7 +147,7 @@ void main() #endif - float shadowing = unshadowedLightRatio(depth); + float shadowing = unshadowedLightRatio(linearDepth); #if !PER_PIXEL_LIGHTING @@ -178,8 +179,11 @@ void main() #endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; - - float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#if @radialFog + float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#else + float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#endif gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); applyShadowDebugOverlay(); diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index ec5449b873..dc8c91b030 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -37,7 +37,8 @@ varying vec2 bumpMapUV; varying vec2 specularMapUV; #endif -varying float depth; +varying float euclideanDepth; +varying float linearDepth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -57,10 +58,12 @@ varying vec3 passNormal; void main(void) { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - depth = gl_Position.z; vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; + euclideanDepth = length(viewPos.xyz); + linearDepth = gl_Position.z; + vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); #if @envMap diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 4910788bb7..b1917d33bb 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -12,7 +12,8 @@ uniform sampler2D normalMap; uniform sampler2D blendMap; #endif -varying float depth; +varying float euclideanDepth; +varying float linearDepth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -66,7 +67,7 @@ void main() gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a; #endif - float shadowing = unshadowedLightRatio(depth); + float shadowing = unshadowedLightRatio(linearDepth); #if !PER_PIXEL_LIGHTING @@ -90,7 +91,11 @@ void main() gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; - float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#if @radialFog + float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#else + float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#endif gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); applyShadowDebugOverlay(); diff --git a/files/shaders/terrain_vertex.glsl b/files/shaders/terrain_vertex.glsl index 8a9cf82cc3..14e291f43c 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -1,7 +1,8 @@ #version 120 varying vec2 uv; -varying float depth; +varying float euclideanDepth; +varying float linearDepth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -21,10 +22,11 @@ varying vec3 passNormal; void main(void) { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - depth = gl_Position.z; vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; + euclideanDepth = length(viewPos.xyz); + linearDepth = gl_Position.z; vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index 808fab5ee9..6e51a20824 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -124,7 +124,7 @@ vec2 normalCoords(vec2 uv, float scale, float speed, float time, float timer1, f varying vec3 screenCoordsPassthrough; varying vec4 position; -varying float depthPassthrough; +varying float linearDepth; uniform sampler2D normalMap; @@ -160,7 +160,7 @@ void main(void) vec2 UV = worldPos.xy / (8192.0*5.0) * 3.0; UV.y *= -1.0; - float shadow = unshadowedLightRatio(depthPassthrough); + float shadow = unshadowedLightRatio(linearDepth); vec2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; screenCoords.y = (1.0-screenCoords.y); @@ -203,11 +203,17 @@ void main(void) float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); // air to water; water to air float fresnel = clamp(fresnel_dielectric(vVec, normal, ior), 0.0, 1.0); +#if @radialFog + float radialDepth = distance(position.xyz, cameraPos); + float radialize = radialDepth / linearDepth; +#else + float radialize = 1.0; +#endif vec2 screenCoordsOffset = normal.xy * REFL_BUMP; #if REFRACTION - float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x); - float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x); - float surfaceDepth = linearizeDepth(gl_FragCoord.z); + float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x) * radialize; + float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x) * radialize; + float surfaceDepth = linearizeDepth(gl_FragCoord.z) * radialize; float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum screenCoordsOffset *= clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1); #endif @@ -246,7 +252,11 @@ void main(void) #endif // fog - float fogValue = clamp((depthPassthrough - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#if @radialFog + float fogValue = clamp((radialDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#else + float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#endif gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); applyShadowDebugOverlay(); diff --git a/files/shaders/water_vertex.glsl b/files/shaders/water_vertex.glsl index 2377f0af4b..02a395f95d 100644 --- a/files/shaders/water_vertex.glsl +++ b/files/shaders/water_vertex.glsl @@ -2,7 +2,7 @@ varying vec3 screenCoordsPassthrough; varying vec4 position; -varying float depthPassthrough; +varying float linearDepth; #include "shadows_vertex.glsl" @@ -20,7 +20,7 @@ void main(void) position = gl_Vertex; - depthPassthrough = gl_Position.z; + linearDepth = gl_Position.z; setupShadowCoords(gl_ModelViewMatrix * gl_Vertex, normalize((gl_NormalMatrix * gl_Normal).xyz)); }