diff options
author | x2048 <codeforsmile@gmail.com> | 2023-01-06 22:33:25 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-06 22:33:25 +0100 |
commit | 6d45c243f85942b20dab58753e735ec89a68f710 (patch) | |
tree | bfe6207d73d1b111af82ea9e5794bdaac4ce26e3 /src | |
parent | 2715cc8bf68a2cc8cd583cd5b0bb732ee13a1b49 (diff) | |
download | minetest-6d45c243f85942b20dab58753e735ec89a68f710.tar.xz |
Add dynamic exposure correction (#12959)
* Add uniform for frame delta time
* Adjust exposure in logarithmic (EV) space
* Add network support and LUA API
* Add testing mod
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/client/clientenvironment.cpp | 11 | ||||
-rw-r--r-- | src/client/clientenvironment.h | 2 | ||||
-rw-r--r-- | src/client/game.cpp | 41 | ||||
-rw-r--r-- | src/client/render/pipeline.cpp | 20 | ||||
-rw-r--r-- | src/client/render/pipeline.h | 44 | ||||
-rw-r--r-- | src/client/render/secondstage.cpp | 55 | ||||
-rw-r--r-- | src/client/renderingengine.cpp | 2 | ||||
-rw-r--r-- | src/client/shader.cpp | 3 | ||||
-rw-r--r-- | src/client/shader.h | 37 | ||||
-rw-r--r-- | src/defaultsettings.cpp | 3 | ||||
-rw-r--r-- | src/lighting.cpp | 29 | ||||
-rw-r--r-- | src/lighting.h | 28 | ||||
-rw-r--r-- | src/network/clientpackethandler.cpp | 8 | ||||
-rw-r--r-- | src/network/networkprotocol.h | 7 | ||||
-rw-r--r-- | src/script/lua_api/l_object.cpp | 26 | ||||
-rw-r--r-- | src/server.cpp | 7 |
17 files changed, 281 insertions, 43 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b4f457340..afe008395 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -375,6 +375,7 @@ set(common_SRCS itemdef.cpp itemstackmetadata.cpp light.cpp + lighting.cpp log.cpp main.cpp map.cpp diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 0070fa82f..d9b88eb4a 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -531,8 +531,13 @@ void ClientEnvironment::updateFrameTime(bool is_paused) { // if paused, m_frame_time_pause_accumulator increases by dtime, // otherwise, m_frame_time increases by dtime - if (is_paused) + if (is_paused) { + m_frame_dtime = 0; m_frame_time_pause_accumulator = porting::getTimeMs() - m_frame_time; - else - m_frame_time = porting::getTimeMs() - m_frame_time_pause_accumulator; + } + else { + auto new_frame_time = porting::getTimeMs() - m_frame_time_pause_accumulator; + m_frame_dtime = new_frame_time - MYMAX(m_frame_time, m_frame_time_pause_accumulator); + m_frame_time = new_frame_time; + } } diff --git a/src/client/clientenvironment.h b/src/client/clientenvironment.h index 87820fbe8..f5d46deb5 100644 --- a/src/client/clientenvironment.h +++ b/src/client/clientenvironment.h @@ -144,6 +144,7 @@ public: void updateFrameTime(bool is_paused); u64 getFrameTime() const { return m_frame_time; } + u64 getFrameTimeDelta() const { return m_frame_dtime; } private: ClientMap *m_map; @@ -158,5 +159,6 @@ private: std::list<std::string> m_player_names; v3s16 m_camera_offset; u64 m_frame_time = 0; + u64 m_frame_dtime = 0; u64 m_frame_time_pause_accumulator = 0; }; diff --git a/src/client/game.cpp b/src/client/game.cpp index 3f76d2e05..cf0117046 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -414,6 +414,8 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting<float> m_fog_distance; CachedVertexShaderSetting<float> m_animation_timer_vertex; CachedPixelShaderSetting<float> m_animation_timer_pixel; + CachedVertexShaderSetting<float> m_animation_timer_delta_vertex; + CachedPixelShaderSetting<float> m_animation_timer_delta_pixel; CachedPixelShaderSetting<float, 3> m_day_light; CachedPixelShaderSetting<float, 4> m_star_color; CachedPixelShaderSetting<float, 3> m_eye_position_pixel; @@ -427,8 +429,8 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter 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; + CachedStructPixelShaderSetting<float, 7> m_exposure_params_pixel; + float m_user_exposure_compensation; bool m_bloom_enabled; CachedPixelShaderSetting<float> m_bloom_intensity_pixel; float m_bloom_intensity; @@ -443,8 +445,8 @@ public: { 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 == "exposure_compensation") + m_user_exposure_compensation = g_settings->getFloat("exposure_compensation", -1.0f, 1.0f); if (name == "bloom_intensity") m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f); if (name == "bloom_strength_factor") @@ -470,6 +472,8 @@ public: m_fog_distance("fogDistance"), m_animation_timer_vertex("animationTimer"), m_animation_timer_pixel("animationTimer"), + m_animation_timer_delta_vertex("animationTimerDelta"), + m_animation_timer_delta_pixel("animationTimerDelta"), m_day_light("dayLight"), m_star_color("starColor"), m_eye_position_pixel("eyePosition"), @@ -482,20 +486,24 @@ public: m_texture2("texture2"), m_texture3("texture3"), m_texel_size0("texelSize0"), - m_exposure_factor_pixel("exposureFactor"), + m_exposure_params_pixel("exposureParams", + std::array<const char*, 7> { + "luminanceMin", "luminanceMax", "exposureCorrection", + "speedDarkBright", "speedBrightDark", "centerWeightPower", "compensationFactor" + }), m_bloom_intensity_pixel("bloomIntensity"), m_bloom_strength_pixel("bloomStrength"), m_bloom_radius_pixel("bloomRadius"), m_saturation_pixel("saturation") { g_settings->registerChangedCallback("enable_fog", settingsCallback, this); - g_settings->registerChangedCallback("exposure_factor", settingsCallback, this); + g_settings->registerChangedCallback("exposure_compensation", settingsCallback, this); g_settings->registerChangedCallback("bloom_intensity", settingsCallback, this); g_settings->registerChangedCallback("bloom_strength_factor", settingsCallback, this); g_settings->registerChangedCallback("bloom_radius", settingsCallback, this); g_settings->registerChangedCallback("saturation", settingsCallback, this); m_fog_enabled = g_settings->getBool("enable_fog"); - m_user_exposure_factor = g_settings->getFloat("exposure_factor", 0.1f, 10.0f); + m_user_exposure_compensation = g_settings->getFloat("exposure_compensation", -1.0f, 1.0f); m_bloom_enabled = g_settings->getBool("enable_bloom"); m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f); m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f); @@ -546,6 +554,10 @@ public: m_animation_timer_vertex.set(&animation_timer_f, services); m_animation_timer_pixel.set(&animation_timer_f, services); + float animation_timer_delta_f = (float)m_client->getEnv().getFrameTimeDelta() / 100000.f; + m_animation_timer_delta_vertex.set(&animation_timer_delta_f, services); + m_animation_timer_delta_pixel.set(&animation_timer_delta_f, services); + float eye_position_array[3]; v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition(); epos.getAs3Values(eye_position_array); @@ -577,10 +589,17 @@ public: m_texel_size0.set(m_texel_size0_values.data(), services); - float exposure_factor = m_user_exposure_factor; - if (std::isnan(exposure_factor)) - exposure_factor = 1.0f; - m_exposure_factor_pixel.set(&exposure_factor, services); + const AutoExposure &exposure_params = m_client->getEnv().getLocalPlayer()->getLighting().exposure; + std::array<float, 7> exposure_buffer = { + std::pow(2.0f, exposure_params.luminance_min), + std::pow(2.0f, exposure_params.luminance_max), + exposure_params.exposure_correction, + exposure_params.speed_dark_bright, + exposure_params.speed_bright_dark, + exposure_params.center_weight_power, + powf(2.f, m_user_exposure_compensation) + }; + m_exposure_params_pixel.set(exposure_buffer.data(), services); if (m_bloom_enabled) { m_bloom_intensity_pixel.set(&m_bloom_intensity, services); diff --git a/src/client/render/pipeline.cpp b/src/client/render/pipeline.cpp index 13898f8a4..cc275a7ef 100644 --- a/src/client/render/pipeline.cpp +++ b/src/client/render/pipeline.cpp @@ -101,6 +101,16 @@ void TextureBuffer::reset(PipelineContext &context) RenderSource::reset(context); } +void TextureBuffer::swapTextures(u8 texture_a, u8 texture_b) +{ + assert(m_definitions[texture_a].valid && m_definitions[texture_b].valid); + + video::ITexture *temp = m_textures[texture_a]; + m_textures[texture_a] = m_textures[texture_b]; + m_textures[texture_b] = temp; +} + + bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefinition& definition, PipelineContext &context) { bool modify; @@ -230,6 +240,16 @@ void SetRenderTargetStep::run(PipelineContext &context) step->setRenderTarget(target); } +SwapTexturesStep::SwapTexturesStep(TextureBuffer *_buffer, u8 _texture_a, u8 _texture_b) + : buffer(_buffer), texture_a(_texture_a), texture_b(_texture_b) +{ +} + +void SwapTexturesStep::run(PipelineContext &context) +{ + buffer->swapTextures(texture_a, texture_b); +} + RenderSource *RenderPipeline::getInput() { return &m_input; diff --git a/src/client/render/pipeline.h b/src/client/render/pipeline.h index 354624102..bfdef2931 100644 --- a/src/client/render/pipeline.h +++ b/src/client/render/pipeline.h @@ -53,7 +53,7 @@ struct PipelineContext /** * Base object that can be owned by RenderPipeline - * + * */ class RenderPipelineObject { @@ -74,7 +74,7 @@ public: virtual u8 getTextureCount() = 0; /** - * Get a texture by index. + * Get a texture by index. * Returns nullptr is the texture does not exist. */ virtual video::ITexture *getTexture(u8 index) = 0; @@ -119,7 +119,7 @@ public: /** * Configure fixed-size texture for the specific index - * + * * @param index index of the texture * @param size width and height of the texture in pixels * @param height height of the texture in pixels @@ -130,7 +130,7 @@ public: /** * Configure relative-size texture for the specific index - * + * * @param index index of the texture * @param scale_factor relation of the texture dimensions to the screen dimensions * @param name unique name of the texture @@ -141,6 +141,7 @@ public: virtual u8 getTextureCount() override { return m_textures.size(); } virtual video::ITexture *getTexture(u8 index) override; virtual void reset(PipelineContext &context) override; + void swapTextures(u8 texture_a, u8 texture_b); private: static const u8 NO_DEPTH_TEXTURE = 255; @@ -193,7 +194,7 @@ private: /** * Allows remapping texture indicies in another RenderSource. - * + * * @note all unmapped indexes are passed through to the underlying render source. */ class RemappingSource : RenderSource @@ -205,7 +206,7 @@ public: /** * Maps texture index to a different index in the dependent source. - * + * * @param index texture index as requested by the @see RenderStep. * @param target_index matching texture index in the underlying @see RenderSource. */ @@ -250,7 +251,7 @@ public: virtual u8 getTextureCount() override; /** - * Get a texture by index. + * Get a texture by index. * Returns nullptr is the texture does not exist. */ virtual video::ITexture *getTexture(u8 index) override; @@ -288,14 +289,14 @@ class RenderStep : virtual public RenderPipelineObject public: /** * Assigns render source to this step. - * + * * @param source source of rendering information */ virtual void setRenderSource(RenderSource *source) = 0; /** * Assigned render target to this step. - * + * * @param target render target to send output to. */ virtual void setRenderTarget(RenderTarget *target) = 0; @@ -319,7 +320,7 @@ public: /** * Dynamically changes render target of another step. - * + * * This allows re-running parts of the pipeline with different outputs */ class SetRenderTargetStep : public TrivialRenderStep @@ -333,8 +334,23 @@ private: }; /** + * Swaps two textures in the texture buffer. + * + */ +class SwapTexturesStep : public TrivialRenderStep +{ +public: + SwapTexturesStep(TextureBuffer *buffer, u8 texture_a, u8 texture_b); + virtual void run(PipelineContext &context) override; +private: + TextureBuffer *buffer; + u8 texture_a; + u8 texture_b; +}; + +/** * Render Pipeline provides a flexible way to execute rendering steps in the engine. - * + * * RenderPipeline also implements @see RenderStep, allowing for nesting of the pipelines. */ class RenderPipeline : public RenderStep @@ -342,7 +358,7 @@ class RenderPipeline : public RenderStep public: /** * Add a step to the end of the pipeline - * + * * @param step reference to a @see RenderStep implementation. */ RenderStep *addStep(RenderStep *step) @@ -353,9 +369,9 @@ public: /** * Capture ownership of a dynamically created @see RenderStep instance. - * + * * RenderPipeline will delete the instance when the pipeline is destroyed. - * + * * @param step reference to the instance. * @return RenderStep* value of the 'step' parameter. */ diff --git a/src/client/render/secondstage.cpp b/src/client/render/secondstage.cpp index ebc7e7411..395a0fe6b 100644 --- a/src/client/render/secondstage.cpp +++ b/src/client/render/secondstage.cpp @@ -115,10 +115,14 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep static const u8 TEXTURE_COLOR = 0; static const u8 TEXTURE_DEPTH = 1; static const u8 TEXTURE_BLOOM = 2; + static const u8 TEXTURE_EXPOSURE_1 = 3; + static const u8 TEXTURE_EXPOSURE_2 = 4; static const u8 TEXTURE_BLOOM_DOWN = 10; static const u8 TEXTURE_BLOOM_UP = 20; buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format); + buffer->setTexture(TEXTURE_EXPOSURE_1, core::dimension2du(1,1), "exposure_1", color_format); + buffer->setTexture(TEXTURE_EXPOSURE_2, core::dimension2du(1,1), "exposure_2", color_format); buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format); // attach buffer to the previous step @@ -127,30 +131,40 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // shared variables u32 shader_id; + // Number of mipmap levels of the bloom downsampling texture + const u8 MIPMAP_LEVELS = 4; + + const bool enable_bloom = g_settings->getBool("enable_bloom"); + const bool enable_auto_exposure = g_settings->getBool("enable_auto_exposure"); + // post-processing stage - // set up bloom - if (g_settings->getBool("enable_bloom")) { + u8 source = TEXTURE_COLOR; - buffer->setTexture(TEXTURE_BLOOM, scale, "bloom", color_format); + // common downsampling step for bloom or autoexposure + if (enable_bloom || enable_auto_exposure) { - const u8 MIPMAP_LEVELS = 4; v2f downscale = scale * 0.5; for (u8 i = 0; i < MIPMAP_LEVELS; i++) { - buffer->setTexture(TEXTURE_BLOOM_DOWN + i, downscale, std::string("bloom_down") + std::to_string(i), color_format); - buffer->setTexture(TEXTURE_BLOOM_UP + i, downscale, std::string("bloom_up") + std::to_string(i), color_format); + buffer->setTexture(TEXTURE_BLOOM_DOWN + i, downscale, std::string("downsample") + std::to_string(i), color_format); + if (enable_bloom) + buffer->setTexture(TEXTURE_BLOOM_UP + i, downscale, std::string("upsample") + std::to_string(i), color_format); downscale *= 0.5; } - // 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, TEXTURE_BLOOM)); + if (enable_bloom) { + buffer->setTexture(TEXTURE_BLOOM, scale, "bloom", color_format); + + // 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, TEXTURE_EXPOSURE_1 }); + extract_bloom->setRenderSource(buffer); + extract_bloom->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM)); + source = TEXTURE_BLOOM; + } // downsample shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH); - u8 source = TEXTURE_BLOOM; for (u8 i = 0; i < MIPMAP_LEVELS; i++) { auto step = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { source }); step->setRenderSource(buffer); @@ -158,7 +172,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep step->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_BLOOM_DOWN + i)); source = TEXTURE_BLOOM_DOWN + i; } + } + if (enable_bloom) { // upsample shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH); for (u8 i = MIPMAP_LEVELS - 1; i > 0; i--) { @@ -171,11 +187,24 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep } } + if (enable_auto_exposure) { + shader_id = client->getShaderSource()->getShader("update_exposure", TILE_MATERIAL_PLAIN, NDT_MESH); + auto update_exposure = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_EXPOSURE_1, u8(TEXTURE_BLOOM_DOWN + MIPMAP_LEVELS - 1) }); + update_exposure->setBilinearFilter(1, true); + update_exposure->setRenderSource(buffer); + update_exposure->setRenderTarget(pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_EXPOSURE_2)); + } + // final post-processing 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_UP }); + PostProcessingStep *effect = pipeline->createOwned<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR, TEXTURE_BLOOM_UP, TEXTURE_EXPOSURE_2 }); pipeline->addStep(effect); effect->setBilinearFilter(1, true); // apply filter to the bloom effect->setRenderSource(buffer); + + if (enable_auto_exposure) { + pipeline->addStep<SwapTexturesStep>(buffer, TEXTURE_EXPOSURE_1, TEXTURE_EXPOSURE_2); + } + return effect; } diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index ec7a05338..a58b0efe6 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -56,7 +56,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif RenderingEngine *RenderingEngine::s_singleton = nullptr; -const float RenderingEngine::BASE_BLOOM_STRENGTH = 8.0f; +const float RenderingEngine::BASE_BLOOM_STRENGTH = 1.0f; static gui::GUISkin *createSkin(gui::IGUIEnvironment *environment, diff --git a/src/client/shader.cpp b/src/client/shader.cpp index da3da8ab1..ccecb22c3 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -784,6 +784,9 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, shaders_header << "#define ENABLE_BLOOM_DEBUG 1\n"; } + if (g_settings->getBool("enable_auto_exposure")) + shaders_header << "#define ENABLE_AUTO_EXPOSURE 1\n"; + shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics std::string common_header = shaders_header.str(); diff --git a/src/client/shader.h b/src/client/shader.h index 8f1ba1e41..33c3e45e3 100644 --- a/src/client/shader.h +++ b/src/client/shader.h @@ -121,6 +121,43 @@ public: CachedShaderSetting<T, count, cache>(name, false){} }; +template <typename T, std::size_t count, bool cache, bool is_pixel> +class CachedStructShaderSetting { + const char *m_name; + T m_sent[count]; + bool has_been_set = false; + std::array<const char*, count> m_fields; +public: + CachedStructShaderSetting(const char *name, std::array<const char*, count> &&fields) : + m_name(name), m_fields(std::move(fields)) + {} + + void set(const T value[count], video::IMaterialRendererServices *services) + { + if (cache && has_been_set && std::equal(m_sent, m_sent + count, value)) + return; + + for (std::size_t i = 0; i < count; i++) { + std::string uniform_name = std::string(m_name) + "." + m_fields[i]; + + if (is_pixel) + services->setPixelShaderConstant(services->getPixelShaderConstantID(uniform_name.c_str()), value + i, 1); + else + services->setVertexShaderConstant(services->getVertexShaderConstantID(uniform_name.c_str()), value + i, 1); + } + + if (cache) { + std::copy(value, value + count, m_sent); + has_been_set = true; + } + } +}; + +template<typename T, std::size_t count, bool cache = true> +using CachedStructVertexShaderSetting = CachedStructShaderSetting<T, count, cache, false>; + +template<typename T, std::size_t count, bool cache = true> +using CachedStructPixelShaderSetting = CachedStructShaderSetting<T, count, cache, true>; /* ShaderSource creates and caches shaders. diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 66a7411c4..e9ee8281d 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -278,7 +278,8 @@ 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("exposure_compensation", "0.0"); + settings->setDefault("enable_auto_exposure", "false"); settings->setDefault("enable_bloom", "false"); settings->setDefault("enable_bloom_debug", "false"); settings->setDefault("bloom_strength_factor", "1.0"); diff --git a/src/lighting.cpp b/src/lighting.cpp new file mode 100644 index 000000000..b19d47772 --- /dev/null +++ b/src/lighting.cpp @@ -0,0 +1,29 @@ +/* +Minetest +Copyright (C) 2021 x2048, Dmitry Kostenko <codeforsmile@gmail.com> + +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 2.1 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, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lighting.h" + +AutoExposure::AutoExposure() + : luminance_min(-3.f), + luminance_max(-3.f), + exposure_correction(0.0f), + speed_dark_bright(1000.f), + speed_bright_dark(1000.f), + center_weight_power(1.f) +{} diff --git a/src/lighting.h b/src/lighting.h index 6c837568b..9c4211605 100644 --- a/src/lighting.h +++ b/src/lighting.h @@ -19,10 +19,38 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once + +/** + * Parameters for automatic exposure compensation + * + * Automatic exposure compensation uses the following equation: + * + * wanted_exposure = 2^exposure_correction / clamp(observed_luminance, 2^luminance_min, 2^luminance_max) + * + */ +struct AutoExposure +{ + /// @brief Minimum boundary for computed luminance + float luminance_min; + /// @brief Maximum boundary for computed luminance + float luminance_max; + /// @brief Luminance bias. Higher values make the scene darker, can be negative. + float exposure_correction; + /// @brief Speed of transition from dark to bright scenes + float speed_dark_bright; + /// @brief Speed of transition from bright to dark scenes + float speed_bright_dark; + /// @brief Power value for center-weighted metering. Value of 1.0 measures entire screen uniformly + float center_weight_power; + + AutoExposure(); +}; + /** Describes ambient light settings for a player */ struct Lighting { + AutoExposure exposure; float shadow_intensity {0.0f}; float saturation {1.0f}; }; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 829b1c3e6..0e6256356 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1766,4 +1766,12 @@ void Client::handleCommand_SetLighting(NetworkPacket *pkt) *pkt >> lighting.shadow_intensity; if (pkt->getRemainingBytes() >= 4) *pkt >> lighting.saturation; + if (pkt->getRemainingBytes() >= 24) { + *pkt >> lighting.exposure.luminance_min + >> lighting.exposure.luminance_max + >> lighting.exposure.exposure_correction + >> lighting.exposure.speed_dark_bright + >> lighting.exposure.speed_bright_dark + >> lighting.exposure.center_weight_power; + } } diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 01e65ef68..4e50ef533 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -831,6 +831,13 @@ enum ToClientCommand /* f32 shadow_intensity f32 saturation + exposure parameters + f32 luminance_min + f32 luminance_max + f32 exposure_correction + f32 speed_dark_bright + f32 speed_bright_dark + f32 center_weight_power */ TOCLIENT_NUM_MSG_TYPES = 0x64, diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 0a3e05907..fc2c1254b 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -2297,8 +2297,20 @@ int ObjectRef::l_set_lighting(lua_State *L) getfloatfield(L, -1, "intensity", lighting.shadow_intensity); } lua_pop(L, 1); // shadows + getfloatfield(L, -1, "saturation", lighting.saturation); + lua_getfield(L, 2, "exposure"); + if (lua_istable(L, -1)) { + lighting.exposure.luminance_min = getfloatfield_default(L, -1, "luminance_min", lighting.exposure.luminance_min); + lighting.exposure.luminance_max = getfloatfield_default(L, -1, "luminance_max", lighting.exposure.luminance_max); + lighting.exposure.exposure_correction = getfloatfield_default(L, -1, "exposure_correction", lighting.exposure.exposure_correction); + lighting.exposure.speed_dark_bright = getfloatfield_default(L, -1, "speed_dark_bright", lighting.exposure.speed_dark_bright); + lighting.exposure.speed_bright_dark = getfloatfield_default(L, -1, "speed_bright_dark", lighting.exposure.speed_bright_dark); + lighting.exposure.center_weight_power = getfloatfield_default(L, -1, "center_weight_power", lighting.exposure.center_weight_power); + } + lua_pop(L, 1); // exposure + getServer(L)->setLighting(player, lighting); return 0; } @@ -2321,6 +2333,20 @@ int ObjectRef::l_get_lighting(lua_State *L) lua_setfield(L, -2, "shadows"); lua_pushnumber(L, lighting.saturation); lua_setfield(L, -2, "saturation"); + lua_newtable(L); // "exposure" + lua_pushnumber(L, lighting.exposure.luminance_min); + lua_setfield(L, -2, "luminance_min"); + lua_pushnumber(L, lighting.exposure.luminance_max); + lua_setfield(L, -2, "luminance_max"); + lua_pushnumber(L, lighting.exposure.exposure_correction); + lua_setfield(L, -2, "exposure_correction"); + lua_pushnumber(L, lighting.exposure.speed_dark_bright); + lua_setfield(L, -2, "speed_dark_bright"); + lua_pushnumber(L, lighting.exposure.speed_bright_dark); + lua_setfield(L, -2, "speed_bright_dark"); + lua_pushnumber(L, lighting.exposure.center_weight_power); + lua_setfield(L, -2, "center_weight_power"); + lua_setfield(L, -2, "exposure"); return 1; } diff --git a/src/server.cpp b/src/server.cpp index ee08b45fd..a1171f404 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1866,6 +1866,13 @@ void Server::SendSetLighting(session_t peer_id, const Lighting &lighting) pkt << lighting.shadow_intensity; pkt << lighting.saturation; + pkt << lighting.exposure.luminance_min + << lighting.exposure.luminance_max + << lighting.exposure.exposure_correction + << lighting.exposure.speed_dark_bright + << lighting.exposure.speed_bright_dark + << lighting.exposure.center_weight_power; + Send(&pkt); } |