From e1884ea0b4edc3f5bb5bc0cc11716b8e345c0a28 Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Sat, 30 May 2026 00:19:29 +0100 Subject: [PATCH] Add RGB Beer-Lambert correction. Allow disabling surface scatter adjustment --- src/libslic3r/ColorSolver.cpp | 157 ++++++++++++++++++-- src/libslic3r/ColorSolver.hpp | 12 +- src/libslic3r/Fill/Fill.cpp | 18 ++- src/libslic3r/TextureMapping.cpp | 14 +- src/libslic3r/TextureMapping.hpp | 8 +- src/libslic3r/TextureMappingContoning.cpp | 11 +- src/libslic3r/TextureMappingContoning.hpp | 1 + src/slic3r/GUI/MMUPaintedTexturePreview.cpp | 24 ++- src/slic3r/GUI/Plater.cpp | 70 ++++++++- 9 files changed, 288 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/ColorSolver.cpp b/src/libslic3r/ColorSolver.cpp index 8359f594c00..4c33cfe5218 100644 --- a/src/libslic3r/ColorSolver.cpp +++ b/src/libslic3r/ColorSolver.cpp @@ -52,6 +52,29 @@ float linear_to_srgb_component(float value) return x <= 0.0031308f ? 12.92f * x : 1.055f * std::pow(x, 1.f / 2.4f) - 0.055f; } +std::array srgb_to_linear_color(const std::array &rgb) +{ + return { + srgb_to_linear_component(rgb[0]), + srgb_to_linear_component(rgb[1]), + srgb_to_linear_component(rgb[2]) + }; +} + +std::array linear_to_srgb_color(const std::array &rgb) +{ + return { + linear_to_srgb_component(rgb[0]), + linear_to_srgb_component(rgb[1]), + linear_to_srgb_component(rgb[2]) + }; +} + +float linear_luminance(const std::array &rgb) +{ + return 0.2126f * rgb[0] + 0.7152f * rgb[1] + 0.0722f * rgb[2]; +} + std::array oklab_from_srgb(const std::array &rgb) { const float r = srgb_to_linear_component(rgb[0]); @@ -341,15 +364,110 @@ ColorSolverNearestResult nearest_color_solver_candidates_perceptual(const Candid return result; } +std::array beer_lambert_rgb_transmission(const std::array &srgb_color, float scalar_transmission) +{ + const float transmission = std::clamp(std::isfinite(scalar_transmission) ? scalar_transmission : 0.5f, 1e-4f, 0.9999f); + const std::array linear = srgb_to_linear_color(srgb_color); + const float min_channel = std::min({ linear[0], linear[1], linear[2] }); + const float max_channel = std::max({ linear[0], linear[1], linear[2] }); + const float saturation = max_channel > 1e-4f ? std::clamp((max_channel - min_channel) / max_channel, 0.f, 1.f) : 0.f; + const float strength = 0.75f * saturation; + if (strength <= 1e-4f) + return { transmission, transmission, transmission }; + + const float luminance = std::clamp(linear_luminance(linear), 0.02f, 0.98f); + std::array density_bias; + for (size_t channel = 0; channel < 3; ++channel) { + const float channel_level = std::max(linear[channel], 0.02f); + density_bias[channel] = std::pow(std::clamp(luminance / channel_level, 0.25f, 4.f), strength); + } + + auto weighted_transmission = [&density_bias, transmission](float scale) { + return 0.2126f * std::pow(transmission, density_bias[0] * scale) + + 0.7152f * std::pow(transmission, density_bias[1] * scale) + + 0.0722f * std::pow(transmission, density_bias[2] * scale); + }; + + float lo = 0.f; + float hi = 8.f; + while (weighted_transmission(hi) > transmission && hi < 256.f) + hi *= 2.f; + for (int step = 0; step < 24; ++step) { + const float mid = 0.5f * (lo + hi); + if (weighted_transmission(mid) > transmission) + lo = mid; + else + hi = mid; + } + const float scale = 0.5f * (lo + hi); + return { + std::pow(transmission, density_bias[0] * scale), + std::pow(transmission, density_bias[1] * scale), + std::pow(transmission, density_bias[2] * scale) + }; +} + +std::array mix_ordered_stack_beer_lambert_rgb(const std::vector> &colors_with_background, + const std::vector &surface_to_deep, + const std::vector &layer_opacities, + float surface_scatter) +{ + if (colors_with_background.empty() || surface_to_deep.empty()) + return colors_with_background.empty() ? std::array{ { 0.f, 0.f, 0.f } } : colors_with_background.back(); + + const size_t component_count = colors_with_background.size() - 1; + std::array accumulated { 0.f, 0.f, 0.f }; + std::array remaining { 1.f, 1.f, 1.f }; + bool surface_layer = true; + const float safe_surface_scatter = + std::clamp(std::isfinite(surface_scatter) ? surface_scatter : 0.f, 0.f, 0.5f); + for (uint16_t component_idx : surface_to_deep) { + if (size_t(component_idx) >= component_count) + continue; + const float opacity = + size_t(component_idx) < layer_opacities.size() && std::isfinite(layer_opacities[size_t(component_idx)]) ? + std::clamp(layer_opacities[size_t(component_idx)], 1e-4f, 0.9999f) : + 0.5f; + std::array transmission = + beer_lambert_rgb_transmission(colors_with_background[size_t(component_idx)], 1.f - opacity); + if (surface_layer) { + for (float &channel_transmission : transmission) { + const float channel_opacity = std::clamp(safe_surface_scatter + (1.f - safe_surface_scatter) * (1.f - channel_transmission), + 1e-4f, + 0.9999f); + channel_transmission = 1.f - channel_opacity; + } + surface_layer = false; + } + const std::array layer_linear = srgb_to_linear_color(colors_with_background[size_t(component_idx)]); + for (size_t channel = 0; channel < 3; ++channel) { + const float channel_opacity = 1.f - transmission[channel]; + accumulated[channel] += remaining[channel] * channel_opacity * layer_linear[channel]; + remaining[channel] *= transmission[channel]; + } + if (std::max({ remaining[0], remaining[1], remaining[2] }) <= 1e-5f) + break; + } + + const std::array background_linear = srgb_to_linear_color(colors_with_background.back()); + for (size_t channel = 0; channel < 3; ++channel) + accumulated[channel] += remaining[channel] * background_linear[channel]; + return linear_to_srgb_color(accumulated); +} + std::array mix_ordered_stack_with_buffers(const std::vector> &colors_with_background, std::vector &weights, const std::vector &surface_to_deep, const std::vector &layer_opacities, - float surface_scatter) + float surface_scatter, + bool beer_lambert_rgb_correction) { if (colors_with_background.empty() || surface_to_deep.empty()) return colors_with_background.empty() ? std::array{ { 0.f, 0.f, 0.f } } : colors_with_background.back(); + if (beer_lambert_rgb_correction) + return mix_ordered_stack_beer_lambert_rgb(colors_with_background, surface_to_deep, layer_opacities, surface_scatter); + const size_t component_count = colors_with_background.size() - 1; weights.assign(colors_with_background.size(), 0.f); float transmission = 1.f; @@ -542,7 +660,8 @@ void append_ordered_stack_candidate(ColorSolverOrderedStackCandidateSet &c const std::vector &surface_to_deep, const std::vector &layer_opacities, int simulated_stack_depth, - float surface_scatter) + float surface_scatter, + bool beer_lambert_rgb_correction) { std::vector simulated_surface_to_deep; const std::vector *mix_stack = &surface_to_deep; @@ -551,7 +670,7 @@ void append_ordered_stack_candidate(ColorSolverOrderedStackCandidateSet &c mix_stack = &simulated_surface_to_deep; } const std::array mixed = - mix_ordered_stack_with_buffers(colors_with_background, weights, *mix_stack, layer_opacities, surface_scatter); + mix_ordered_stack_with_buffers(colors_with_background, weights, *mix_stack, layer_opacities, surface_scatter, beer_lambert_rgb_correction); const std::array perceptual = oklab_from_srgb(mixed); candidates.rgbs.emplace_back(mixed[0]); candidates.rgbs.emplace_back(mixed[1]); @@ -625,7 +744,8 @@ std::array mix_color_solver_ordered_stack(const std::vector &layer_opacities, const std::array &background_rgb, ColorSolverMixModel mix_model, - float surface_scatter) + float surface_scatter, + bool beer_lambert_rgb_correction) { (void) mix_model; if (component_colors.empty() || surface_to_deep.empty()) @@ -634,7 +754,12 @@ std::array mix_color_solver_ordered_stack(const std::vector> colors = component_colors; colors.emplace_back(background_rgb); std::vector weights; - return mix_ordered_stack_with_buffers(colors, weights, surface_to_deep, layer_opacities, surface_scatter); + return mix_ordered_stack_with_buffers(colors, + weights, + surface_to_deep, + layer_opacities, + surface_scatter, + beer_lambert_rgb_correction); } std::array color_solver_oklab_from_srgb(const std::array &rgb) @@ -768,7 +893,8 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vectorsecond; @@ -947,7 +1079,8 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates simulated_stack_depth, candidate_limit, stack_item_limit, - surface_scatter)).first->second; + surface_scatter, + beer_lambert_rgb_correction)).first->second; } std::vector solve_color_solver_ordered_stack_for_target( diff --git a/src/libslic3r/ColorSolver.hpp b/src/libslic3r/ColorSolver.hpp index b9d2ae1d2f0..725bd1d8ce4 100644 --- a/src/libslic3r/ColorSolver.hpp +++ b/src/libslic3r/ColorSolver.hpp @@ -110,7 +110,8 @@ std::array mix_color_solver_ordered_stack(const std::vector &layer_opacities, const std::array &background_rgb, ColorSolverMixModel mix_model, - float surface_scatter = 0.f); + float surface_scatter = 0.f, + bool beer_lambert_rgb_correction = false); std::array color_solver_oklab_from_srgb(const std::array &rgb); std::array color_solver_srgb_from_oklab(const std::array &oklab); @@ -136,7 +137,8 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector> &component_colors, const std::vector &layer_opacities, @@ -146,7 +148,8 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates( int simulated_stack_depth = 0, size_t candidate_limit = 0, size_t stack_item_limit = 0, - float surface_scatter = 0.f); + float surface_scatter = 0.f, + bool beer_lambert_rgb_correction = false); const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates( ColorSolverOrderedStackCandidateCache &cache, const std::vector> &component_colors, @@ -157,7 +160,8 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates int simulated_stack_depth = 0, size_t candidate_limit = 0, size_t stack_item_limit = 0, - float surface_scatter = 0.f); + float surface_scatter = 0.f, + bool beer_lambert_rgb_correction = false); std::vector solve_color_solver_ordered_stack_for_target( const ColorSolverOrderedStackCandidateSet &candidates, const std::array &target_rgb, diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index fccc5fd15bc..0cb18297265 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -505,6 +505,8 @@ struct TopSurfaceImageRegionPlan { int contoning_polygonize_resolution = TextureMappingZone::DefaultTopSurfaceContoningPolygonizeResolution; bool contoning_surface_anchored_stacks_enabled = false; bool contoning_td_adjustment_enabled = TextureMappingZone::DefaultTopSurfaceContoningTdAdjustmentEnabled; + bool contoning_surface_scatter_enabled = TextureMappingZone::DefaultTopSurfaceContoningSurfaceScatterEnabled; + bool contoning_beer_lambert_rgb_correction_enabled = TextureMappingZone::DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled; int contoning_flat_surface_infill_mode = TextureMappingZone::SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode; }; @@ -1169,6 +1171,8 @@ struct TopSurfaceImageContoningStackPlanKey { bool polygonize { false }; int polygonize_resolution { 1 }; bool td_adjustment { false }; + bool surface_scatter { false }; + bool beer_lambert_rgb_correction { false }; bool operator<(const TopSurfaceImageContoningStackPlanKey &rhs) const { @@ -1196,7 +1200,9 @@ struct TopSurfaceImageContoningStackPlanKey { blue_noise, polygonize, polygonize_resolution, - td_adjustment) < + td_adjustment, + surface_scatter, + beer_lambert_rgb_correction) < std::tie(rhs.source_layer, rhs.source_layer_id, rhs.target_layer, @@ -1221,7 +1227,9 @@ struct TopSurfaceImageContoningStackPlanKey { rhs.blue_noise, rhs.polygonize, rhs.polygonize_resolution, - rhs.td_adjustment); + rhs.td_adjustment, + rhs.surface_scatter, + rhs.beer_lambert_rgb_correction); } }; @@ -2622,6 +2630,8 @@ static TopSurfaceImageContoningStackPlanKey top_surface_image_contoning_stack_pl TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(plan.contoning_polygonize_resolution) : 1; key.td_adjustment = plan.contoning_td_adjustment_enabled; + key.surface_scatter = plan.contoning_surface_scatter_enabled; + key.beer_lambert_rgb_correction = plan.contoning_beer_lambert_rgb_correction_enabled; return key; } @@ -3285,6 +3295,10 @@ static std::vector top_surface_image_region_plans( plan.contoning_surface_anchored_stacks_enabled = zone->effective_top_surface_contoning_surface_anchored_stacks_enabled(); plan.contoning_td_adjustment_enabled = zone->top_surface_contoning_td_adjustment_enabled; + plan.contoning_surface_scatter_enabled = + plan.contoning_td_adjustment_enabled && zone->top_surface_contoning_surface_scatter_enabled; + plan.contoning_beer_lambert_rgb_correction_enabled = + plan.contoning_td_adjustment_enabled && zone->top_surface_contoning_beer_lambert_rgb_correction_enabled; const TextureMappingContoningSolver contoning_solver(*zone, print_config, components, float(layer.height)); const int stack_depth = plan.contoning ? diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index 89d9701a224..1a79d26b68d 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -1149,9 +1149,11 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const rhs.effective_top_surface_contoning_supersampled_cells_enabled() && top_surface_contoning_polygonize_color_regions_enabled == rhs.top_surface_contoning_polygonize_color_regions_enabled && TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(top_surface_contoning_polygonize_resolution) == - TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(rhs.top_surface_contoning_polygonize_resolution) && + TextureMappingZone::normalize_top_surface_contoning_polygonize_resolution(rhs.top_surface_contoning_polygonize_resolution) && effective_top_surface_contoning_surface_anchored_stacks_enabled() == rhs.effective_top_surface_contoning_surface_anchored_stacks_enabled() && top_surface_contoning_td_adjustment_enabled == rhs.top_surface_contoning_td_adjustment_enabled && + top_surface_contoning_surface_scatter_enabled == rhs.top_surface_contoning_surface_scatter_enabled && + top_surface_contoning_beer_lambert_rgb_correction_enabled == rhs.top_surface_contoning_beer_lambert_rgb_correction_enabled && compact_offset_mode == rhs.compact_offset_mode && use_legacy_fixed_color_mode == rhs.use_legacy_fixed_color_mode && high_speed_image_texture_sampling == rhs.high_speed_image_texture_sampling && @@ -1556,6 +1558,10 @@ std::string TextureMappingManager::serialize_entries() zone.effective_top_surface_contoning_surface_anchored_stacks_enabled(); texture["top_surface_contoning_td_adjustment_enabled"] = zone.top_surface_contoning_td_adjustment_enabled; + texture["top_surface_contoning_surface_scatter_enabled"] = + zone.top_surface_contoning_surface_scatter_enabled; + texture["top_surface_contoning_beer_lambert_rgb_correction_enabled"] = + zone.top_surface_contoning_beer_lambert_rgb_correction_enabled; texture["compact_offset_mode"] = zone.compact_offset_mode; texture["use_legacy_fixed_color_mode"] = zone.use_legacy_fixed_color_mode; texture["high_speed_image_texture_sampling"] = true; @@ -1858,6 +1864,12 @@ void TextureMappingManager::load_entries(const std::string &serialized, zone.top_surface_contoning_td_adjustment_enabled = texture.value("top_surface_contoning_td_adjustment_enabled", TextureMappingZone::DefaultTopSurfaceContoningTdAdjustmentEnabled); + zone.top_surface_contoning_surface_scatter_enabled = + texture.value("top_surface_contoning_surface_scatter_enabled", + TextureMappingZone::DefaultTopSurfaceContoningSurfaceScatterEnabled); + zone.top_surface_contoning_beer_lambert_rgb_correction_enabled = + texture.value("top_surface_contoning_beer_lambert_rgb_correction_enabled", + TextureMappingZone::DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled); zone.apply_top_surface_contoning_experimental_defaults(); zone.compact_offset_mode = texture.value("compact_offset_mode", TextureMappingZone::DefaultCompactOffsetMode); zone.use_legacy_fixed_color_mode = diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index 8561ec3a3ec..df761621730 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -185,7 +185,7 @@ struct TextureMappingZone static constexpr int DefaultTopSurfaceContoningStackLayers = 100; static constexpr int MinTopSurfaceContoningPatternFilaments = 1; static constexpr int MaxTopSurfaceContoningPatternFilaments = 100; - static constexpr int DefaultTopSurfaceContoningPatternFilaments = 5; + static constexpr int DefaultTopSurfaceContoningPatternFilaments = 8; static constexpr float MinTopSurfaceContoningMinFeatureMm = 0.f; static constexpr float MaxTopSurfaceContoningMinFeatureMm = 20.f; static constexpr float DefaultTopSurfaceContoningMinFeatureMm = 0.f; @@ -205,6 +205,8 @@ struct TextureMappingZone static constexpr int DefaultTopSurfaceContoningPolygonizeResolution = 4; static constexpr bool DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled = false; static constexpr bool DefaultTopSurfaceContoningTdAdjustmentEnabled = true; + static constexpr bool DefaultTopSurfaceContoningSurfaceScatterEnabled = true; + static constexpr bool DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled = false; static constexpr bool DefaultCompactOffsetMode = true; static constexpr bool DefaultUseLegacyFixedColorMode = false; static constexpr bool DefaultHighSpeedImageTextureSampling = true; @@ -384,6 +386,8 @@ struct TextureMappingZone int top_surface_contoning_polygonize_resolution = DefaultTopSurfaceContoningPolygonizeResolution; bool top_surface_contoning_surface_anchored_stacks_enabled = DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled; bool top_surface_contoning_td_adjustment_enabled = DefaultTopSurfaceContoningTdAdjustmentEnabled; + bool top_surface_contoning_surface_scatter_enabled = DefaultTopSurfaceContoningSurfaceScatterEnabled; + bool top_surface_contoning_beer_lambert_rgb_correction_enabled = DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled; bool compact_offset_mode = DefaultCompactOffsetMode; bool use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode; bool high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling; @@ -604,6 +608,8 @@ struct TextureMappingZone top_surface_contoning_polygonize_resolution = DefaultTopSurfaceContoningPolygonizeResolution; top_surface_contoning_surface_anchored_stacks_enabled = DefaultTopSurfaceContoningSurfaceAnchoredStacksEnabled; top_surface_contoning_td_adjustment_enabled = DefaultTopSurfaceContoningTdAdjustmentEnabled; + top_surface_contoning_surface_scatter_enabled = DefaultTopSurfaceContoningSurfaceScatterEnabled; + top_surface_contoning_beer_lambert_rgb_correction_enabled = DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled; compact_offset_mode = DefaultCompactOffsetMode; use_legacy_fixed_color_mode = DefaultUseLegacyFixedColorMode; high_speed_image_texture_sampling = DefaultHighSpeedImageTextureSampling; diff --git a/src/libslic3r/TextureMappingContoning.cpp b/src/libslic3r/TextureMappingContoning.cpp index 33edd7de683..422dd446665 100644 --- a/src/libslic3r/TextureMappingContoning.cpp +++ b/src/libslic3r/TextureMappingContoning.cpp @@ -352,10 +352,13 @@ TextureMappingContoningSolver::TextureMappingContoningSolver(const TextureMappin float layer_height_mm) { m_td_adjustment_enabled = zone.top_surface_contoning_td_adjustment_enabled; + m_beer_lambert_rgb_correction_enabled = + m_td_adjustment_enabled && zone.top_surface_contoning_beer_lambert_rgb_correction_enabled; m_layer_height_mm = std::isfinite(layer_height_mm) && layer_height_mm > 0.f ? layer_height_mm : 0.2f; if (!std::isfinite(m_layer_height_mm) || m_layer_height_mm <= 0.f) m_layer_height_mm = 0.2f; - m_surface_scatter = CONTONING_SURFACE_SCATTER; + m_surface_scatter = + m_td_adjustment_enabled && zone.top_surface_contoning_surface_scatter_enabled ? CONTONING_SURFACE_SCATTER : 0.f; component_ids.erase(std::remove_if(component_ids.begin(), component_ids.end(), [&config](unsigned int id) { return id == 0 || id > config.filament_colour.values.size(); @@ -483,7 +486,8 @@ std::optional> TextureMappingContoningSolver::stack_rgb( m_component_layer_opacity, m_background_rgb, ColorSolverMixModel::PigmentPainter, - m_surface_scatter); + m_surface_scatter, + m_beer_lambert_rgb_correction_enabled); } void TextureMappingContoningSolver::arrange_stack_for_light_path(std::vector &bottom_to_top, @@ -583,7 +587,8 @@ TextureMappingContoningStack TextureMappingContoningSolver::solve(const std::arr visible_depth, MAX_TD_ORDERED_CONTONING_CANDIDATES, MAX_TD_ORDERED_CONTONING_STACK_ITEMS, - m_surface_scatter); + m_surface_scatter, + m_beer_lambert_rgb_correction_enabled); } const std::vector surface_to_deep = solve_color_solver_ordered_stack_for_target(*ordered_candidates, target_rgb, ColorSolverMode::V2); diff --git a/src/libslic3r/TextureMappingContoning.hpp b/src/libslic3r/TextureMappingContoning.hpp index 311dab4b8c9..2229867303a 100644 --- a/src/libslic3r/TextureMappingContoning.hpp +++ b/src/libslic3r/TextureMappingContoning.hpp @@ -67,6 +67,7 @@ private: float m_layer_height_mm { 0.2f }; float m_surface_scatter { 0.f }; bool m_td_adjustment_enabled { false }; + bool m_beer_lambert_rgb_correction_enabled { false }; mutable std::map> m_candidates_by_depth; mutable std::shared_ptr m_ordered_candidate_cache { std::make_shared() }; mutable std::shared_ptr m_ordered_candidate_cache_mutex { std::make_shared() }; diff --git a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp index 66383990775..59ae3e3ffbb 100644 --- a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp +++ b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp @@ -95,6 +95,7 @@ struct TexturePreviewSimulationSettings bool contoning_flat_surface_quantization = false; bool contoning_flat_surface_pattern_blend = false; bool contoning_flat_surface_td_adjustment = false; + bool contoning_flat_surface_beer_lambert_rgb_correction = false; int contoning_flat_surface_pattern_filaments = TextureMappingZone::DefaultTopSurfaceContoningPatternFilaments; bool simulate_top_surface_lod = false; float top_surface_lod_pitch_mm = 0.f; @@ -2510,7 +2511,8 @@ std::array contoning_flat_surface_rgb_for_texture_preview( settings.contoning_flat_surface_layer_opacities, settings.contoning_flat_surface_background_rgb, ColorSolverMixModel::PigmentPainter, - settings.contoning_flat_surface_surface_scatter); + settings.contoning_flat_surface_surface_scatter, + settings.contoning_flat_surface_beer_lambert_rgb_correction); } } if (!candidates.empty()) { @@ -2663,6 +2665,14 @@ std::optional texture_preview_simulation_setti settings.contoning_flat_surface_pattern_filaments = std::max(1, std::min(stack_layers, pattern_filaments)); settings.contoning_flat_surface_td_adjustment = zone->top_surface_contoning_active() && zone->top_surface_contoning_td_adjustment_enabled; + settings.contoning_flat_surface_beer_lambert_rgb_correction = + settings.contoning_flat_surface_td_adjustment && + zone->top_surface_contoning_beer_lambert_rgb_correction_enabled; + settings.contoning_flat_surface_surface_scatter = + settings.contoning_flat_surface_td_adjustment && + zone->top_surface_contoning_surface_scatter_enabled ? + k_contoning_preview_surface_scatter : + 0.f; } settings.minimum_visibility_offset_factor = zone->minimum_visibility_offset_enabled ? std::clamp((std::isfinite(zone->minimum_visibility_offset_pct) ? @@ -2712,8 +2722,10 @@ std::optional texture_preview_simulation_setti mix_color_solver_components(settings.component_colors, background_weights, ColorSolverMixModel::PigmentPainter); settings.contoning_flat_surface_layer_opacities = contoning_flat_surface_preview_layer_opacities(*zone, settings); - if (settings.contoning_flat_surface_layer_opacities.size() != settings.component_colors.size()) + if (settings.contoning_flat_surface_layer_opacities.size() != settings.component_colors.size()) { settings.contoning_flat_surface_td_adjustment = false; + settings.contoning_flat_surface_beer_lambert_rgb_correction = false; + } } const std::array raw_offset_visibility_factors = raw_offset_visibility_factors_for_texture_preview(*zone); @@ -2752,6 +2764,7 @@ size_t texture_preview_simulation_signature(const ModelVolume &model_volume, mix(std::hash{}(settings.contoning_flat_surface_quantization ? 1 : 0)); mix(std::hash{}(settings.contoning_flat_surface_pattern_blend ? 1 : 0)); mix(std::hash{}(settings.contoning_flat_surface_td_adjustment ? 1 : 0)); + mix(std::hash{}(settings.contoning_flat_surface_beer_lambert_rgb_correction ? 1 : 0)); if (settings.contoning_flat_surface_quantization) { mix(std::hash{}(settings.contoning_flat_surface_pattern_filaments)); mix(std::hash{}(settings.simulate_top_surface_lod ? 1 : 0)); @@ -2849,7 +2862,8 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig contoning_flat_surface_pattern_filaments, k_contoning_flat_surface_preview_max_ordered_candidates, k_contoning_flat_surface_preview_max_ordered_stack_items, - settings.contoning_flat_surface_surface_scatter) : + settings.contoning_flat_surface_surface_scatter, + settings.contoning_flat_surface_beer_lambert_rgb_correction) : ColorSolverOrderedStackCandidateSet{}; const std::vector contoning_flat_surface_candidates = use_contoning_flat_surface_quantization && contoning_flat_surface_ordered_candidates.empty() ? @@ -5657,6 +5671,8 @@ static size_t texture_preview_settings_signature_impl(size_t num_physical, signature_mix(std::hash{}(zone.top_surface_contoning_pattern_filaments)); signature_mix(std::hash{}(zone.top_surface_contoning_color_lower_surfaces ? 1 : 0)); signature_mix(std::hash{}(zone.top_surface_contoning_td_adjustment_enabled ? 1 : 0)); + signature_mix(std::hash{}(zone.top_surface_contoning_surface_scatter_enabled ? 1 : 0)); + signature_mix(std::hash{}(zone.top_surface_contoning_beer_lambert_rgb_correction_enabled ? 1 : 0)); signature_mix(std::hash{}(zone.nonlinear_offset_adjustment ? 1 : 0)); signature_mix(std::hash{}(zone.compact_offset_mode ? 1 : 0)); signature_mix(std::hash{}(zone.use_legacy_fixed_color_mode ? 1 : 0)); @@ -5810,6 +5826,8 @@ static void texture_preview_mix_zone_baked_model_settings(size_t &signature, signature_mix(std::hash{}(zone.top_surface_contoning_pattern_filaments)); signature_mix(std::hash{}(zone.top_surface_contoning_color_lower_surfaces ? 1 : 0)); signature_mix(std::hash{}(zone.top_surface_contoning_td_adjustment_enabled ? 1 : 0)); + signature_mix(std::hash{}(zone.top_surface_contoning_surface_scatter_enabled ? 1 : 0)); + signature_mix(std::hash{}(zone.top_surface_contoning_beer_lambert_rgb_correction_enabled ? 1 : 0)); signature_mix(std::hash{}(zone.nonlinear_offset_adjustment ? 1 : 0)); signature_mix(std::hash{}(zone.compact_offset_mode ? 1 : 0)); signature_mix(std::hash{}(zone.use_legacy_fixed_color_mode ? 1 : 0)); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4b6a4065a4e..340ef5ebf17 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2094,6 +2094,8 @@ public: int top_surface_contoning_polygonize_resolution, bool top_surface_contoning_surface_anchored_stacks_enabled, bool top_surface_contoning_td_adjustment_enabled, + bool top_surface_contoning_surface_scatter_enabled, + bool top_surface_contoning_beer_lambert_rgb_correction_enabled, const TextureMappingZoneShellUsageSummary &top_surface_contoning_shell_usage, const TextureMappingManager &texture_mapping_zones, const TextureMappingGlobalSettings &global_settings, @@ -2781,6 +2783,24 @@ public: 0, wxEXPAND | wxTOP | wxBOTTOM, gap / 2); + m_top_surface_contoning_surface_scatter_checkbox = + new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Surface scatter correction")); + m_top_surface_contoning_surface_scatter_checkbox->SetValue(top_surface_contoning_surface_scatter_enabled); + m_top_surface_contoning_surface_scatter_checkbox->SetMinSize( + wxSize(-1, std::max(m_top_surface_contoning_surface_scatter_checkbox->GetBestSize().GetHeight(), FromDIP(24)))); + contoning_checkboxes_root->Add(m_top_surface_contoning_surface_scatter_checkbox, + 0, + wxEXPAND | wxTOP | wxBOTTOM, + gap / 2); + m_top_surface_contoning_beer_lambert_rgb_correction_checkbox = + new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("RGB Beer-Lambert correction")); + m_top_surface_contoning_beer_lambert_rgb_correction_checkbox->SetValue(top_surface_contoning_beer_lambert_rgb_correction_enabled); + m_top_surface_contoning_beer_lambert_rgb_correction_checkbox->SetMinSize( + wxSize(-1, std::max(m_top_surface_contoning_beer_lambert_rgb_correction_checkbox->GetBestSize().GetHeight(), FromDIP(24)))); + contoning_checkboxes_root->Add(m_top_surface_contoning_beer_lambert_rgb_correction_checkbox, + 0, + wxEXPAND | wxTOP | wxBOTTOM, + gap / 2); m_top_surface_contoning_only_one_perimeter_around_shell_infill_checkbox = new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("Only one perimeter around shell infill")); m_top_surface_contoning_only_one_perimeter_around_shell_infill_checkbox->SetValue( @@ -2956,6 +2976,17 @@ public: } if (m_top_surface_contoning_td_adjustment_checkbox != nullptr) { m_top_surface_contoning_td_adjustment_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) { + update_top_surface_image_options_visibility(false); + queue_top_surface_contoning_message_update(true); + }); + } + if (m_top_surface_contoning_surface_scatter_checkbox != nullptr) { + m_top_surface_contoning_surface_scatter_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) { + queue_top_surface_contoning_message_update(true); + }); + } + if (m_top_surface_contoning_beer_lambert_rgb_correction_checkbox != nullptr) { + m_top_surface_contoning_beer_lambert_rgb_correction_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) { queue_top_surface_contoning_message_update(true); }); } @@ -3445,6 +3476,19 @@ public: TextureMappingZone::DefaultTopSurfaceContoningTdAdjustmentEnabled : m_top_surface_contoning_td_adjustment_checkbox->GetValue(); } + bool top_surface_contoning_surface_scatter_enabled() const + { + return m_top_surface_contoning_surface_scatter_checkbox == nullptr ? + TextureMappingZone::DefaultTopSurfaceContoningSurfaceScatterEnabled : + m_top_surface_contoning_surface_scatter_checkbox->GetValue(); + } + bool top_surface_contoning_beer_lambert_rgb_correction_enabled() const + { + return top_surface_contoning_td_adjustment_enabled() && + (m_top_surface_contoning_beer_lambert_rgb_correction_checkbox == nullptr ? + TextureMappingZone::DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled : + m_top_surface_contoning_beer_lambert_rgb_correction_checkbox->GetValue()); + } bool minimum_visibility_offset_enabled() const { return m_minimum_visibility_offset_checkbox && m_minimum_visibility_offset_checkbox->GetValue(); @@ -3779,7 +3823,7 @@ private: return 0; const float layer_height = top_surface_contoning_layer_height_mm(); const float safe_strength = std::clamp(strength, 0.01f, 0.99f); - constexpr float surface_scatter = 0.12f; + const float surface_scatter = top_surface_contoning_surface_scatter_enabled() ? 0.12f : 0.f; if (safe_strength <= surface_scatter) return 1; const float remaining = std::clamp((1.f - safe_strength) / (1.f - surface_scatter), 1e-6f, 0.999999f); @@ -4230,6 +4274,22 @@ private: m_top_surface_contoning_td_adjustment_checkbox->Show(contoning); m_top_surface_contoning_td_adjustment_checkbox->Enable(contoning); } + if (m_top_surface_contoning_surface_scatter_checkbox != nullptr) { + const bool td_adjustment = + contoning && + m_top_surface_contoning_td_adjustment_checkbox != nullptr && + m_top_surface_contoning_td_adjustment_checkbox->GetValue(); + m_top_surface_contoning_surface_scatter_checkbox->Show(contoning); + m_top_surface_contoning_surface_scatter_checkbox->Enable(td_adjustment); + } + if (m_top_surface_contoning_beer_lambert_rgb_correction_checkbox != nullptr) { + const bool td_adjustment = + contoning && + m_top_surface_contoning_td_adjustment_checkbox != nullptr && + m_top_surface_contoning_td_adjustment_checkbox->GetValue(); + m_top_surface_contoning_beer_lambert_rgb_correction_checkbox->Show(contoning); + m_top_surface_contoning_beer_lambert_rgb_correction_checkbox->Enable(td_adjustment); + } if (m_top_surface_contoning_only_one_perimeter_around_shell_infill_checkbox != nullptr) { m_top_surface_contoning_only_one_perimeter_around_shell_infill_checkbox->Show(contoning); m_top_surface_contoning_only_one_perimeter_around_shell_infill_checkbox->Enable(contoning); @@ -4376,6 +4436,8 @@ private: wxChoice *m_top_surface_contoning_polygonize_resolution_choice {nullptr}; wxCheckBox *m_top_surface_contoning_surface_anchored_stacks_checkbox {nullptr}; wxCheckBox *m_top_surface_contoning_td_adjustment_checkbox {nullptr}; + wxCheckBox *m_top_surface_contoning_surface_scatter_checkbox {nullptr}; + wxCheckBox *m_top_surface_contoning_beer_lambert_rgb_correction_checkbox {nullptr}; wxCheckBox *m_use_legacy_fixed_color_mode_checkbox {nullptr}; wxCheckBox *m_minimum_visibility_offset_checkbox {nullptr}; wxSpinCtrl *m_minimum_visibility_offset_spin {nullptr}; @@ -9404,6 +9466,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.top_surface_contoning_polygonize_resolution, updated.top_surface_contoning_surface_anchored_stacks_enabled, updated.top_surface_contoning_td_adjustment_enabled, + updated.top_surface_contoning_surface_scatter_enabled, + updated.top_surface_contoning_beer_lambert_rgb_correction_enabled, shell_usage, bundle->texture_mapping_zones, bundle->texture_mapping_global_settings, @@ -9482,6 +9546,10 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) dlg.top_surface_contoning_surface_anchored_stacks_enabled(); updated.top_surface_contoning_td_adjustment_enabled = dlg.top_surface_contoning_td_adjustment_enabled(); + updated.top_surface_contoning_surface_scatter_enabled = + dlg.top_surface_contoning_surface_scatter_enabled(); + updated.top_surface_contoning_beer_lambert_rgb_correction_enabled = + dlg.top_surface_contoning_beer_lambert_rgb_correction_enabled(); if (updated.top_surface_image_printing_enabled && updated.top_surface_image_printing_method == int(TextureMappingZone::TopSurfaceImageContoning)) { updated.modulation_mode = int(TextureMappingZone::ModulationPerimeterPathV2);