From 138406a4836372bdc47aed11093bc0fbe492945a Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Sat, 30 May 2026 14:27:58 +0100 Subject: [PATCH] Add TD effective alpha correction --- src/libslic3r/ColorSolver.cpp | 196 +++++++++++++++++--- src/libslic3r/ColorSolver.hpp | 26 ++- src/libslic3r/Fill/Fill.cpp | 11 +- src/libslic3r/TextureMapping.cpp | 6 + src/libslic3r/TextureMappingContoning.cpp | 69 ++++++- src/libslic3r/TextureMappingContoning.hpp | 4 + src/slic3r/GUI/MMUPaintedTexturePreview.cpp | 23 ++- src/slic3r/GUI/Plater.cpp | 45 ++++- 8 files changed, 340 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/ColorSolver.cpp b/src/libslic3r/ColorSolver.cpp index 7f7a676d91f..37b2ea57cd6 100644 --- a/src/libslic3r/ColorSolver.cpp +++ b/src/libslic3r/ColorSolver.cpp @@ -30,6 +30,10 @@ namespace Slic3r { namespace { +constexpr float TD_EFFECTIVE_ALPHA_DENSITY_SCALE = 0.6761904762f; +constexpr float TD_EFFECTIVE_ALPHA_PASS_DENSITY = 0.05f; +constexpr float TD_EFFECTIVE_ALPHA_WHITE_DENSITY = 0.35f; + float clamp01(float value) { if (!std::isfinite(value)) @@ -189,6 +193,11 @@ std::array color_solver_v2_axis_weights(const std::array &ta }; } +uint8_t stack_role_index(ColorSolverStackComponentRole role) +{ + return uint8_t(std::clamp(int(role), int(ColorSolverStackComponentRole::Generic), int(ColorSolverStackComponentRole::White))); +} + int build_color_solver_kd_tree(const std::vector &coords, std::vector &nodes, std::vector &indices, @@ -429,24 +438,9 @@ ColorSolverNearestResult nearest_color_solver_candidates_perceptual(const Candid return result; } -std::array beer_lambert_rgb_transmission(const std::array &srgb_color, float scalar_transmission) +std::array transmission_from_density_bias(const std::array &density_bias, 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) + @@ -472,6 +466,56 @@ std::array beer_lambert_rgb_transmission(const std::array &s }; } +std::array rgb_density_bias(const std::array &srgb_color, float strength_scale) +{ + 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 = std::clamp(strength_scale, 0.f, 2.f) * saturation; + if (strength <= 1e-4f) + return { 1.f, 1.f, 1.f }; + + 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); + } + return density_bias; +} + +std::array beer_lambert_rgb_transmission(const std::array &srgb_color, float scalar_transmission) +{ + return transmission_from_density_bias(rgb_density_bias(srgb_color, 0.75f), scalar_transmission); +} + +std::array td_effective_alpha_density_bias(const std::array &srgb_color, + ColorSolverStackComponentRole role) +{ + switch (role) { + case ColorSolverStackComponentRole::Cyan: + return { 1.f, TD_EFFECTIVE_ALPHA_PASS_DENSITY, TD_EFFECTIVE_ALPHA_PASS_DENSITY }; + case ColorSolverStackComponentRole::Magenta: + return { TD_EFFECTIVE_ALPHA_PASS_DENSITY, 1.f, TD_EFFECTIVE_ALPHA_PASS_DENSITY }; + case ColorSolverStackComponentRole::Yellow: + return { TD_EFFECTIVE_ALPHA_PASS_DENSITY, TD_EFFECTIVE_ALPHA_PASS_DENSITY, 1.f }; + case ColorSolverStackComponentRole::Black: + return { 1.f, 1.f, 1.f }; + case ColorSolverStackComponentRole::White: + return { TD_EFFECTIVE_ALPHA_WHITE_DENSITY, TD_EFFECTIVE_ALPHA_WHITE_DENSITY, TD_EFFECTIVE_ALPHA_WHITE_DENSITY }; + default: + return rgb_density_bias(srgb_color, 0.85f); + } +} + +std::array td_effective_alpha_transmission(const std::array &srgb_color, + float scalar_transmission, + ColorSolverStackComponentRole role) +{ + return transmission_from_density_bias(td_effective_alpha_density_bias(srgb_color, role), scalar_transmission); +} + 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, @@ -520,17 +564,76 @@ std::array mix_ordered_stack_beer_lambert_rgb(const std::vector mix_ordered_stack_td_effective_alpha( + const std::vector> &colors_with_background, + const std::vector &surface_to_deep, + const std::vector &layer_opacities, + float surface_scatter, + const std::vector &component_roles) +{ + 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; + const float scalar_transmission = std::pow(1.f - opacity, TD_EFFECTIVE_ALPHA_DENSITY_SCALE); + const ColorSolverStackComponentRole role = + size_t(component_idx) < component_roles.size() ? + component_roles[size_t(component_idx)] : + ColorSolverStackComponentRole::Generic; + std::array transmission = + td_effective_alpha_transmission(colors_with_background[size_t(component_idx)], scalar_transmission, role); + 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, ColorSolverMixModel mix_model, float surface_scatter, - bool beer_lambert_rgb_correction) + bool beer_lambert_rgb_correction, + bool td_effective_alpha_correction, + const std::vector &component_roles) { 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 (td_effective_alpha_correction) + return mix_ordered_stack_td_effective_alpha(colors_with_background, surface_to_deep, layer_opacities, surface_scatter, component_roles); if (beer_lambert_rgb_correction) return mix_ordered_stack_beer_lambert_rgb(colors_with_background, surface_to_deep, layer_opacities, surface_scatter); @@ -728,7 +831,9 @@ void append_ordered_stack_candidate(ColorSolverOrderedStackCandidateSet &c ColorSolverMixModel mix_model, int simulated_stack_depth, float surface_scatter, - bool beer_lambert_rgb_correction) + bool beer_lambert_rgb_correction, + bool td_effective_alpha_correction, + const std::vector &component_roles) { std::vector simulated_surface_to_deep; const std::vector *mix_stack = &surface_to_deep; @@ -737,7 +842,15 @@ 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, mix_model, surface_scatter, beer_lambert_rgb_correction); + mix_ordered_stack_with_buffers(colors_with_background, + weights, + *mix_stack, + layer_opacities, + mix_model, + surface_scatter, + beer_lambert_rgb_correction, + td_effective_alpha_correction, + component_roles); const std::array perceptual = oklab_from_srgb(mixed); candidates.rgbs.emplace_back(mixed[0]); candidates.rgbs.emplace_back(mixed[1]); @@ -813,7 +926,9 @@ std::array mix_color_solver_ordered_stack(const std::vector &background_rgb, ColorSolverMixModel mix_model, float surface_scatter, - bool beer_lambert_rgb_correction) + bool beer_lambert_rgb_correction, + bool td_effective_alpha_correction, + const std::vector &component_roles) { if (component_colors.empty() || surface_to_deep.empty()) return background_rgb; @@ -827,7 +942,9 @@ std::array mix_color_solver_ordered_stack(const std::vector color_solver_oklab_from_srgb(const std::array &rgb) @@ -962,7 +1079,9 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector &component_roles) { std::ostringstream key; key << component_colors.size(); @@ -975,6 +1094,15 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector &component_roles) { ColorSolverOrderedStackCandidateSet candidates; int simulated_depth = simulated_stack_depth > 0 ? simulated_stack_depth : stack_depth; @@ -1045,7 +1175,9 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates( mix_model, simulated_depth, surface_scatter, - beer_lambert_rgb_correction); + beer_lambert_rgb_correction, + td_effective_alpha_correction, + component_roles); return; } @@ -1094,7 +1226,9 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates( mix_model, simulated_depth, surface_scatter, - beer_lambert_rgb_correction); + beer_lambert_rgb_correction, + td_effective_alpha_correction, + component_roles); } return; } @@ -1122,7 +1256,9 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates size_t candidate_limit, size_t stack_item_limit, float surface_scatter, - bool beer_lambert_rgb_correction) + bool beer_lambert_rgb_correction, + bool td_effective_alpha_correction, + const std::vector &component_roles) { const std::string key = color_solver_ordered_stack_candidate_cache_key(component_colors, @@ -1134,7 +1270,9 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates candidate_limit, stack_item_limit, surface_scatter, - beer_lambert_rgb_correction); + beer_lambert_rgb_correction, + td_effective_alpha_correction, + component_roles); auto it = cache.find(key); if (it != cache.end()) return it->second; @@ -1149,7 +1287,9 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates candidate_limit, stack_item_limit, surface_scatter, - beer_lambert_rgb_correction)).first->second; + beer_lambert_rgb_correction, + td_effective_alpha_correction, + component_roles)).first->second; } std::vector solve_color_solver_ordered_stack_for_target( diff --git a/src/libslic3r/ColorSolver.hpp b/src/libslic3r/ColorSolver.hpp index f6e5e52c66e..0ae219f004a 100644 --- a/src/libslic3r/ColorSolver.hpp +++ b/src/libslic3r/ColorSolver.hpp @@ -44,6 +44,16 @@ enum class ColorSolverMode : int V2 = 1 }; +enum class ColorSolverStackComponentRole : uint8_t +{ + Generic = 0, + Cyan = 1, + Magenta = 2, + Yellow = 3, + Black = 4, + White = 5 +}; + struct ColorSolverCandidateSet { struct KdNode { uint32_t candidate_idx { 0 }; @@ -112,7 +122,9 @@ std::array mix_color_solver_ordered_stack(const std::vector &background_rgb, ColorSolverMixModel mix_model, float surface_scatter = 0.f, - bool beer_lambert_rgb_correction = false); + bool beer_lambert_rgb_correction = false, + bool td_effective_alpha_correction = false, + const std::vector &component_roles = {}); std::array color_solver_oklab_from_srgb(const std::array &rgb); std::array color_solver_srgb_from_oklab(const std::array &oklab); @@ -139,7 +151,9 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector &component_roles = {}); ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates( const std::vector> &component_colors, const std::vector &layer_opacities, @@ -150,7 +164,9 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates( size_t candidate_limit = 0, size_t stack_item_limit = 0, float surface_scatter = 0.f, - bool beer_lambert_rgb_correction = false); + bool beer_lambert_rgb_correction = false, + bool td_effective_alpha_correction = false, + const std::vector &component_roles = {}); const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates( ColorSolverOrderedStackCandidateCache &cache, const std::vector> &component_colors, @@ -162,7 +178,9 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates size_t candidate_limit = 0, size_t stack_item_limit = 0, float surface_scatter = 0.f, - bool beer_lambert_rgb_correction = false); + bool beer_lambert_rgb_correction = false, + bool td_effective_alpha_correction = false, + const std::vector &component_roles = {}); 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 98e0bcd4f19..8ae6ad50f6f 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -507,6 +507,7 @@ struct TopSurfaceImageRegionPlan { bool contoning_td_adjustment_enabled = TextureMappingZone::DefaultTopSurfaceContoningTdAdjustmentEnabled; bool contoning_surface_scatter_enabled = TextureMappingZone::DefaultTopSurfaceContoningSurfaceScatterEnabled; bool contoning_beer_lambert_rgb_correction_enabled = TextureMappingZone::DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled; + bool contoning_td_effective_alpha_correction_enabled = TextureMappingZone::DefaultTopSurfaceContoningTdEffectiveAlphaCorrectionEnabled; int contoning_generic_solver_mix_model = TextureMappingZone::DefaultGenericSolverMixModel; int contoning_flat_surface_infill_mode = TextureMappingZone::SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode; }; @@ -1176,6 +1177,7 @@ struct TopSurfaceImageContoningStackPlanKey { bool td_adjustment { false }; bool surface_scatter { false }; bool beer_lambert_rgb_correction { false }; + bool td_effective_alpha_correction { false }; int mix_model { TextureMappingZone::DefaultGenericSolverMixModel }; bool operator<(const TopSurfaceImageContoningStackPlanKey &rhs) const @@ -1207,6 +1209,7 @@ struct TopSurfaceImageContoningStackPlanKey { td_adjustment, surface_scatter, beer_lambert_rgb_correction, + td_effective_alpha_correction, mix_model) < std::tie(rhs.source_layer, rhs.source_layer_id, @@ -1235,6 +1238,7 @@ struct TopSurfaceImageContoningStackPlanKey { rhs.td_adjustment, rhs.surface_scatter, rhs.beer_lambert_rgb_correction, + rhs.td_effective_alpha_correction, rhs.mix_model); } }; @@ -2640,6 +2644,7 @@ static TopSurfaceImageContoningStackPlanKey top_surface_image_contoning_stack_pl 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; + key.td_effective_alpha_correction = plan.contoning_td_effective_alpha_correction_enabled; key.mix_model = plan.contoning_generic_solver_mix_model; return key; } @@ -3306,8 +3311,12 @@ static std::vector top_surface_image_region_plans( 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_td_effective_alpha_correction_enabled = + plan.contoning_td_adjustment_enabled && zone->top_surface_contoning_td_effective_alpha_correction_enabled; plan.contoning_beer_lambert_rgb_correction_enabled = - plan.contoning_td_adjustment_enabled && zone->top_surface_contoning_beer_lambert_rgb_correction_enabled; + plan.contoning_td_adjustment_enabled && + !plan.contoning_td_effective_alpha_correction_enabled && + zone->top_surface_contoning_beer_lambert_rgb_correction_enabled; plan.contoning_generic_solver_mix_model = std::clamp(zone->generic_solver_mix_model, int(TextureMappingZone::GenericSolverPigmentPainter), diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index facef826800..bf4c357b04f 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -1164,6 +1164,7 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const 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 && + top_surface_contoning_td_effective_alpha_correction_enabled == rhs.top_surface_contoning_td_effective_alpha_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 && @@ -1572,6 +1573,8 @@ std::string TextureMappingManager::serialize_entries() 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["top_surface_contoning_td_effective_alpha_correction_enabled"] = + zone.top_surface_contoning_td_effective_alpha_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; @@ -1880,6 +1883,9 @@ void TextureMappingManager::load_entries(const std::string &serialized, zone.top_surface_contoning_beer_lambert_rgb_correction_enabled = texture.value("top_surface_contoning_beer_lambert_rgb_correction_enabled", TextureMappingZone::DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled); + zone.top_surface_contoning_td_effective_alpha_correction_enabled = + texture.value("top_surface_contoning_td_effective_alpha_correction_enabled", + TextureMappingZone::DefaultTopSurfaceContoningTdEffectiveAlphaCorrectionEnabled); 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/TextureMappingContoning.cpp b/src/libslic3r/TextureMappingContoning.cpp index 9ccdccc4009..5d5fec962c1 100644 --- a/src/libslic3r/TextureMappingContoning.cpp +++ b/src/libslic3r/TextureMappingContoning.cpp @@ -70,6 +70,51 @@ bool black_role_component(int filament_color_mode, size_t component_idx, size_t } } +ColorSolverStackComponentRole cmy_component_role(int filament_color_mode, size_t component_idx, size_t component_count) +{ + switch (std::clamp(filament_color_mode, + int(TextureMappingZone::FilamentColorAny), + int(TextureMappingZone::FilamentColorRGBKW))) { + case int(TextureMappingZone::FilamentColorCMY): + if (component_count == 3) { + if (component_idx == 0) + return ColorSolverStackComponentRole::Cyan; + if (component_idx == 1) + return ColorSolverStackComponentRole::Magenta; + if (component_idx == 2) + return ColorSolverStackComponentRole::Yellow; + } + break; + case int(TextureMappingZone::FilamentColorCMYK): + if (component_count == 4) { + if (component_idx == 0) + return ColorSolverStackComponentRole::Cyan; + if (component_idx == 1) + return ColorSolverStackComponentRole::Magenta; + if (component_idx == 2) + return ColorSolverStackComponentRole::Yellow; + if (component_idx == 3) + return ColorSolverStackComponentRole::Black; + } + break; + case int(TextureMappingZone::FilamentColorCMYW): + if (component_count == 4) { + if (component_idx == 0) + return ColorSolverStackComponentRole::Cyan; + if (component_idx == 1) + return ColorSolverStackComponentRole::Magenta; + if (component_idx == 2) + return ColorSolverStackComponentRole::Yellow; + if (component_idx == 3) + return ColorSolverStackComponentRole::White; + } + break; + default: + break; + } + return ColorSolverStackComponentRole::Generic; +} + bool is_black_color_filament(const PrintConfig &config, unsigned int component_id) { ColorRGB color; @@ -270,6 +315,15 @@ std::optional> texture_mapping_contoning_component_colors( return out.front(); } +std::vector texture_mapping_contoning_component_roles(const TextureMappingZone &zone, + size_t component_count) +{ + std::vector roles(component_count, ColorSolverStackComponentRole::Generic); + for (size_t idx = 0; idx < component_count; ++idx) + roles[idx] = cmy_component_role(zone.filament_color_mode, idx, component_count); + return roles; +} + std::vector texture_mapping_contoning_components_bottom_to_top( const TextureMappingZone &zone, const PrintConfig &config, @@ -353,8 +407,12 @@ TextureMappingContoningSolver::TextureMappingContoningSolver(const TextureMappin { m_mix_model = color_solver_mix_model_from_index(zone.generic_solver_mix_model); m_td_adjustment_enabled = zone.top_surface_contoning_td_adjustment_enabled; + m_td_effective_alpha_correction_enabled = + m_td_adjustment_enabled && zone.top_surface_contoning_td_effective_alpha_correction_enabled; m_beer_lambert_rgb_correction_enabled = - m_td_adjustment_enabled && zone.top_surface_contoning_beer_lambert_rgb_correction_enabled; + m_td_adjustment_enabled && + !m_td_effective_alpha_correction_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; @@ -370,6 +428,7 @@ TextureMappingContoningSolver::TextureMappingContoningSolver(const TextureMappin m_component_ids.clear(); if (m_component_ids.empty()) return; + m_component_roles = texture_mapping_contoning_component_roles(zone, m_component_ids.size()); m_component_luminance.reserve(m_component_ids.size()); for (const unsigned int id : m_component_ids) @@ -488,7 +547,9 @@ std::optional> TextureMappingContoningSolver::stack_rgb( m_background_rgb, m_mix_model, m_surface_scatter, - m_beer_lambert_rgb_correction_enabled); + m_beer_lambert_rgb_correction_enabled, + m_td_effective_alpha_correction_enabled, + m_component_roles); } void TextureMappingContoningSolver::arrange_stack_for_light_path(std::vector &bottom_to_top, @@ -589,7 +650,9 @@ TextureMappingContoningStack TextureMappingContoningSolver::solve(const std::arr MAX_TD_ORDERED_CONTONING_CANDIDATES, MAX_TD_ORDERED_CONTONING_STACK_ITEMS, m_surface_scatter, - m_beer_lambert_rgb_correction_enabled); + m_beer_lambert_rgb_correction_enabled, + m_td_effective_alpha_correction_enabled, + m_component_roles); } 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 144b4fe19f2..484965c017d 100644 --- a/src/libslic3r/TextureMappingContoning.hpp +++ b/src/libslic3r/TextureMappingContoning.hpp @@ -69,6 +69,8 @@ private: ColorSolverMixModel m_mix_model { ColorSolverMixModel::PigmentPainter }; bool m_td_adjustment_enabled { false }; bool m_beer_lambert_rgb_correction_enabled { false }; + bool m_td_effective_alpha_correction_enabled { false }; + std::vector m_component_roles; 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() }; @@ -85,6 +87,8 @@ bool texture_mapping_contoning_normal_eligible(float normal_z, float threshold_d std::optional> texture_mapping_contoning_component_colors(const PrintConfig &config, const std::vector &component_ids, std::vector> &out); +std::vector texture_mapping_contoning_component_roles(const TextureMappingZone &zone, + size_t component_count); } // namespace Slic3r diff --git a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp index 8af9e996369..3402b9914fc 100644 --- a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp +++ b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp @@ -13,6 +13,7 @@ #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" #include "libslic3r/TextureMapping.hpp" +#include "libslic3r/TextureMappingContoning.hpp" #include "libslic3r/ColorSolver.hpp" #include @@ -96,6 +97,7 @@ struct TexturePreviewSimulationSettings bool contoning_flat_surface_pattern_blend = false; bool contoning_flat_surface_td_adjustment = false; bool contoning_flat_surface_beer_lambert_rgb_correction = false; + bool contoning_flat_surface_td_effective_alpha_correction = false; int contoning_flat_surface_pattern_filaments = TextureMappingZone::DefaultTopSurfaceContoningPatternFilaments; bool simulate_top_surface_lod = false; float top_surface_lod_pitch_mm = 0.f; @@ -106,6 +108,7 @@ struct TexturePreviewSimulationSettings float minimum_visibility_offset_factor = 0.f; std::vector component_ids; std::vector> component_colors; + std::vector component_roles; std::vector component_strength_factors; std::vector component_minimum_offset_factors; std::vector semantic_component_indices; @@ -2522,7 +2525,9 @@ std::array contoning_flat_surface_rgb_for_texture_preview( settings.contoning_flat_surface_background_rgb, color_solver_mix_model_from_index(settings.generic_solver_mix_model), settings.contoning_flat_surface_surface_scatter, - settings.contoning_flat_surface_beer_lambert_rgb_correction); + settings.contoning_flat_surface_beer_lambert_rgb_correction, + settings.contoning_flat_surface_td_effective_alpha_correction, + settings.component_roles); } } if (!candidates.empty()) { @@ -2679,7 +2684,11 @@ std::optional texture_preview_simulation_setti 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_td_effective_alpha_correction_enabled && zone->top_surface_contoning_beer_lambert_rgb_correction_enabled; + settings.contoning_flat_surface_td_effective_alpha_correction = + settings.contoning_flat_surface_td_adjustment && + zone->top_surface_contoning_td_effective_alpha_correction_enabled; settings.contoning_flat_surface_surface_scatter = settings.contoning_flat_surface_td_adjustment && zone->top_surface_contoning_surface_scatter_enabled ? @@ -2696,6 +2705,7 @@ std::optional texture_preview_simulation_setti settings.component_ids = TextureMappingManager::effective_texture_component_ids(*zone, num_physical, physical_colors); if (settings.component_ids.empty()) return std::nullopt; + settings.component_roles = texture_mapping_contoning_component_roles(*zone, settings.component_ids.size()); settings.simulate_top_surface_lod = texture_preview_simulate_top_surface_lod() && zone->top_surface_image_printing_enabled; @@ -2739,6 +2749,7 @@ std::optional texture_preview_simulation_setti 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; + settings.contoning_flat_surface_td_effective_alpha_correction = false; } } @@ -2779,6 +2790,7 @@ size_t texture_preview_simulation_signature(const ModelVolume &model_volume, 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)); + mix(std::hash{}(settings.contoning_flat_surface_td_effective_alpha_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)); @@ -2790,6 +2802,9 @@ size_t texture_preview_simulation_signature(const ModelVolume &model_volume, mix(std::hash{}(int(std::lround(settings.contoning_flat_surface_surface_scatter * 1000000.f)))); for (const float opacity : settings.contoning_flat_surface_layer_opacities) mix(std::hash{}(int(std::lround(opacity * 1000000.f)))); + if (settings.contoning_flat_surface_td_effective_alpha_correction) + for (const ColorSolverStackComponentRole role : settings.component_roles) + mix(std::hash{}(int(role))); } mix(std::hash{}(int(std::lround(settings.contrast_pct * 100.f)))); mix(std::hash{}(int(std::lround(settings.tone_gamma * 1000.f)))); @@ -2879,7 +2894,9 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig 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_beer_lambert_rgb_correction) : + settings.contoning_flat_surface_beer_lambert_rgb_correction, + settings.contoning_flat_surface_td_effective_alpha_correction, + settings.component_roles) : ColorSolverOrderedStackCandidateSet{}; const std::vector contoning_flat_surface_candidates = use_contoning_flat_surface_quantization && contoning_flat_surface_ordered_candidates.empty() ? @@ -5694,6 +5711,7 @@ static size_t texture_preview_settings_signature_impl(size_t num_physical, 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.top_surface_contoning_td_effective_alpha_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)); @@ -5849,6 +5867,7 @@ static void texture_preview_mix_zone_baked_model_settings(size_t &signature, 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.top_surface_contoning_td_effective_alpha_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 dcc71ae7dde..2f946062434 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2108,6 +2108,7 @@ public: bool top_surface_contoning_td_adjustment_enabled, bool top_surface_contoning_surface_scatter_enabled, bool top_surface_contoning_beer_lambert_rgb_correction_enabled, + bool top_surface_contoning_td_effective_alpha_correction_enabled, const TextureMappingZoneShellUsageSummary &top_surface_contoning_shell_usage, const TextureMappingManager &texture_mapping_zones, const TextureMappingGlobalSettings &global_settings, @@ -2806,6 +2807,15 @@ public: 0, wxEXPAND | wxTOP | wxBOTTOM, gap / 2); + m_top_surface_contoning_td_effective_alpha_correction_checkbox = + new wxCheckBox(m_top_surface_contoning_checkboxes_panel, wxID_ANY, _L("TD Effective Alpha correction")); + m_top_surface_contoning_td_effective_alpha_correction_checkbox->SetValue(top_surface_contoning_td_effective_alpha_correction_enabled); + m_top_surface_contoning_td_effective_alpha_correction_checkbox->SetMinSize( + wxSize(-1, std::max(m_top_surface_contoning_td_effective_alpha_correction_checkbox->GetBestSize().GetHeight(), FromDIP(24)))); + contoning_checkboxes_root->Add(m_top_surface_contoning_td_effective_alpha_correction_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); @@ -3002,6 +3012,12 @@ public: queue_top_surface_contoning_message_update(true); }); } + if (m_top_surface_contoning_td_effective_alpha_correction_checkbox != nullptr) { + m_top_surface_contoning_td_effective_alpha_correction_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_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); @@ -3508,10 +3524,18 @@ public: bool top_surface_contoning_beer_lambert_rgb_correction_enabled() const { return top_surface_contoning_td_adjustment_enabled() && + !top_surface_contoning_td_effective_alpha_correction_enabled() && (m_top_surface_contoning_beer_lambert_rgb_correction_checkbox == nullptr ? TextureMappingZone::DefaultTopSurfaceContoningBeerLambertRgbCorrectionEnabled : m_top_surface_contoning_beer_lambert_rgb_correction_checkbox->GetValue()); } + bool top_surface_contoning_td_effective_alpha_correction_enabled() const + { + return top_surface_contoning_td_adjustment_enabled() && + (m_top_surface_contoning_td_effective_alpha_correction_checkbox == nullptr ? + TextureMappingZone::DefaultTopSurfaceContoningTdEffectiveAlphaCorrectionEnabled : + m_top_surface_contoning_td_effective_alpha_correction_checkbox->GetValue()); + } bool minimum_visibility_offset_enabled() const { return m_minimum_visibility_offset_checkbox && m_minimum_visibility_offset_checkbox->GetValue(); @@ -3854,7 +3878,8 @@ private: if (safe_strength <= surface_scatter) return 1; const float remaining = std::clamp((1.f - safe_strength) / (1.f - surface_scatter), 1e-6f, 0.999999f); - const float layers = (td_mm / (2.f * layer_height)) * (std::log(remaining) / std::log(0.05f)); + const float density_scale = top_surface_contoning_td_effective_alpha_correction_enabled() ? 0.6761904762f : 1.f; + const float layers = (td_mm / (2.f * layer_height * density_scale)) * (std::log(remaining) / std::log(0.05f)); return std::max(1, int(std::ceil(layers))); } @@ -4314,8 +4339,20 @@ private: contoning && m_top_surface_contoning_td_adjustment_checkbox != nullptr && m_top_surface_contoning_td_adjustment_checkbox->GetValue(); + const bool td_effective_alpha = + td_adjustment && + m_top_surface_contoning_td_effective_alpha_correction_checkbox != nullptr && + m_top_surface_contoning_td_effective_alpha_correction_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); + m_top_surface_contoning_beer_lambert_rgb_correction_checkbox->Enable(td_adjustment && !td_effective_alpha); + } + if (m_top_surface_contoning_td_effective_alpha_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_td_effective_alpha_correction_checkbox->Show(contoning); + m_top_surface_contoning_td_effective_alpha_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); @@ -4465,6 +4502,7 @@ private: 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_top_surface_contoning_td_effective_alpha_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}; @@ -9497,6 +9535,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.top_surface_contoning_td_adjustment_enabled, updated.top_surface_contoning_surface_scatter_enabled, updated.top_surface_contoning_beer_lambert_rgb_correction_enabled, + updated.top_surface_contoning_td_effective_alpha_correction_enabled, shell_usage, bundle->texture_mapping_zones, bundle->texture_mapping_global_settings, @@ -9579,6 +9618,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) 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(); + updated.top_surface_contoning_td_effective_alpha_correction_enabled = + dlg.top_surface_contoning_td_effective_alpha_correction_enabled(); if (updated.top_surface_image_printing_enabled && updated.top_surface_image_printing_method == int(TextureMappingZone::TopSurfaceImageContoning)) { updated.modulation_mode = int(TextureMappingZone::ModulationPerimeterPathV2);