mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-06 02:18:35 +01:00
Add a white balance to the tonemapping
This commit is contained in:
@@ -24,6 +24,51 @@ std::size_t const BLOOM_LEVELS{6};
|
||||
int const BLOOM_MIN_SIZE{8};
|
||||
// soft knee of the threshold, a fraction of it
|
||||
float const BLOOM_KNEE{0.5f};
|
||||
// colour temperature of the white the image is balanced to: the white of the display (d65 is the daylight of about 6500 K)
|
||||
float const WHITE_BALANCE_REFERENCE{6500.f};
|
||||
// distance off the daylight locus (duv, cie 1960 ucs) of the tint at its ends (-1, 1)
|
||||
float const WHITE_BALANCE_TINT_DUV{0.02f};
|
||||
|
||||
// chromaticity (cie xy) of the cie daylight of the correlated colour temperature Kelvin; the formula holds for 4000..25000 K
|
||||
glm::dvec2 daylight_chromaticity(double const Kelvin)
|
||||
{
|
||||
auto const t{std::clamp(Kelvin, 4000.0, 25000.0)};
|
||||
auto const x{t <= 7000.0 ? (-4.6070e9 / (t * t * t) + 2.9678e6 / (t * t) + 0.09911e3 / t + 0.244063)
|
||||
: (-2.0064e9 / (t * t * t) + 1.9018e6 / (t * t) + 0.24748e3 / t + 0.237040)};
|
||||
return glm::dvec2{x, -3.0 * x * x + 2.87 * x - 0.275};
|
||||
}
|
||||
|
||||
glm::dvec2 xy_to_uv(glm::dvec2 const &Xy)
|
||||
{
|
||||
auto const d{-2.0 * Xy.x + 12.0 * Xy.y + 3.0};
|
||||
return glm::dvec2{4.0 * Xy.x / d, 6.0 * Xy.y / d};
|
||||
}
|
||||
|
||||
glm::dvec2 uv_to_xy(glm::dvec2 const &Uv)
|
||||
{
|
||||
auto const d{2.0 * Uv.x - 8.0 * Uv.y + 4.0};
|
||||
return glm::dvec2{3.0 * Uv.x / d, 2.0 * Uv.y / d};
|
||||
}
|
||||
|
||||
// cone responses (bradford) of the light of the temperature Kelvin, taken off the daylight locus by Tint: + towards green
|
||||
// (v grows), so a positive tint takes the green out of the image and makes it magenta
|
||||
glm::dvec3 white_cone_response(double const Kelvin, double const Tint)
|
||||
{
|
||||
auto uv{xy_to_uv(daylight_chromaticity(Kelvin))};
|
||||
if (Tint != 0.0)
|
||||
{
|
||||
auto const along{xy_to_uv(daylight_chromaticity(Kelvin + 100.0)) - xy_to_uv(daylight_chromaticity(Kelvin - 100.0))};
|
||||
auto normal{glm::normalize(glm::dvec2{-along.y, along.x})};
|
||||
if (normal.y < 0.0)
|
||||
normal = -normal;
|
||||
uv += normal * (Tint * WHITE_BALANCE_TINT_DUV);
|
||||
}
|
||||
auto const xy{uv_to_xy(uv)};
|
||||
glm::dvec3 const xyz{xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y};
|
||||
return glm::dvec3{0.8951 * xyz.x + 0.2664 * xyz.y - 0.1614 * xyz.z,
|
||||
-0.7502 * xyz.x + 1.7135 * xyz.y + 0.0367 * xyz.z,
|
||||
0.0389 * xyz.x - 0.0685 * xyz.y + 1.0296 * xyz.z};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -77,6 +122,15 @@ glm::vec4 exposure_settings::tonemap_params(std::size_t const Bloomlevels)
|
||||
return glm::vec4{bloom, Global.gfx_postfx_autoexposure ? 1.f : 0.f, std::exp2(Global.gfx_postfx_autoexposure_compensation), Bloomlevels > 0 ? 1.f / static_cast<float>(Bloomlevels) : 0.f};
|
||||
}
|
||||
|
||||
glm::vec3 exposure_settings::white_balance()
|
||||
{
|
||||
if (!Global.gfx_postfx_whitebalance)
|
||||
return glm::vec3{0.f};
|
||||
// von kries: the light of the set temperature becomes the white of the display; the luminance of grey stays
|
||||
auto const gains{white_cone_response(WHITE_BALANCE_REFERENCE, 0.0) / white_cone_response(Global.gfx_postfx_whitebalance_temperature, Global.gfx_postfx_whitebalance_tint)};
|
||||
return glm::vec3{gains};
|
||||
}
|
||||
|
||||
void exposure_settings::draw_debug_ui(float const *Readback)
|
||||
{
|
||||
ImGui::Checkbox("automatic exposure (gfx.postfx.autoexposure)", &Global.gfx_postfx_autoexposure);
|
||||
@@ -90,6 +144,9 @@ void exposure_settings::draw_debug_ui(float const *Readback)
|
||||
ImGui::Checkbox("bloom (gfx.postfx.bloom)", &Global.gfx_postfx_bloom);
|
||||
ImGui::SliderFloat("bloom intensity", &Global.gfx_postfx_bloom_intensity, 0.f, 0.5f);
|
||||
ImGui::SliderFloat("bloom threshold", &Global.gfx_postfx_bloom_threshold, 0.f, 10.f);
|
||||
ImGui::Checkbox("white balance (gfx.postfx.whitebalance)", &Global.gfx_postfx_whitebalance);
|
||||
ImGui::SliderFloat("temperature K, 6500 = none, more = warmer", &Global.gfx_postfx_whitebalance_temperature, 4000.f, 12000.f, "%.0f");
|
||||
ImGui::SliderFloat("tint, + magenta, - green", &Global.gfx_postfx_whitebalance_tint, -1.f, 1.f, "%.2f");
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
@@ -37,6 +37,8 @@ struct exposure_settings
|
||||
static glm::vec4 bloom_params(std::size_t const Level);
|
||||
// parameters of the tonemapping (postfx_tonemapping.frag), with bloom from a chain of Bloomlevels levels (0: none)
|
||||
static glm::vec4 tonemap_params(std::size_t const Bloomlevels);
|
||||
// white balance of the tonemapping (gfx.postfx.whitebalance*): gains of the cone responses (bradford), 0 when off
|
||||
static glm::vec3 white_balance();
|
||||
// controls of the debug window; Readback: the adapted luminance, the exposure and the average luminance the gpu worked out
|
||||
static void draw_debug_ui(float const *Readback);
|
||||
};
|
||||
|
||||
@@ -132,6 +132,11 @@ glm::vec4 opengl46_exposure::tonemap_params(std::size_t const Bloomlevels) const
|
||||
return gfx::exposure_settings::tonemap_params(Bloomlevels);
|
||||
}
|
||||
|
||||
glm::vec3 opengl46_exposure::white_balance() const
|
||||
{
|
||||
return gfx::exposure_settings::white_balance();
|
||||
}
|
||||
|
||||
std::string opengl46_exposure::info_times()
|
||||
{
|
||||
std::string text;
|
||||
|
||||
@@ -60,6 +60,8 @@ class opengl46_exposure
|
||||
opengl_texture *bloom(bloom_chain &Chain, opengl_texture &Image, gl::stream_ubo &Modelubo, gl::model_ubs &Model);
|
||||
// parameters of the tonemapping (postfx_tonemapping.frag), with bloom from a chain of Bloomlevels levels (0: none)
|
||||
glm::vec4 tonemap_params(std::size_t const Bloomlevels) const;
|
||||
// gains of the white balance of the tonemapping (gfx.postfx.whitebalance*), 0 when off
|
||||
glm::vec3 white_balance() const;
|
||||
// lines for the gpu times of the debug panel
|
||||
std::string info_times();
|
||||
// controls of the debug window
|
||||
|
||||
@@ -2055,7 +2055,7 @@ if (!Global.gfx_skippipeline)
|
||||
}
|
||||
model_ubs.param[0] = m_exposure.tonemap_params(bloomlevels);
|
||||
// the dither goes to the last pass before the window: the chromatic aberration, when it follows
|
||||
model_ubs.param[1] = glm::vec4{Global.gfx_postfx_chromaticaberration_enabled ? 0.f : 1.f, 0.f, 0.f, 0.f};
|
||||
model_ubs.param[1] = glm::vec4{Global.gfx_postfx_chromaticaberration_enabled ? 0.f : 1.f, m_exposure.white_balance()};
|
||||
model_ubo->update(model_ubs);
|
||||
std::vector<opengl_texture *> tonemapinput{vp.main2_tex.get()};
|
||||
if (bloomtexture != nullptr)
|
||||
@@ -5864,8 +5864,9 @@ bool opengl46_renderer::Render_Preview(TAnimModel *Instance, int const Size, int
|
||||
glViewport(0, 0, targetsize, targetsize);
|
||||
if (!Global.gfx_shadergamma)
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
// no bloom, no automatic exposure
|
||||
// no bloom, no automatic exposure, no white balance
|
||||
model_ubs.param[0] = glm::vec4{0.f, 0.f, 1.f, 0.f};
|
||||
model_ubs.param[1].y = 0.f;
|
||||
model_ubo->update(model_ubs);
|
||||
m_pfx_tonemapping->apply(*m_preview.hdr_tex, m_preview.ldr_fb.get());
|
||||
if (!Global.gfx_shadergamma)
|
||||
|
||||
@@ -3544,7 +3544,7 @@ void vulkan14_renderer::render_post(std::size_t const Index, bool const Motionbl
|
||||
vk14::draw_constants tonemap;
|
||||
tonemap.data[0] = gfx::exposure_settings::tonemap_params(bloom != nullptr ? targets.bloom_sizes.size() : 0);
|
||||
// the dither goes to the last pass before the window: the chromatic aberration, when it follows
|
||||
tonemap.data[1] = glm::vec4{targets.aberration ? 0.f : 1.f, 0.f, 0.f, 0.f};
|
||||
tonemap.data[1] = glm::vec4{targets.aberration ? 0.f : 1.f, gfx::exposure_settings::white_balance()};
|
||||
std::vector<vk14::image *> const tonemapinput{hdr, (bloom != nullptr ? bloom : hdr)};
|
||||
if (targets.aberration && targets.tonemapped)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user