]> git.lizzy.rs Git - minetest.git/commitdiff
Bloom (#12791)
authorx2048 <codeforsmile@gmail.com>
Thu, 29 Sep 2022 18:34:05 +0000 (20:34 +0200)
committerGitHub <noreply@github.com>
Thu, 29 Sep 2022 18:34:05 +0000 (20:34 +0200)
Adds configurable light exposure control and bloom effect (light bleeding) with client-side settings.

16 files changed:
builtin/settingtypes.txt
client/shaders/blur_h/opengl_fragment.glsl [new file with mode: 0644]
client/shaders/blur_h/opengl_vertex.glsl [new file with mode: 0644]
client/shaders/blur_v/opengl_fragment.glsl [new file with mode: 0644]
client/shaders/blur_v/opengl_vertex.glsl [new file with mode: 0644]
client/shaders/extract_bloom/opengl_fragment.glsl [new file with mode: 0644]
client/shaders/extract_bloom/opengl_vertex.glsl [new file with mode: 0644]
client/shaders/second_stage/opengl_fragment.glsl
src/client/game.cpp
src/client/render/pipeline.cpp
src/client/render/pipeline.h
src/client/render/secondstage.cpp
src/client/render/secondstage.h
src/client/renderingengine.h
src/client/shader.cpp
src/defaultsettings.cpp

index 91cc3ec32a3dc96726f180d747a315fc584f2400..e4d345e2d91ae7d630360504b930b5ead818e9a8 100644 (file)
@@ -449,6 +449,44 @@ shadow_soft_radius (Soft shadow radius) float 5.0 1.0 15.0
 #    Minimum value: 0.0; maximum value: 60.0
 shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 0.0 60.0
 
+[**Post processing]
+
+#    Set the exposure compensation factor.
+#    This factor is applied to linear color value 
+#    before all other post-processing effects.
+#    Value of 1.0 (default) means no exposure compensation.
+#    Range: from 0.1 to 10.0
+exposure_factor        (Exposure Factor) float 1.0 0.1 10.0
+
+[**Bloom]
+
+#    Set to true to enable bloom effect.
+#    Bright colors will bleed over the neighboring objects.
+enable_bloom (Enable Bloom) bool false
+
+#    Set to true to render debugging breakdown of the bloom effect.
+#    In debug mode, the screen is split into 4 quadrants: 
+#    top-left - processed base image, top-right - final image
+#    bottom-left - raw base image, bottom-right - bloom texture.
+enable_bloom_debug (Enable Bloom Debug) bool false
+
+#    Set to true to use dedicated texture at each step of bloom effect.
+#    This is a compatibility setting to avoid visual artifacts 
+#    on certain GPUs and video drivers.
+enable_bloom_dedicated_texture (Enable Bloom Dedicated Texture) bool false
+
+#    Set the intensity of bloom
+#    Smaller values make bloom more subtle
+#    Range: from 0.01 to 1.0, default: 0.05
+bloom_intensity (Bloom Intensity) float 0.05 0.01 1.0
+
+#    Set the radius of the bloom filter in pixels.
+#    Larger values render more glow around bright objects
+#    at the cost of higher resource consumption.
+#    Range: from 1 to 64, default: 16
+bloom_radius (Bloom Radius) int 16 1 64
+
+
 [*Audio]
 
 #    Volume of all sounds.
diff --git a/client/shaders/blur_h/opengl_fragment.glsl b/client/shaders/blur_h/opengl_fragment.glsl
new file mode 100644 (file)
index 0000000..c5818f9
--- /dev/null
@@ -0,0 +1,29 @@
+#define rendered texture0
+
+uniform sampler2D rendered;
+uniform vec2 texelSize0;
+uniform mediump float bloomRadius = 3.0;
+
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
+
+void main(void)
+{
+       // kernel distance and linear size
+       mediump float n = 2. * bloomRadius + 1.;
+
+       vec2 uv = varTexCoord.st - vec2(bloomRadius * texelSize0.x, 0.);
+       vec4 color = vec4(0.);
+       mediump float sum = 0.;
+       for (mediump float i = 0.; i < n; i++) {
+               mediump float weight = pow(1. - (abs(i / bloomRadius - 1.)), 1.3);
+               color += texture2D(rendered, uv).rgba * weight;
+               sum += weight;
+               uv += vec2(texelSize0.x, 0.);
+       }
+       color /= sum;
+       gl_FragColor = vec4(color.rgb, 1.0); // force full alpha to avoid holes in the image.
+}
diff --git a/client/shaders/blur_h/opengl_vertex.glsl b/client/shaders/blur_h/opengl_vertex.glsl
new file mode 100644 (file)
index 0000000..12692c2
--- /dev/null
@@ -0,0 +1,11 @@
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
+
+void main(void)
+{
+       varTexCoord.st = inTexCoord0.st;
+       gl_Position = inVertexPosition;
+}
diff --git a/client/shaders/blur_v/opengl_fragment.glsl b/client/shaders/blur_v/opengl_fragment.glsl
new file mode 100644 (file)
index 0000000..6820ca1
--- /dev/null
@@ -0,0 +1,29 @@
+#define rendered texture0
+
+uniform sampler2D rendered;
+uniform vec2 texelSize0;
+uniform mediump float bloomRadius = 3.0;
+
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
+
+void main(void)
+{
+       // kernel distance and linear size
+       mediump float n = 2. * bloomRadius + 1.;
+
+       vec2 uv = varTexCoord.st - vec2(0., bloomRadius * texelSize0.y);
+       vec4 color = vec4(0.);
+       mediump float sum = 0.;
+       for (mediump float i = 0.; i < n; i++) {
+               mediump float weight = pow(1. - (abs(i / bloomRadius - 1.)), 1.3);
+               color += texture2D(rendered, uv).rgba * weight;
+               sum += weight;
+               uv += vec2(0., texelSize0.y);
+       }
+       color /= sum;
+       gl_FragColor = vec4(color.rgb, 1.0); // force full alpha to avoid holes in the image.
+}
diff --git a/client/shaders/blur_v/opengl_vertex.glsl b/client/shaders/blur_v/opengl_vertex.glsl
new file mode 100644 (file)
index 0000000..12692c2
--- /dev/null
@@ -0,0 +1,11 @@
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
+
+void main(void)
+{
+       varTexCoord.st = inTexCoord0.st;
+       gl_Position = inVertexPosition;
+}
diff --git a/client/shaders/extract_bloom/opengl_fragment.glsl b/client/shaders/extract_bloom/opengl_fragment.glsl
new file mode 100644 (file)
index 0000000..8c25328
--- /dev/null
@@ -0,0 +1,21 @@
+#define rendered texture0
+
+uniform sampler2D rendered;
+uniform mediump float exposureFactor = 2.5;
+uniform float bloomLuminanceThreshold = 1.0;
+
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
+
+
+void main(void)
+{
+       vec2 uv = varTexCoord.st;
+       vec4 color = texture2D(rendered, uv).rgba;
+       // translate to linear colorspace (approximate)
+       color.rgb = pow(color.rgb, vec3(2.2)) * exposureFactor;
+       gl_FragColor = vec4(color.rgb, 1.0); // force full alpha to avoid holes in the image.
+}
diff --git a/client/shaders/extract_bloom/opengl_vertex.glsl b/client/shaders/extract_bloom/opengl_vertex.glsl
new file mode 100644 (file)
index 0000000..12692c2
--- /dev/null
@@ -0,0 +1,11 @@
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
+
+void main(void)
+{
+       varTexCoord.st = inTexCoord0.st;
+       gl_Position = inVertexPosition;
+}
index 965450fcbf3243880ed0afed93e36214dc7737af..b929b33b48ad600e0a95b1b9a49d1edd5dd4f848 100644 (file)
@@ -1,6 +1,10 @@
-uniform sampler2D baseTexture;
+#define rendered texture0
+#define bloom texture1
 
-#define rendered baseTexture
+uniform sampler2D rendered;
+uniform sampler2D bloom;
+uniform mediump float exposureFactor = 2.5;
+uniform lowp float bloomIntensity = 1.0;
 
 #ifdef GL_ES
 varying mediump vec2 varTexCoord;
@@ -8,6 +12,24 @@ varying mediump vec2 varTexCoord;
 centroid varying vec2 varTexCoord;
 #endif
 
+#if ENABLE_BLOOM
+
+vec4 applyBloom(vec4 color, vec2 uv)
+{
+       float bias = bloomIntensity;
+       vec4 bloom = texture2D(bloom, uv);
+#if ENABLE_BLOOM_DEBUG
+       if (uv.x > 0.5 && uv.y < 0.5)
+               return vec4(bloom.rgb, color.a);
+       if (uv.x < 0.5)
+               return color;
+#endif
+       color.rgb = mix(color.rgb, bloom.rgb, bias);
+       return color;
+}
+
+#endif
+
 #if ENABLE_TONE_MAPPING
 
 /* Hable's UC2 Tone mapping parameters
@@ -28,15 +50,13 @@ vec3 uncharted2Tonemap(vec3 x)
 
 vec4 applyToneMapping(vec4 color)
 {
-       color = vec4(pow(color.rgb, vec3(2.2)), color.a);
-       const float gamma = 1.6;
-       const float exposureBias = 5.5;
+       const float exposureBias = 2.0;
        color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
        // Precalculated white_scale from
        //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
        vec3 whiteScale = vec3(1.036015346);
        color.rgb *= whiteScale;
-       return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
+       return color;
 }
 #endif
 
@@ -45,9 +65,36 @@ void main(void)
        vec2 uv = varTexCoord.st;
        vec4 color = texture2D(rendered, uv).rgba;
 
+       // translate to linear colorspace (approximate)
+       color.rgb = pow(color.rgb, vec3(2.2));
+
+#if ENABLE_BLOOM_DEBUG
+       if (uv.x > 0.5 || uv.y > 0.5)
+#endif
+       {
+               color.rgb *= exposureFactor;
+       }
+
+
+#if ENABLE_BLOOM
+       color = applyBloom(color, uv);
+#endif
+
+#if ENABLE_BLOOM_DEBUG
+       if (uv.x > 0.5 || uv.y > 0.5)
+#endif
+       {
 #if ENABLE_TONE_MAPPING
-       color = applyToneMapping(color);
+               color = applyToneMapping(color);
+#else
+               color.rgb /= 2.5; // default exposure factor, see also RenderingEngine::DEFAULT_EXPOSURE_FACTOR;
 #endif
+       }
+
+       color.rgb = clamp(color.rgb, vec3(0.), vec3(1.));
+
+       // return to sRGB colorspace (approximate)
+       color.rgb = pow(color.rgb, vec3(1.0 / 2.2));
 
        gl_FragColor = vec4(color.rgb, 1.0); // force full alpha to avoid holes in the image.
 }
index d484e81933ac1ae0dde88ce9235a138718bb8190..e62d0f4a3b7a81d7f5bcff5594b5d747b18194c8 100644 (file)
@@ -405,6 +405,7 @@ typedef s32 SamplerLayer_t;
 class GameGlobalShaderConstantSetter : public IShaderConstantSetter
 {
        Sky *m_sky;
+       Client *m_client;
        bool *m_force_fog_off;
        f32 *m_fog_range;
        bool m_fog_enabled;
@@ -419,16 +420,31 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
        CachedPixelShaderSetting<float, 3> m_minimap_yaw;
        CachedPixelShaderSetting<float, 3> m_camera_offset_pixel;
        CachedPixelShaderSetting<float, 3> m_camera_offset_vertex;
-       CachedPixelShaderSetting<SamplerLayer_t> m_base_texture;
-       CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture;
-       CachedPixelShaderSetting<SamplerLayer_t> m_texture_flags;
-       Client *m_client;
+       CachedPixelShaderSetting<SamplerLayer_t> m_texture0;
+       CachedPixelShaderSetting<SamplerLayer_t> m_texture1;
+       CachedPixelShaderSetting<SamplerLayer_t> m_texture2;
+       CachedPixelShaderSetting<SamplerLayer_t> m_texture3;
+       CachedPixelShaderSetting<float, 2> m_texel_size0;
+       std::array<float, 2> m_texel_size0_values;
+       CachedPixelShaderSetting<float> m_exposure_factor_pixel;
+       float m_user_exposure_factor;
+       bool m_bloom_enabled;
+       CachedPixelShaderSetting<float> m_bloom_intensity_pixel;
+       float m_bloom_intensity;
+       CachedPixelShaderSetting<float> m_bloom_radius_pixel;
+       float m_bloom_radius;
 
 public:
        void onSettingsChange(const std::string &name)
        {
                if (name == "enable_fog")
                        m_fog_enabled = g_settings->getBool("enable_fog");
+               if (name == "exposure_factor")
+                       m_user_exposure_factor = g_settings->getFloat("exposure_factor", 0.1f, 10.0f);
+               if (name == "bloom_intensity")
+                       m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
+               if (name == "bloom_radius")
+                       m_bloom_radius = g_settings->getFloat("bloom_radius", 1.0f, 64.0f);
        }
 
        static void settingsCallback(const std::string &name, void *userdata)
@@ -441,6 +457,7 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
        GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
                        f32 *fog_range, Client *client) :
                m_sky(sky),
+               m_client(client),
                m_force_fog_off(force_fog_off),
                m_fog_range(fog_range),
                m_sky_bg_color("skyBgColor"),
@@ -454,13 +471,24 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
                m_minimap_yaw("yawVec"),
                m_camera_offset_pixel("cameraOffset"),
                m_camera_offset_vertex("cameraOffset"),
-               m_base_texture("baseTexture"),
-               m_normal_texture("normalTexture"),
-               m_texture_flags("textureFlags"),
-               m_client(client)
+               m_texture0("texture0"),
+               m_texture1("texture1"),
+               m_texture2("texture2"),
+               m_texture3("texture3"),
+               m_texel_size0("texelSize0"),
+               m_exposure_factor_pixel("exposureFactor"),
+               m_bloom_intensity_pixel("bloomIntensity"),
+               m_bloom_radius_pixel("bloomRadius")
        {
                g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
+               g_settings->registerChangedCallback("exposure_factor", settingsCallback, this);
+               g_settings->registerChangedCallback("bloom_intensity", settingsCallback, this);
+               g_settings->registerChangedCallback("bloom_radius", settingsCallback, this);
                m_fog_enabled = g_settings->getBool("enable_fog");
+               m_user_exposure_factor = g_settings->getFloat("exposure_factor", 0.1f, 10.0f);
+               m_bloom_enabled = g_settings->getBool("enable_bloom");
+               m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
+               m_bloom_radius = g_settings->getFloat("bloom_radius", 1.0f, 64.0f);
        }
 
        ~GameGlobalShaderConstantSetter()
@@ -526,12 +554,41 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
                m_camera_offset_pixel.set(camera_offset_array, services);
                m_camera_offset_vertex.set(camera_offset_array, services);
 
-               SamplerLayer_t base_tex = 0,
-                               normal_tex = 1,
-                               flags_tex = 2;
-               m_base_texture.set(&base_tex, services);
-               m_normal_texture.set(&normal_tex, services);
-               m_texture_flags.set(&flags_tex, services);
+               SamplerLayer_t tex_id;
+               tex_id = 0;
+               m_texture0.set(&tex_id, services);
+               tex_id = 1;
+               m_texture1.set(&tex_id, services);
+               tex_id = 2;
+               m_texture2.set(&tex_id, services);
+               tex_id = 3;
+               m_texture3.set(&tex_id, services);
+
+               m_texel_size0.set(m_texel_size0_values.data(), services);
+
+               float exposure_factor = RenderingEngine::DEFAULT_EXPOSURE_FACTOR * m_user_exposure_factor;
+               if (std::isnan(exposure_factor))
+                       exposure_factor = RenderingEngine::DEFAULT_EXPOSURE_FACTOR;
+               m_exposure_factor_pixel.set(&exposure_factor, services);
+
+               if (m_bloom_enabled) {
+                       m_bloom_intensity_pixel.set(&m_bloom_intensity, services);
+                       m_bloom_radius_pixel.set(&m_bloom_radius, services);
+               }
+       }
+
+       void onSetMaterial(const video::SMaterial &material)
+       {
+               video::ITexture *texture = material.getTexture(0);
+               if (texture) {
+                       core::dimension2du size = texture->getSize();
+                       m_texel_size0_values[0] = 1.f / size.Width;
+                       m_texel_size0_values[1] = 1.f / size.Height;
+               }
+               else {
+                       m_texel_size0_values[0] = 0.f;
+                       m_texel_size0_values[1] = 0.f;
+               }
        }
 };
 
index baf215d8ed74b72bd52ed8fc9ef574040101d1de..eb38adbad0f315af1ad43dcc0a04654cf966d1e8 100644 (file)
@@ -27,9 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 TextureBuffer::~TextureBuffer()
 {
-       if (m_render_target)
-               m_driver->removeRenderTarget(m_render_target);
-       m_render_target = nullptr;
        for (u32 index = 0; index < m_textures.size(); index++)
                m_driver->removeTexture(m_textures[index]);
        m_textures.clear();
@@ -37,8 +34,6 @@ TextureBuffer::~TextureBuffer()
 
 video::ITexture *TextureBuffer::getTexture(u8 index)
 {
-       if (index == m_depth_texture_index)
-               return m_depth_texture;
        if (index >= m_textures.size())
                return nullptr;
        return m_textures[index];
@@ -52,9 +47,6 @@ void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::str
        if (m_definitions.size() <= index)
                m_definitions.resize(index + 1);
 
-       if (m_depth_texture_index == index)
-               m_depth_texture_index = NO_DEPTH_TEXTURE;
-       
        auto &definition = m_definitions[index];
        definition.valid = true;
        definition.dirty = true;
@@ -71,9 +63,6 @@ void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &na
        if (m_definitions.size() <= index)
                m_definitions.resize(index + 1);
        
-       if (m_depth_texture_index == index)
-               m_depth_texture_index = NO_DEPTH_TEXTURE;
-
        auto &definition = m_definitions[index];
        definition.valid = true;
        definition.dirty = true;
@@ -83,20 +72,6 @@ void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &na
        definition.format = format;
 }
 
-void TextureBuffer::setDepthTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format)
-{
-       assert(index != NO_DEPTH_TEXTURE);
-       setTexture(index, size, name, format);
-       m_depth_texture_index = index;
-}
-
-void TextureBuffer::setDepthTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format)
-{
-       assert(index != NO_DEPTH_TEXTURE);
-       setTexture(index, scale_factor, name, format);
-       m_depth_texture_index = index;
-}
-
 void TextureBuffer::reset(PipelineContext &context)
 {
        if (!m_driver)
@@ -116,41 +91,14 @@ void TextureBuffer::reset(PipelineContext &context)
                m_textures.push_back(nullptr);
 
        // change textures to match definitions
-       bool modified = false;
        for (u32 i = 0; i < m_definitions.size(); i++) {
                video::ITexture **ptr = &m_textures[i];
-               if (i == m_depth_texture_index) {
-                       if (*ptr) {
-                               m_driver->removeTexture(*ptr);
-                               *ptr = nullptr;
-                       }
-                       ptr = &m_depth_texture;
-               }
-
-               if (ensureTexture(ptr, m_definitions[i], context))
-                       modified = true;
+
+               ensureTexture(ptr, m_definitions[i], context);
                m_definitions[i].dirty = false;
        }
        
-       // make sude depth texture is removed and reset
-       if (m_depth_texture_index == NO_DEPTH_TEXTURE && m_depth_texture) {
-               m_driver->removeTexture(m_depth_texture);
-               m_depth_texture = nullptr;
-       }
-
-       if (!m_render_target)
-               m_render_target = m_driver->addRenderTarget();
-
-       if (modified)
-               m_render_target->setTexture(m_textures, m_depth_texture);
-
-       RenderTarget::reset(context);
-}
-
-void TextureBuffer::activate(PipelineContext &context)
-{
-       m_driver->setRenderTargetEx(m_render_target, m_clear ? video::ECBF_DEPTH | video::ECBF_COLOR : 0, context.clear_color);
-       RenderTarget::activate(context);
+       RenderSource::reset(context);
 }
 
 bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefinition& definition, PipelineContext &context)
@@ -186,15 +134,48 @@ bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefini
 }
 
 TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, u8 _texture_index)
-       : buffer(_buffer), texture_index(_texture_index)
+       : buffer(_buffer), texture_map({_texture_index})
+{}
+
+TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector<u8> &_texture_map)
+       : buffer(_buffer), texture_map(_texture_map)
+{}
+
+TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, const std::vector<u8> &_texture_map, u8 _depth_stencil)
+       : buffer(_buffer), texture_map(_texture_map), depth_stencil(_depth_stencil)
 {}
 
+TextureBufferOutput::~TextureBufferOutput()
+{
+       if (render_target && driver)
+               driver->removeRenderTarget(render_target);
+}
+
 void TextureBufferOutput::activate(PipelineContext &context)
 {
-       auto texture = buffer->getTexture(texture_index);
-       auto driver = context.device->getVideoDriver();
-       driver->setRenderTarget(texture, m_clear, m_clear, context.clear_color);
-       driver->OnResize(texture->getSize());
+       if (!driver)
+               driver = context.device->getVideoDriver();
+
+       if (!render_target)
+               render_target = driver->addRenderTarget();
+
+       core::array<video::ITexture *> textures;
+       core::dimension2du size(0, 0);
+       for (size_t i = 0; i < texture_map.size(); i++) {
+               video::ITexture *texture = buffer->getTexture(texture_map[i]);
+               textures.push_back(texture);
+               if (texture && size.Width == 0)
+                       size = texture->getSize();
+       }
+
+       video::ITexture *depth_texture = nullptr;
+       if (depth_stencil != NO_DEPTH_TEXTURE)
+               depth_texture = buffer->getTexture(depth_stencil);
+
+       render_target->setTexture(textures, depth_texture);
+
+       driver->setRenderTargetEx(render_target, m_clear ? video::ECBF_ALL : video::ECBF_NONE, context.clear_color);
+       driver->OnResize(size);
 
        RenderTarget::activate(context);
 }
index 4a6df7de268fd22038b7d55b3c1e1d765855a242..771c6f1d59cbbbb0d3895862b07c41a917883e6f 100644 (file)
@@ -112,7 +112,7 @@ class RenderTarget : virtual public RenderPipelineObject
  *
  * @note Use of TextureBuffer requires use of gl_FragData[] in the shader
  */
-class TextureBuffer : public RenderSource, public RenderTarget
+class TextureBuffer : public RenderSource
 {
 public:
        virtual ~TextureBuffer() override;
@@ -138,29 +138,8 @@ class TextureBuffer : public RenderSource, public RenderTarget
         */
        void setTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format);
 
-       /**
-        * @Configure depth texture and assign index
-        * 
-        * @param index index to use for the depth texture
-        * @param size width and height of the texture in pixels
-        * @param name unique name for the texture
-        * @param format color format
-        */
-       void setDepthTexture(u8 index, core::dimension2du size, const std::string& name, video::ECOLOR_FORMAT format);
-
-       /**
-        * @Configure depth texture and assign index
-        * 
-        * @param index index to use for the depth texture
-        * @param scale_factor relation of the texture dimensions to the screen dimensions
-        * @param name unique name for the texture
-        * @param format color format
-        */
-       void setDepthTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format);
-
        virtual u8 getTextureCount() override { return m_textures.size(); }
        virtual video::ITexture *getTexture(u8 index) override;
