diff --git a/doomsday/apps/client/include/gl/gl_main.h b/doomsday/apps/client/include/gl/gl_main.h index 9b59d8d1de..faf51afdce 100644 --- a/doomsday/apps/client/include/gl/gl_main.h +++ b/doomsday/apps/client/include/gl/gl_main.h @@ -131,6 +131,8 @@ void GL_Restore2DState(int step, viewport_t const *port, viewdata_t const *viewD void GL_ProjectionMatrix(); +de::Rangef GL_DepthClipRange(); + /** * Returns the projection matrix that is used for rendering the current frame's * 3D portions. diff --git a/doomsday/apps/client/include/render/rend_main.h b/doomsday/apps/client/include/render/rend_main.h index f5f973d5c2..337c50e5a3 100644 --- a/doomsday/apps/client/include/render/rend_main.h +++ b/doomsday/apps/client/include/render/rend_main.h @@ -44,12 +44,12 @@ class LightGrid; } // Multiplicative blending for dynamic lights? -#define IS_MUL (dynlightBlend != 1 && !usingFog) +#define IS_MUL (dynlightBlend != 1 && !fogParams.usingFog) #define MTEX_DETAILS_ENABLED (r_detail && useMultiTexDetails && \ defs.details.size() > 0) #define IS_MTEX_DETAILS (MTEX_DETAILS_ENABLED && numTexUnits > 1) -#define IS_MTEX_LIGHTS (!IS_MTEX_DETAILS && !usingFog && useMultiTexLights \ +#define IS_MTEX_LIGHTS (!IS_MTEX_DETAILS && !fogParams.usingFog && useMultiTexLights \ && numTexUnits > 1 && envModAdd) #define GLOW_HEIGHT_MAX (1024.f) /// Absolute maximum @@ -61,10 +61,18 @@ class LightGrid; DENG_EXTERN_C de::Vector3d vOrigin; // Y/Z swizzled for drawing DENG_EXTERN_C float vang, vpitch, yfov; DENG_EXTERN_C float viewsidex, viewsidey; -DENG_EXTERN_C float fogColor[4]; + +struct FogParams +{ + bool usingFog; + float fogColor[4]; + float fogStart; + float fogEnd; +}; + +extern FogParams fogParams; DENG_EXTERN_C byte smoothTexAnim, devMobjVLights; -DENG_EXTERN_C dd_bool usingFog; DENG_EXTERN_C int renderTextures; /// @c 0= no textures, @c 1= normal mode, @c 2= lighting debug DENG_EXTERN_C int renderWireframe; diff --git a/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/include/fog.glsl b/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/include/fog.glsl new file mode 100644 index 0000000000..90e8d2b3b3 --- /dev/null +++ b/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/include/fog.glsl @@ -0,0 +1,40 @@ +/* + * The Doomsday Engine Project + * Common OpenGL Shaders: Fog (fragment shader) + * + * Copyright (c) 2015 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. This program 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 Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +uniform highp vec4 uFogRange; // startDepth, fogDepth, nearclip, farclip +uniform highp vec4 uFogColor; // set alpha to zero to disable fog + +void applyFog() +{ + if(uFogColor.a > 0.0) + { + highp float near = uFogRange.z; + highp float far = uFogRange.w; + + // First convert the fragment Z back to view space. + highp float zNorm = gl_FragCoord.z * 2.0 - 1.0; + highp float zEye = -2.0 * far * near / (zNorm * (far - near) - (far + near)); + + highp float fogAmount = clamp((zEye - uFogRange.x) / uFogRange.y, 0.0, 1.0); + gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor.rgb, fogAmount); + +// gl_FragColor.rgb = vec3(zEye, 0.0, normZ); + } +} diff --git a/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/model.dei b/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/model.dei index 03a6ad09b2..193b07fb67 100644 --- a/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/model.dei +++ b/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/model.dei @@ -46,7 +46,8 @@ model.skeletal { }" include.fragment + include/lighting.glsl, + include/fog.glsl> fragment = " uniform highp float uAlpha; uniform highp float uAlphaLimit; // alpha test to discard fragments @@ -79,6 +80,8 @@ model.skeletal { gl_FragColor.a = min(1.0, diffuse.a + specular.a); if(gl_FragColor.a < uAlphaLimit) discard; + + applyFog(); }" } @@ -126,7 +129,8 @@ model.skeletal { }" include.fragment + include/lighting.glsl, + include/fog.glsl> fragment = " uniform highp float uAlphaLimit; @@ -152,6 +156,8 @@ model.skeletal { // All accepted fragments are opaque. gl_FragColor.a = 1.0; + + applyFog(); }" } } diff --git a/doomsday/apps/client/src/dd_main.cpp b/doomsday/apps/client/src/dd_main.cpp index 982e5c175d..1ab6ebef3a 100644 --- a/doomsday/apps/client/src/dd_main.cpp +++ b/doomsday/apps/client/src/dd_main.cpp @@ -2247,7 +2247,7 @@ void DD_UpdateEngineState() } #ifdef __CLIENT__ - dd_bool hadFog = usingFog; + dd_bool hadFog = fogParams.usingFog; GL_TotalReset(); GL_TotalRestore(); // Bring GL back online. diff --git a/doomsday/apps/client/src/gl/gl_main.cpp b/doomsday/apps/client/src/gl/gl_main.cpp index c2d94fc8f2..22a37dfb83 100644 --- a/doomsday/apps/client/src/gl/gl_main.cpp +++ b/doomsday/apps/client/src/gl/gl_main.cpp @@ -379,18 +379,18 @@ void GL_Init2DState() glOrtho(0, 320, 200, 0, -1, 1); // Default state for the white fog is off. - usingFog = false; + fogParams.usingFog = false; glDisable(GL_FOG); glFogi(GL_FOG_MODE, (fogModeDefault == 0 ? GL_LINEAR : fogModeDefault == 1 ? GL_EXP : GL_EXP2)); glFogf(GL_FOG_START, DEFAULT_FOG_START); glFogf(GL_FOG_END, DEFAULT_FOG_END); glFogf(GL_FOG_DENSITY, DEFAULT_FOG_DENSITY); - fogColor[0] = DEFAULT_FOG_COLOR_RED; - fogColor[1] = DEFAULT_FOG_COLOR_GREEN; - fogColor[2] = DEFAULT_FOG_COLOR_BLUE; - fogColor[3] = 1; - glFogfv(GL_FOG_COLOR, fogColor); + fogParams.fogColor[0] = DEFAULT_FOG_COLOR_RED; + fogParams.fogColor[1] = DEFAULT_FOG_COLOR_GREEN; + fogParams.fogColor[2] = DEFAULT_FOG_COLOR_BLUE; + fogParams.fogColor[3] = 1; + glFogfv(GL_FOG_COLOR, fogParams.fogColor); } void GL_SwitchTo3DState(dd_bool push_state, viewport_t const *port, viewdata_t const *viewData) @@ -499,6 +499,11 @@ void GL_Restore2DState(dint step, viewport_t const *port, viewdata_t const *view } } +Rangef GL_DepthClipRange() +{ + return Rangef(glNearClip, glFarClip); +} + Matrix4f GL_GetProjectionMatrix() { dfloat const fov = Rend_FieldOfView(); @@ -553,7 +558,7 @@ void GL_SetupFogFromMapInfo(Record const *mapInfo) #undef GL_UseFog DENG_EXTERN_C void GL_UseFog(dint yes) { - usingFog = yes; + fogParams.usingFog = yes; } void GL_SelectTexUnits(dint count) @@ -1547,23 +1552,26 @@ D_CMD(Fog) { for(dint i = 0; i < 3; ++i) { - fogColor[i] = strtol(argv[2 + i], nullptr, 0) / 255.0f; + fogParams.fogColor[i] = strtol(argv[2 + i], nullptr, 0) / 255.0f; } - fogColor[3] = 1; + fogParams.fogColor[3] = 1; - glFogfv(GL_FOG_COLOR, fogColor); + glFogfv(GL_FOG_COLOR, fogParams.fogColor); LOG_GL_VERBOSE("Fog color set"); return true; } if(!stricmp(argv[1], "start") && argc == 3) { - glFogf(GL_FOG_START, (GLfloat) strtod(argv[2], nullptr)); + fogParams.fogStart = (GLfloat) strtod(argv[2], nullptr); + + glFogf(GL_FOG_START, fogParams.fogStart); LOG_GL_VERBOSE("Fog start distance set"); return true; } if(!stricmp(argv[1], "end") && argc == 3) { - glFogf(GL_FOG_END, (GLfloat) strtod(argv[2], nullptr)); + fogParams.fogEnd = (GLfloat) strtod(argv[2], nullptr); + glFogf(GL_FOG_END, fogParams.fogEnd); LOG_GL_VERBOSE("Fog end distance set"); return true; } diff --git a/doomsday/apps/client/src/render/api_render.cpp b/doomsday/apps/client/src/render/api_render.cpp index 95abd1f4a5..ec211bd6f1 100644 --- a/doomsday/apps/client/src/render/api_render.cpp +++ b/doomsday/apps/client/src/render/api_render.cpp @@ -198,7 +198,7 @@ DENG_EXTERN_C void R_SetupFog(dfloat start, dfloat end, dfloat density, dfloat * DENG_EXTERN_C void R_SetupFogDefaults() { // Go with the defaults. - Con_Execute(CMDS_DDAY,"fog off", true, false); + Con_Execute(CMDS_DDAY, "fog off", true, false); } DENG_DECLARE_API(Rend) = diff --git a/doomsday/apps/client/src/render/modelrenderer.cpp b/doomsday/apps/client/src/render/modelrenderer.cpp index 634571d765..ba86386e0e 100644 --- a/doomsday/apps/client/src/render/modelrenderer.cpp +++ b/doomsday/apps/client/src/render/modelrenderer.cpp @@ -130,6 +130,8 @@ DENG2_PIMPL(ModelRenderer) GLUniform uAmbientLight { "uAmbientLight", GLUniform::Vec4 }; GLUniform uLightDirs { "uLightDirs", GLUniform::Vec3Array, MAX_LIGHTS }; GLUniform uLightIntensities { "uLightIntensities", GLUniform::Vec4Array, MAX_LIGHTS }; + GLUniform uFogRange { "uFogRange", GLUniform::Vec4 }; + GLUniform uFogColor { "uFogColor", GLUniform::Vec4 }; Matrix4f inverseLocal; int lightCount = 0; @@ -229,7 +231,9 @@ DENG2_PIMPL(ModelRenderer) << uEyePos << uAmbientLight << uLightDirs - << uLightIntensities; + << uLightIntensities + << uFogRange + << uFogColor; programs[name] = prog.get(); return prog.release(); @@ -614,9 +618,32 @@ DENG2_PIMPL(ModelRenderer) lightCount++; } + void setupFog() + { + if(fogParams.usingFog) + { + uFogColor = Vector4f(fogParams.fogColor[0], + fogParams.fogColor[1], + fogParams.fogColor[2], + 1.f); + + Rangef const depthPlanes = GL_DepthClipRange(); + float const fogDepth = fogParams.fogEnd - fogParams.fogStart; + uFogRange = Vector4f(fogParams.fogStart, + fogDepth, + depthPlanes.start, + depthPlanes.end); + } + else + { + uFogColor = Vector4f(); + } + } + template // generic to accommodate psprites and vispsprites void draw(Params const &p) { + setupFog(); uTex = static_cast(p.model->textures->atlas()); p.model->draw(&p.animator->appearance(), p.animator); diff --git a/doomsday/apps/client/src/render/r_main.cpp b/doomsday/apps/client/src/render/r_main.cpp index f8518b7c43..e2c2eb6a55 100644 --- a/doomsday/apps/client/src/render/r_main.cpp +++ b/doomsday/apps/client/src/render/r_main.cpp @@ -173,7 +173,7 @@ void Rend_Draw2DPlayerSprites() if(ddpl.flags & DDPF_CAMERA ) return; if(ddpl.flags & DDPF_CHASECAM) return; - if(usingFog) + if(fogParams.usingFog) { glEnable(GL_FOG); } @@ -201,7 +201,7 @@ void Rend_Draw2DPlayerSprites() } } - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } diff --git a/doomsday/apps/client/src/render/rend_main.cpp b/doomsday/apps/client/src/render/rend_main.cpp index c48cce2560..661697df2a 100644 --- a/doomsday/apps/client/src/render/rend_main.cpp +++ b/doomsday/apps/client/src/render/rend_main.cpp @@ -152,8 +152,7 @@ D_CMD(TexReset); dint useBias; ///< Shadow Bias enabled? cvar -dd_bool usingFog; ///< Is the fog in use? -dfloat fogColor[4]; +FogParams fogParams; dfloat fieldOfView = 95.0f; dbyte smoothTexAnim = true; @@ -628,7 +627,7 @@ Vector3f Rend_LuminousColor(Vector3f const &color, dfloat light) light = de::clamp(0.f, light, 1.f) * dynlightFactor; // In fog additive blending is used; the normal fog color is way too bright. - if(usingFog) light *= dynlightFogBright; + if(fogParams.usingFog) light *= dynlightFogBright; // Multiply light with (ambient) color. return color * light; @@ -2169,7 +2168,7 @@ static bool projectShadow(Vector3d const &topLeft, Vector3d const &bottomRight, } dfloat shadowStrength = Mobj_ShadowStrength(mob) * ::shadowFactor; - if(::usingFog) shadowStrength /= 2; + if(fogParams.usingFog) shadowStrength /= 2; if(shadowStrength <= 0) return false; coord_t shadowRadius = Mobj_ShadowRadius(mob); @@ -3814,7 +3813,7 @@ static void pushGLStateForPass(DrawMode mode, TexUnitMap &texUnitMap) glDepthFunc(GL_LESS); // Fog is allowed during this pass. - if(usingFog) + if(fogParams.usingFog) { glEnable(GL_FOG); } @@ -3844,7 +3843,7 @@ static void pushGLStateForPass(DrawMode mode, TexUnitMap &texUnitMap) glDepthFunc(GL_LESS); // Fog is allowed during this pass. - if(usingFog) + if(fogParams.usingFog) { glEnable(GL_FOG); } @@ -3901,7 +3900,7 @@ static void pushGLStateForPass(DrawMode mode, TexUnitMap &texUnitMap) glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); - if(usingFog) + if(fogParams.usingFog) { glEnable(GL_FOG); glFogfv(GL_FOG_COLOR, black); @@ -3938,7 +3937,7 @@ static void pushGLStateForPass(DrawMode mode, TexUnitMap &texUnitMap) // All of the surfaces are opaque. glDisable(GL_BLEND); // Fog is allowed. - if(usingFog) + if(fogParams.usingFog) { glEnable(GL_FOG); } @@ -3970,10 +3969,10 @@ static void pushGLStateForPass(DrawMode mode, TexUnitMap &texUnitMap) glEnable(GL_BLEND); glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); // Use fog to fade the details, if fog is enabled. - if(usingFog) + if(fogParams.usingFog) { glEnable(GL_FOG); - dfloat const midGray[] = { .5f, .5f, .5f, fogColor[3] }; // The alpha is probably meaningless? + dfloat const midGray[] = { .5f, .5f, .5f, fogParams.fogColor[3] }; // The alpha is probably meaningless? glFogfv(GL_FOG_COLOR, midGray); } break; @@ -3992,10 +3991,10 @@ static void pushGLStateForPass(DrawMode mode, TexUnitMap &texUnitMap) glEnable(GL_BLEND); glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); // Use fog to fade the details, if fog is enabled. - if(usingFog) + if(fogParams.usingFog) { glEnable(GL_FOG); - dfloat const midGray[] = { .5f, .5f, .5f, fogColor[3] }; // The alpha is probably meaningless? + dfloat const midGray[] = { .5f, .5f, .5f, fogParams.fogColor[3] }; // The alpha is probably meaningless? glFogfv(GL_FOG_COLOR, midGray); } break; @@ -4011,10 +4010,10 @@ static void pushGLStateForPass(DrawMode mode, TexUnitMap &texUnitMap) glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); // Set normal fog, if it's enabled. - if(usingFog) + if(fogParams.usingFog) { glEnable(GL_FOG); - glFogfv(GL_FOG_COLOR, fogColor); + glFogfv(GL_FOG_COLOR, fogParams.fogColor); } glEnable(GL_BLEND); GL_BlendMode(BM_NORMAL); @@ -4029,7 +4028,7 @@ static void pushGLStateForPass(DrawMode mode, TexUnitMap &texUnitMap) glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); - if(usingFog) + if(fogParams.usingFog) { // Fog makes the shininess diminish in the distance. glEnable(GL_FOG); @@ -4049,7 +4048,7 @@ static void pushGLStateForPass(DrawMode mode, TexUnitMap &texUnitMap) glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); - if(usingFog) + if(fogParams.usingFog) { // Fog makes the shininess diminish in the distance. glEnable(GL_FOG); @@ -4082,7 +4081,7 @@ static void popGLStateForPass(DrawMode mode) case DM_ALL: glEnable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } @@ -4095,7 +4094,7 @@ static void popGLStateForPass(DrawMode mode) GL_ModulateTexture(1); glEnable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } @@ -4125,7 +4124,7 @@ static void popGLStateForPass(DrawMode mode) case DM_LIGHTS: glDisable(GL_DEPTH_TEST); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } @@ -4144,7 +4143,7 @@ static void popGLStateForPass(DrawMode mode) glEnable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } @@ -4161,7 +4160,7 @@ static void popGLStateForPass(DrawMode mode) glEnable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } @@ -4173,7 +4172,7 @@ static void popGLStateForPass(DrawMode mode) glEnable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } @@ -4181,7 +4180,7 @@ static void popGLStateForPass(DrawMode mode) case DM_SHADOW: glDisable(GL_DEPTH_TEST); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } @@ -4190,7 +4189,7 @@ static void popGLStateForPass(DrawMode mode) case DM_SHINY: glEnable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } @@ -4202,7 +4201,7 @@ static void popGLStateForPass(DrawMode mode) GL_ModulateTexture(1); glEnable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } @@ -4582,10 +4581,10 @@ static void drawAllLists(Map &map) glDepthFunc(GL_LESS); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0); - if(usingFog) + if(fogParams.usingFog) { glEnable(GL_FOG); - glFogfv(GL_FOG_COLOR, fogColor); + glFogfv(GL_FOG_COLOR, fogParams.fogColor); } // Draw masked walls, sprites and models. @@ -4594,7 +4593,7 @@ static void drawAllLists(Map &map) // Draw particles. Rend_RenderParticles(map); - if(usingFog) + if(fogParams.usingFog) { glDisable(GL_FOG); } diff --git a/doomsday/apps/client/src/render/skydrawable.cpp b/doomsday/apps/client/src/render/skydrawable.cpp index a6680f7ec1..3447a7ca6e 100644 --- a/doomsday/apps/client/src/render/skydrawable.cpp +++ b/doomsday/apps/client/src/render/skydrawable.cpp @@ -677,12 +677,12 @@ void SkyDrawable::draw(Animator const *animator) const // Only drawn when at least one layer is active. if(d->firstActiveLayer < 0) return; - if(usingFog) glEnable(GL_FOG); + if(fogParams.usingFog) glEnable(GL_FOG); d->drawSphere(); d->drawModels(animator); - if(usingFog) glDisable(GL_FOG); + if(fogParams.usingFog) glDisable(GL_FOG); } MaterialVariantSpec const &SkyDrawable::layerMaterialSpec(bool masked) // static diff --git a/doomsday/apps/client/src/resource/materialanimator.cpp b/doomsday/apps/client/src/resource/materialanimator.cpp index 08715eea96..191d20848f 100644 --- a/doomsday/apps/client/src/resource/materialanimator.cpp +++ b/doomsday/apps/client/src/resource/materialanimator.cpp @@ -490,7 +490,7 @@ DENG2_PIMPL(MaterialAnimator) // If fog is active, inter=0 is accepted as well. Otherwise // flickering may occur if the rendering passes don't match for // blended and unblended surfaces. - if(!(!::usingFog && ls.inter == 0)) + if(!(!fogParams.usingFog && ls.inter == 0)) { snapshot->units[TU_DETAIL_INTER] = GLTextureUnit(*tex, @@ -565,7 +565,7 @@ DENG2_PIMPL(MaterialAnimator) // If fog is active, inter=0 is accepted as well. Otherwise // flickering may occur if the rendering passes don't match for // blended and unblended surfaces. - if(!(!usingFog && ls.inter == 0)) + if(!(!fogParams.usingFog && ls.inter == 0)) { snapshot->units[TU_LAYER0_INTER + texLayerIndex] = GLTextureUnit(*tex,