@@ -117,14 +117,16 @@ class DisplayInstance final : public Film::Instance {
117
117
luisa::unique_ptr<Film::Instance> _base;
118
118
luisa::unique_ptr<ImGuiWindow> _window;
119
119
Image<float > _framebuffer;
120
- Shader2D<int , bool , float3> _blit;
120
+ Shader2D<int , bool , float3, float3 > _blit;
121
121
Shader2D<Image<float >> _clear;
122
122
Clock _clock;
123
123
Stream *_stream{};
124
124
ImTextureID _background{};
125
+ mutable float3 _exposure{};
125
126
mutable double _last_frame_time{};
127
+ mutable float2 _white_balance{};
128
+ mutable float _brightness{};
126
129
mutable int _tone_mapping{};
127
- mutable float3 _exposure{};
128
130
mutable int _background_fit{};
129
131
mutable bool _link_rgb_exposure{true };
130
132
@@ -180,9 +182,6 @@ class DisplayInstance final : public Film::Instance {
180
182
// All values used to derive this implementation are sourced from Troy’s initial AgX implementation/OCIO config file available here:
181
183
// https://github.com/sobotka/AgX
182
184
183
- // 0: Default, 1: Golden, 2: Punchy
184
- #define AGX_LOOK 0
185
-
186
185
// Mean error^2: 1.85907662e-06
187
186
static Callable agxDefaultContrastApprox = [](Float3 x) noexcept {
188
187
auto x2 = x * x;
@@ -263,6 +262,11 @@ class DisplayInstance final : public Film::Instance {
263
262
color * 12 .92f ,
264
263
1 .055f * pow (color, 1 .f / 2 .4f ) - .055f );
265
264
}
265
+ [[nodiscard]] static auto _apply_white_balance (Expr<float3> rgb, Expr<float > brightness, Expr<float > temperature, Expr<float > tint) noexcept {
266
+ auto lab = cie_xyz_to_lab (linear_srgb_to_cie_xyz (rgb));
267
+ auto v = make_float3 (clamp (lab.x + brightness, 0 .f , 100 .f ), lab.y + tint, lab.z + temperature);
268
+ return cie_xyz_to_linear_srgb (lab_to_cie_xyz (v));
269
+ }
266
270
267
271
public:
268
272
DisplayInstance (const Pipeline &pipeline, const Display *film,
@@ -296,9 +300,13 @@ class DisplayInstance final : public Film::Instance {
296
300
.back_buffers = d->back_buffers ()});
297
301
_framebuffer = device.create_image <float >(_window->swapchain ().backend_storage (), size);
298
302
_background = reinterpret_cast <ImTextureID>(_window->register_texture (_framebuffer, TextureSampler::linear_point_zero ()));
299
- _blit = device.compile <2 >([base = _base.get (), &framebuffer = _framebuffer](Int tonemapping, Bool ldr, Float3 scale) noexcept {
303
+ _blit = device.compile <2 >([base = _base.get (), &framebuffer = _framebuffer](Int tonemapping, Bool ldr, Float3 scale, Float3 white_balance ) noexcept {
300
304
auto p = dispatch_id ().xy ();
301
305
auto color = base->read (p).average * scale;
306
+ $if (any (white_balance != 0 .f )) {
307
+ color = _apply_white_balance (color, white_balance.z , white_balance.x , white_balance.y );
308
+ };
309
+ color = max (color, 0 .f );
302
310
$switch (tonemapping) {
303
311
$case (static_cast <int >(Display::ToneMapping::NONE)) {};
304
312
$case (static_cast <int >(Display::ToneMapping::UNCHARTED2)) { color = _tone_mapping_uncharted2 (color); };
@@ -363,7 +371,7 @@ class DisplayInstance final : public Film::Instance {
363
371
auto scale = _link_rgb_exposure ? make_float3 (luisa::exp2 (_exposure.x )) : luisa::exp2 (_exposure);
364
372
auto is_ldr = _window->framebuffer ().storage () != PixelStorage::FLOAT4;
365
373
auto size = _framebuffer.size ();
366
- *_stream << _blit (_tone_mapping, is_ldr, scale).dispatch (size);
374
+ *_stream << _blit (_tone_mapping, is_ldr, scale, make_float3 (_white_balance, _brightness) ).dispatch (size);
367
375
auto viewport = ImGui::GetMainViewport ();
368
376
auto bg_size = _compute_background_size (viewport, _background_fit);
369
377
auto p_min = make_float2 (viewport->Pos .x , viewport->Pos .y ) +
@@ -373,16 +381,30 @@ class DisplayInstance final : public Film::Instance {
373
381
ImVec2{p_min.x + bg_size.x , p_min.y + bg_size.y });
374
382
ImGui::Begin (" Console" , nullptr , ImGuiWindowFlags_AlwaysAutoResize);
375
383
{
376
- ImGui::Text (" Display FPS: %.2f" , ImGui::GetIO ().Framerate );
384
+ ImGui::Text (" Render: %ux%u" , node ()->resolution ().x , node ()->resolution ().y );
385
+ ImGui::Text (" Display: %ux%u (%.2ffps)" , size.x , size.y , ImGui::GetIO ().Framerate );
386
+ // Exposure
387
+ if (ImGui::Button (" Reset##Exposure" )) { _exposure = make_float3 (); }
388
+ ImGui::SameLine ();
377
389
if (_link_rgb_exposure) {
378
390
ImGui::SliderFloat (" Exposure" , &_exposure.x , -10 .f , 10 .f );
379
391
} else {
380
392
ImGui::SliderFloat3 (" Exposure" , &_exposure.x , -10 .f , 10 .f );
381
393
}
382
394
ImGui::SameLine ();
383
395
ImGui::Checkbox (" Link" , &_link_rgb_exposure);
396
+ // Brightness
397
+ if (ImGui::Button (" Reset##Brightness" )) { _brightness = 0 .f ; }
398
+ ImGui::SameLine ();
399
+ ImGui::SliderFloat (" Brightness" , &_brightness, -100 .f , 100 .f );
400
+ // Temperature
401
+ if (ImGui::Button (" Reset##Temperature" )) { _white_balance.x = 0 .f ; }
402
+ ImGui::SameLine ();
403
+ ImGui::SliderFloat (" Temperature" , &_white_balance.x , -100 .f , 100 .f );
404
+ // Tint
405
+ if (ImGui::Button (" Reset##Tint" )) { _white_balance.y = 0 .f ; }
384
406
ImGui::SameLine ();
385
- if ( ImGui::Button ( " Reset " )) { _exposure = make_float3 (); }
407
+ ImGui::SliderFloat ( " Tint " , &_white_balance. y , - 100 . f , 100 . f );
386
408
constexpr const char *const tone_mapping_names[] = {" None" , " Uncharted2" , " ACES" , " AgX" , " AgX (Golden)" , " AgX (Punchy)" };
387
409
ImGui::Combo (" Tone Mapping" , &_tone_mapping, tone_mapping_names, std::size (tone_mapping_names));
388
410
constexpr const char *const fit_names[] = {" Fill" , " Fit" , " Stretch" };
0 commit comments