-       virtual void activate(PipelineContext &context) override;
        virtual void reset(PipelineContext &context) override;
 private:
        static const u8 NO_DEPTH_TEXTURE = 255;
@@ -189,9 +168,6 @@ class TextureBuffer : public RenderSource, public RenderTarget
        video::IVideoDriver *m_driver { nullptr };
        std::vector<TextureDefinition> m_definitions;
        core::array<video::ITexture *> m_textures;
-       video::ITexture *m_depth_texture { nullptr };
-       u8 m_depth_texture_index { NO_DEPTH_TEXTURE };
-       video::IRenderTarget *m_render_target { nullptr };
 };
 
 /**
@@ -201,10 +177,18 @@ class TextureBufferOutput : public RenderTarget
 {
 public:
        TextureBufferOutput(TextureBuffer *buffer, u8 texture_index);
+       TextureBufferOutput(TextureBuffer *buffer, const std::vector<u8> &texture_map);
+       TextureBufferOutput(TextureBuffer *buffer, const std::vector<u8> &texture_map, u8 depth_stencil);
+       virtual ~TextureBufferOutput() override;
        void activate(PipelineContext &context) override;
 private:
+       static const u8 NO_DEPTH_TEXTURE = 255;
+
        TextureBuffer *buffer;
-       u8 texture_index;
+       std::vector<u8> texture_map;
+       u8 depth_stencil { NO_DEPTH_TEXTURE };
+       video::IRenderTarget* render_target { nullptr };
+       video::IVideoDriver* driver { nullptr };
 };
 
 /**
index 8f6f59537e369abe29ef6838cd6a2113c606c2d3..860b277bd8b09857d774efca8c7c6f7e803f4721 100644 (file)
@@ -88,31 +88,77 @@ void PostProcessingStep::run(PipelineContext &context)
        driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2);
 }
 
+void PostProcessingStep::setBilinearFilter(u8 index, bool value)
+{
+       assert(index < video::MATERIAL_MAX_TEXTURES);
+       material.TextureLayer[index].BilinearFilter = value;
+}
+
 RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client)
 {
        auto buffer = pipeline->createOwned<TextureBuffer>();
-       static const u8 TEXTURE_COLOR = 0;
-       static const u8 TEXTURE_DEPTH = 3;
+       auto driver = client->getSceneManager()->getVideoDriver();
 
-       // init post-processing buffer
-       buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", video::ECF_A8R8G8B8);
+       // configure texture formats
+       video::ECOLOR_FORMAT color_format = video::ECF_A8R8G8B8;
+       if (driver->queryTextureFormat(video::ECF_A16B16G16R16F))
+               color_format = video::ECF_A16B16G16R16F;
 
        video::ECOLOR_FORMAT depth_format = video::ECF_D16; // fallback depth format
-       auto driver = client->getSceneManager()->getVideoDriver();
        if (driver->queryTextureFormat(video::ECF_D32))
                depth_format = video::ECF_D32;
        else if (driver->queryTextureFormat(video::ECF_D24S8))
                depth_format = video::ECF_D24S8;
-       buffer->setDepthTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);
+
+
+       // init post-processing buffer
+       static const u8 TEXTURE_COLOR = 0;
+       static const u8 TEXTURE_DEPTH = 1;
+       static const u8 TEXTURE_BLOOM = 2;
+       static const u8 TEXTURE_BLUR = 3;
+       static const u8 TEXTURE_BLUR_SECONDARY = 4;
+
+       buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format);
+       buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);
 
        // attach buffer to the previous step
-       previousStep->setRenderTarget(buffer);
+       previousStep->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, std::vector<u8> { TEXTURE_COLOR }, TEXTURE_DEPTH));
 
        // post-processing stage
-       // set up shader
-       u32 shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
+       // set up bloom
+       if (g_settings->getBool("enable_bloom")) {
+
+               buffer->setTexture(TEXTURE_BLUR, scale * 0.5, "blur", color_format);
+               buffer->setTexture(TEXTURE_BLOOM, scale * 0.5, "bloom", color_format);
+               u8 bloom_input_texture = TEXTURE_BLOOM;
+               
+               if (g_settings->getBool("enable_bloom_dedicated_texture")) {
+                       buffer->setTexture(TEXTURE_BLUR_SECONDARY, scale * 0.5, "blur2", color_format);
+                       bloom_input_texture = TEXTURE_BLUR_SECONDARY;
+               }
+
+               // get bright spots
+               u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH);
+               RenderStep *extract_bloom = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR });
+               extract_bloom->setRenderSource(buffer);
+               extract_bloom->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, bloom_input_texture));
+               // horizontal blur
+               shader_id = client->getShaderSource()->getShader("blur_h", TILE_MATERIAL_PLAIN, NDT_MESH);
+               RenderStep *blur_h = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { bloom_input_texture });
+               blur_h->setRenderSource(buffer);
+               blur_h->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLUR));
+               // vertical blur
+               shader_id = client->getShaderSource()->getShader("blur_v", TILE_MATERIAL_PLAIN, NDT_MESH);
+               RenderStep *blur_v = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_BLUR });
+               blur_v->setRenderSource(buffer);
+               blur_v->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM));
+       }
 
-       RenderStep *effect = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR });
+       // final post-processing
+       u32 shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
+       PostProcessingStep *effect = pipeline->createOwned<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_BLOOM });
+       pipeline->addStep(effect);
+       effect->setBilinearFilter(1, true); // apply filter to the bloom
        effect->setRenderSource(buffer);
        return effect;
 }
index 0c3c0a44722db16839a9320620b7e8cbcf33ec46..37d5b50718d1c729caba4bb331408940fbc0aec1 100644 (file)
@@ -22,17 +22,33 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "stereo.h"
 #include "pipeline.h"
 
+/**
+ *  Step to apply post-processing filter to the rendered image
+ */
 class PostProcessingStep : public RenderStep
 {
 public:
+       /**
+        * Construct a new PostProcessingStep object
+        * 
+        * @param shader_id ID of the shader in IShaderSource
+        * @param texture_map Map of textures to be chosen from the render source
+        */
        PostProcessingStep(u32 shader_id, const std::vector<u8> &texture_map);
 
-       
+
        void setRenderSource(RenderSource *source) override;
        void setRenderTarget(RenderTarget *target) override;
        void reset(PipelineContext &context) override;
        void run(PipelineContext &context) override;
 
+       /**
+        * Configure bilinear filtering for a specific texture layer
+        * 
+        * @param index Index of the texture layer
+        * @param value true to enable the bilinear filter, false to disable
+        */
+       void setBilinearFilter(u8 index, bool value);
 private:
        u32 shader_id;
        std::vector<u8> texture_map;
index 38420010f65d1cecb17181bd34936a976d9060f7..e26171a44fe1abfe0799803065750381245bce49 100644 (file)
@@ -46,6 +46,10 @@ class RenderingCore;
 class RenderingEngine
 {
 public:
+       /// Default color factor before applying effects like bloom or tomemapping
+       /// this is derived from tonemapping code and tuned empirically
+       static constexpr float DEFAULT_EXPOSURE_FACTOR = 2.5f;
+
        RenderingEngine(IEventReceiver *eventReceiver);
        ~RenderingEngine();
 
index f468078648d1354276bfdf6e91dada044af9deb3..ab9b23117510957e3a57b999ceb820e5fbc06251 100644 (file)
@@ -682,6 +682,13 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
                )";
        }
 
