Skip to content

Commit

Permalink
add white balance adjustments in display
Browse files Browse the repository at this point in the history
  • Loading branch information
Mike-Leo-Smith committed Jun 3, 2024
1 parent 76324cc commit b5492d0
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 19 deletions.
40 changes: 31 additions & 9 deletions src/films/display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,16 @@ class DisplayInstance final : public Film::Instance {
luisa::unique_ptr<Film::Instance> _base;
luisa::unique_ptr<ImGuiWindow> _window;
Image<float> _framebuffer;
Shader2D<int, bool, float3> _blit;
Shader2D<int, bool, float3, float3> _blit;
Shader2D<Image<float>> _clear;
Clock _clock;
Stream *_stream{};
ImTextureID _background{};
mutable float3 _exposure{};
mutable double _last_frame_time{};
mutable float2 _white_balance{};
mutable float _brightness{};
mutable int _tone_mapping{};
mutable float3 _exposure{};
mutable int _background_fit{};
mutable bool _link_rgb_exposure{true};

Expand Down Expand Up @@ -180,9 +182,6 @@ class DisplayInstance final : public Film::Instance {
// All values used to derive this implementation are sourced from Troy’s initial AgX implementation/OCIO config file available here:
// https://github.com/sobotka/AgX

// 0: Default, 1: Golden, 2: Punchy
#define AGX_LOOK 0

// Mean error^2: 1.85907662e-06
static Callable agxDefaultContrastApprox = [](Float3 x) noexcept {
auto x2 = x * x;
Expand Down Expand Up @@ -263,6 +262,11 @@ class DisplayInstance final : public Film::Instance {
color * 12.92f,
1.055f * pow(color, 1.f / 2.4f) - .055f);
}
[[nodiscard]] static auto _apply_white_balance(Expr<float3> rgb, Expr<float> brightness, Expr<float> temperature, Expr<float> tint) noexcept {
auto lab = cie_xyz_to_lab(linear_srgb_to_cie_xyz(rgb));
auto v = make_float3(clamp(lab.x + brightness, 0.f, 100.f), lab.y + tint, lab.z + temperature);
return cie_xyz_to_linear_srgb(lab_to_cie_xyz(v));
}

public:
DisplayInstance(const Pipeline &pipeline, const Display *film,
Expand Down Expand Up @@ -296,9 +300,13 @@ class DisplayInstance final : public Film::Instance {
.back_buffers = d->back_buffers()});
_framebuffer = device.create_image<float>(_window->swapchain().backend_storage(), size);
_background = reinterpret_cast<ImTextureID>(_window->register_texture(_framebuffer, TextureSampler::linear_point_zero()));
_blit = device.compile<2>([base = _base.get(), &framebuffer = _framebuffer](Int tonemapping, Bool ldr, Float3 scale) noexcept {
_blit = device.compile<2>([base = _base.get(), &framebuffer = _framebuffer](Int tonemapping, Bool ldr, Float3 scale, Float3 white_balance) noexcept {
auto p = dispatch_id().xy();
auto color = base->read(p).average * scale;
$if (any(white_balance != 0.f)) {
color = _apply_white_balance(color, white_balance.z, white_balance.x, white_balance.y);
};
color = max(color, 0.f);
$switch (tonemapping) {
$case (static_cast<int>(Display::ToneMapping::NONE)) {};
$case (static_cast<int>(Display::ToneMapping::UNCHARTED2)) { color = _tone_mapping_uncharted2(color); };
Expand Down Expand Up @@ -363,7 +371,7 @@ class DisplayInstance final : public Film::Instance {
auto scale = _link_rgb_exposure ? make_float3(luisa::exp2(_exposure.x)) : luisa::exp2(_exposure);
auto is_ldr = _window->framebuffer().storage() != PixelStorage::FLOAT4;
auto size = _framebuffer.size();
*_stream << _blit(_tone_mapping, is_ldr, scale).dispatch(size);
*_stream << _blit(_tone_mapping, is_ldr, scale, make_float3(_white_balance, _brightness)).dispatch(size);
auto viewport = ImGui::GetMainViewport();
auto bg_size = _compute_background_size(viewport, _background_fit);
auto p_min = make_float2(viewport->Pos.x, viewport->Pos.y) +
Expand All @@ -373,16 +381,30 @@ class DisplayInstance final : public Film::Instance {
ImVec2{p_min.x + bg_size.x, p_min.y + bg_size.y});
ImGui::Begin("Console", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
{
ImGui::Text("Display FPS: %.2f", ImGui::GetIO().Framerate);
ImGui::Text("Render: %ux%u", node()->resolution().x, node()->resolution().y);
ImGui::Text("Display: %ux%u (%.2ffps)", size.x, size.y, ImGui::GetIO().Framerate);
// Exposure
if (ImGui::Button("Reset##Exposure")) { _exposure = make_float3(); }
ImGui::SameLine();
if (_link_rgb_exposure) {
ImGui::SliderFloat("Exposure", &_exposure.x, -10.f, 10.f);
} else {
ImGui::SliderFloat3("Exposure", &_exposure.x, -10.f, 10.f);
}
ImGui::SameLine();
ImGui::Checkbox("Link", &_link_rgb_exposure);
// Brightness
if (ImGui::Button("Reset##Brightness")) { _brightness = 0.f; }
ImGui::SameLine();
ImGui::SliderFloat("Brightness", &_brightness, -100.f, 100.f);
// Temperature
if (ImGui::Button("Reset##Temperature")) { _white_balance.x = 0.f; }
ImGui::SameLine();
ImGui::SliderFloat("Temperature", &_white_balance.x, -100.f, 100.f);
// Tint
if (ImGui::Button("Reset##Tint")) { _white_balance.y = 0.f; }
ImGui::SameLine();
if (ImGui::Button("Reset")) { _exposure = make_float3(); }
ImGui::SliderFloat("Tint", &_white_balance.y, -100.f, 100.f);
constexpr const char *const tone_mapping_names[] = {"None", "Uncharted2", "ACES", "AgX", "AgX (Golden)", "AgX (Punchy)"};
ImGui::Combo("Tone Mapping", &_tone_mapping, tone_mapping_names, std::size(tone_mapping_names));
constexpr const char *const fit_names[] = {"Fill", "Fit", "Stretch"};
Expand Down
6 changes: 3 additions & 3 deletions src/spectra/hero.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@ class RGB2SpectrumTable {
};
return c;
};
return make_float4(decode(Expr{array}, base_index, rgb), srgb_to_cie_y(rgb));
return make_float4(decode(Expr{array}, base_index, rgb), linear_srgb_to_cie_y(rgb));
}

[[nodiscard]] float4 decode_albedo(float3 rgb_in) const noexcept {
auto rgb = clamp(rgb_in, 0.0f, 1.0f);
if (rgb[0] == rgb[1] && rgb[1] == rgb[2]) {
auto s = (rgb[0] - 0.5f) / std::sqrt(rgb[0] * (1.0f - rgb[0]));
return make_float4(0.0f, 0.0f, s, srgb_to_cie_y(rgb));
return make_float4(0.0f, 0.0f, s, linear_srgb_to_cie_y(rgb));
}
// Find maximum component and compute remapped component values
auto maxc = (rgb[0] > rgb[1]) ?
Expand Down Expand Up @@ -124,7 +124,7 @@ class RGB2SpectrumTable {
lerp(co(0, 1, 1), co(1, 1, 1), dx), dy),
dz);
}
return make_float4(c, srgb_to_cie_y(rgb));
return make_float4(c, linear_srgb_to_cie_y(rgb));
}

[[nodiscard]] Float4 decode_unbounded(
Expand Down
10 changes: 5 additions & 5 deletions src/spectra/srgb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,31 @@ struct SRGBSpectrumInstance final : public Spectrum::Instance {
SampledSpectrum s{node()->dimension()};
auto sv = saturate(v.xyz());
for (auto i = 0u; i < 3u; i++) { s[i] = sv[i]; }
return {.value = s, .strength = srgb_to_cie_y(sv)};
return {.value = s, .strength = linear_srgb_to_cie_y(sv)};
}
[[nodiscard]] Spectrum::Decode decode_unbounded(
const SampledWavelengths &swl, Expr<float4> v) const noexcept override {
SampledSpectrum s{node()->dimension()};
auto sv = v.xyz();
for (auto i = 0u; i < 3u; i++) { s[i] = sv[i]; }
return {.value = s, .strength = srgb_to_cie_y(sv)};
return {.value = s, .strength = linear_srgb_to_cie_y(sv)};
}
[[nodiscard]] Spectrum::Decode decode_illuminant(
const SampledWavelengths &swl, Expr<float4> v) const noexcept override {
auto sv = max(v.xyz(), 0.f);
SampledSpectrum s{node()->dimension()};
for (auto i = 0u; i < 3u; i++) { s[i] = sv[i]; }
return {.value = s, .strength = srgb_to_cie_y(sv)};
return {.value = s, .strength = linear_srgb_to_cie_y(sv)};
}
[[nodiscard]] Float cie_y(
const SampledWavelengths &swl,
const SampledSpectrum &sp) const noexcept override {
return srgb_to_cie_y(srgb(swl, sp));
return linear_srgb_to_cie_y(srgb(swl, sp));
}
[[nodiscard]] Float3 cie_xyz(
const SampledWavelengths &swl,
const SampledSpectrum &sp) const noexcept override {
return srgb_to_cie_xyz(srgb(swl, sp));
return linear_srgb_to_cie_xyz(srgb(swl, sp));
}
[[nodiscard]] Float3 srgb(
const SampledWavelengths &swl,
Expand Down
41 changes: 39 additions & 2 deletions src/util/colorspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,55 @@ template<typename T>
}

template<typename T>
[[nodiscard]] inline auto srgb_to_cie_y(T &&rgb) noexcept {
[[nodiscard]] inline auto linear_srgb_to_cie_y(T &&rgb) noexcept {
constexpr auto m = make_float3(0.212671f, 0.715160f, 0.072169f);
return dot(m, std::forward<T>(rgb));
}

template<typename T>
[[nodiscard]] inline auto srgb_to_cie_xyz(T &&rgb) noexcept {
[[nodiscard]] inline auto linear_srgb_to_cie_xyz(T &&rgb) noexcept {
constexpr auto m = make_float3x3(
0.412453f, 0.212671f, 0.019334f,
0.357580f, 0.715160f, 0.119193f,
0.180423f, 0.072169f, 0.950227f);
return m * std::forward<T>(rgb);
}

template<typename T>
[[nodiscard]] inline auto cie_xyz_to_lab(T &&xyz) noexcept {
static constexpr auto delta = 6.f / 29.f;
auto f = [](const auto &x) noexcept {
constexpr auto d3 = delta * delta * delta;
constexpr auto one_over_3d2 = 1.f / (3.f * delta * delta);
if constexpr (compute::is_dsl_v<T>) {
return compute::ite(x > d3, compute::pow(x, 1.f / 3.f), one_over_3d2 * x + (4.f / 29.f));
} else {
return x > d3 ? std::pow(x, 1.f / 3.f) : one_over_3d2 * x + 4.f / 29.f;
}
};
auto f_y_yn = f(xyz.y);
auto l = 116.f * f_y_yn - 16.f;
auto a = 500.f * (f(xyz.x / .950489f) - f_y_yn);
auto b = 200.f * (f_y_yn - f(xyz.z / 1.08884f));
return make_float3(l, a, b);
}

template<typename T>
[[nodiscard]] inline auto lab_to_cie_xyz(T &&lab) noexcept {
static constexpr auto delta = 6.f / 29.f;
auto f = [](const auto &x) noexcept {
constexpr auto three_dd = 3.f * delta * delta;
if constexpr (compute::is_dsl_v<T>) {
return compute::ite(x > delta, x * x * x, three_dd * x - (three_dd * 4.f / 29.f));
} else {
return x > delta ? x * x * x : three_dd * x - three_dd * 4.f / 29.f;
}
};
auto v = (lab.x + 16.f) / 116.f;
auto x = .950489f * f(v + lab.y / 500.f);
auto y = f(v);
auto z = 1.08884f * f(v - lab.z / 200.f);
return make_float3(x, y, z);
}

}// namespace luisa::render

0 comments on commit b5492d0

Please sign in to comment.