+       // map legacy semantic texture names to texture identifiers
+       fragment_header += R"(
+               #define baseTexture texture0
+               #define normalTexture texture1
+               #define textureFlags texture2
+       )";
+
        // Since this is the first time we're using the GL bindings be extra careful.
        // This should be removed before 5.6.0 or similar.
        if (!GL.GetString) {
@@ -771,6 +778,12 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
                shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n";
        }
 
+       if (g_settings->getBool("enable_bloom")) {
+               shaders_header << "#define ENABLE_BLOOM 1\n";
+               if (g_settings->getBool("enable_bloom_debug"))
+                       shaders_header << "#define ENABLE_BLOOM_DEBUG 1\n";
+       }
+
        shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics
 
        std::string common_header = shaders_header.str();
index bc1c98988460dfe8fc32d2583c31d6f294896499..804bef2ca902156f9774f98d330534ddadfb89cb 100644 (file)
@@ -263,6 +263,12 @@ void set_default_settings()
        settings->setDefault("water_wave_speed", "5.0");
        settings->setDefault("enable_waving_leaves", "false");
        settings->setDefault("enable_waving_plants", "false");
+       settings->setDefault("exposure_factor", "1.0");
+       settings->setDefault("enable_bloom", "false");
+       settings->setDefault("enable_bloom_debug", "false");
+       settings->setDefault("enable_bloom_dedicated_texture", "false");
+       settings->setDefault("bloom_intensity", "0.05");
+       settings->setDefault("bloom_radius", "16");
 
        // Effects Shadows
        settings->setDefault("enable_dynamic_shadows", "false");