Add RGB Beer-Lambert correction. Allow disabling surface scatter adjustment

This commit is contained in:
sentientstardust
2026-05-30 00:19:29 +01:00
parent df8f231168
commit e1884ea0b4
9 changed files with 288 additions and 27 deletions

View File

@@ -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<float, 3> srgb_to_linear_color(const std::array<float, 3> &rgb)
{
return {
srgb_to_linear_component(rgb[0]),
srgb_to_linear_component(rgb[1]),
srgb_to_linear_component(rgb[2])
};
}
std::array<float, 3> linear_to_srgb_color(const std::array<float, 3> &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<float, 3> &rgb)
{
return 0.2126f * rgb[0] + 0.7152f * rgb[1] + 0.0722f * rgb[2];
}
std::array<float, 3> oklab_from_srgb(const std::array<float, 3> &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<float, 3> beer_lambert_rgb_transmission(const std::array<float, 3> &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<float, 3> 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<float, 3> 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<float, 3> mix_ordered_stack_beer_lambert_rgb(const std::vector<std::array<float, 3>> &colors_with_background,
const std::vector<uint16_t> &surface_to_deep,
const std::vector<float> &layer_opacities,
float surface_scatter)
{
if (colors_with_background.empty() || surface_to_deep.empty())
return colors_with_background.empty() ? std::array<float, 3>{ { 0.f, 0.f, 0.f } } : colors_with_background.back();
const size_t component_count = colors_with_background.size() - 1;
std::array<float, 3> accumulated { 0.f, 0.f, 0.f };
std::array<float, 3> 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<float, 3> 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<float, 3> 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<float, 3> 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<float, 3> mix_ordered_stack_with_buffers(const std::vector<std::array<float, 3>> &colors_with_background,
std::vector<float> &weights,
const std::vector<uint16_t> &surface_to_deep,
const std::vector<float> &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<float, 3>{ { 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<uint16_t> &surface_to_deep,
const std::vector<float> &layer_opacities,
int simulated_stack_depth,
float surface_scatter)
float surface_scatter,
bool beer_lambert_rgb_correction)
{
std::vector<uint16_t> simulated_surface_to_deep;
const std::vector<uint16_t> *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<float, 3> 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<float, 3> perceptual = oklab_from_srgb(mixed);
candidates.rgbs.emplace_back(mixed[0]);
candidates.rgbs.emplace_back(mixed[1]);
@@ -625,7 +744,8 @@ std::array<float, 3> mix_color_solver_ordered_stack(const std::vector<std::array
const std::vector<float> &layer_opacities,
const std::array<float, 3> &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<float, 3> mix_color_solver_ordered_stack(const std::vector<std::array
std::vector<std::array<float, 3>> colors = component_colors;
colors.emplace_back(background_rgb);
std::vector<float> 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<float, 3> color_solver_oklab_from_srgb(const std::array<float, 3> &rgb)
@@ -768,7 +893,8 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector<std
int simulated_stack_depth,
size_t candidate_limit,
size_t stack_item_limit,
float surface_scatter)
float surface_scatter,
bool beer_lambert_rgb_correction)
{
std::ostringstream key;
key << component_colors.size();
@@ -780,6 +906,7 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector<std
const float safe_surface_scatter =
std::clamp(std::isfinite(surface_scatter) ? surface_scatter : 0.f, 0.f, 0.5f);
key << "|ss" << int(std::lround(safe_surface_scatter * 1000000.f));
key << "|bl" << (beer_lambert_rgb_correction ? 1 : 0);
key << "|bg"
<< int(std::lround(clamp01(background_rgb[0]) * 65535.f)) << ','
<< int(std::lround(clamp01(background_rgb[1]) * 65535.f)) << ','
@@ -810,7 +937,8 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates(
int simulated_stack_depth,
size_t candidate_limit,
size_t stack_item_limit,
float surface_scatter)
float surface_scatter,
bool beer_lambert_rgb_correction)
{
(void) mix_model;
ColorSolverOrderedStackCandidateSet candidates;
@@ -848,7 +976,8 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates(
surface_to_deep,
layer_opacities,
simulated_depth,
surface_scatter);
surface_scatter,
beer_lambert_rgb_correction);
return;
}
@@ -895,7 +1024,8 @@ ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates(
surface_to_deep,
layer_opacities,
simulated_depth,
surface_scatter);
surface_scatter,
beer_lambert_rgb_correction);
}
return;
}
@@ -922,7 +1052,8 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates
int simulated_stack_depth,
size_t candidate_limit,
size_t stack_item_limit,
float surface_scatter)
float surface_scatter,
bool beer_lambert_rgb_correction)
{
const std::string key =
color_solver_ordered_stack_candidate_cache_key(component_colors,
@@ -933,7 +1064,8 @@ const ColorSolverOrderedStackCandidateSet &color_solver_ordered_stack_candidates
simulated_stack_depth,
candidate_limit,
stack_item_limit,
surface_scatter);
surface_scatter,
beer_lambert_rgb_correction);
auto it = cache.find(key);
if (it != cache.end())
return it->second;
@@ -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<uint16_t> solve_color_solver_ordered_stack_for_target(

View File

@@ -110,7 +110,8 @@ std::array<float, 3> mix_color_solver_ordered_stack(const std::vector<std::array
const std::vector<float> &layer_opacities,
const std::array<float, 3> &background_rgb,
ColorSolverMixModel mix_model,
float surface_scatter = 0.f);
float surface_scatter = 0.f,
bool beer_lambert_rgb_correction = false);
std::array<float, 3> color_solver_oklab_from_srgb(const std::array<float, 3> &rgb);
std::array<float, 3> color_solver_srgb_from_oklab(const std::array<float, 3> &oklab);
@@ -136,7 +137,8 @@ std::string color_solver_ordered_stack_candidate_cache_key(const std::vector<std
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);
ColorSolverOrderedStackCandidateSet build_color_solver_ordered_stack_candidates(
const std::vector<std::array<float, 3>> &component_colors,
const std::vector<float> &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<std::array<float, 3>> &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<uint16_t> solve_color_solver_ordered_stack_for_target(
const ColorSolverOrderedStackCandidateSet &candidates,
const std::array<float, 3> &target_rgb,

View File

@@ -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<TopSurfaceImageRegionPlan> 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 ?

View File

@@ -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 =

View File

@@ -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;

View File

@@ -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<std::array<float, 3>> 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<unsigned int> &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<uint16_t> surface_to_deep =
solve_color_solver_ordered_stack_for_target(*ordered_candidates, target_rgb, ColorSolverMode::V2);

View File

@@ -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<int, std::vector<Candidate>> m_candidates_by_depth;
mutable std::shared_ptr<ColorSolverOrderedStackCandidateCache> m_ordered_candidate_cache { std::make_shared<ColorSolverOrderedStackCandidateCache>() };
mutable std::shared_ptr<std::mutex> m_ordered_candidate_cache_mutex { std::make_shared<std::mutex>() };

View File

@@ -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<float, 3> 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<TexturePreviewSimulationSettings> 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<TexturePreviewSimulationSettings> 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<float, 2> 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<int>{}(settings.contoning_flat_surface_quantization ? 1 : 0));
mix(std::hash<int>{}(settings.contoning_flat_surface_pattern_blend ? 1 : 0));
mix(std::hash<int>{}(settings.contoning_flat_surface_td_adjustment ? 1 : 0));
mix(std::hash<int>{}(settings.contoning_flat_surface_beer_lambert_rgb_correction ? 1 : 0));
if (settings.contoning_flat_surface_quantization) {
mix(std::hash<int>{}(settings.contoning_flat_surface_pattern_filaments));
mix(std::hash<int>{}(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<TexturePreviewMixCandidate> 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<int>{}(zone.top_surface_contoning_pattern_filaments));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_color_lower_surfaces ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_td_adjustment_enabled ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_surface_scatter_enabled ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_beer_lambert_rgb_correction_enabled ? 1 : 0));
signature_mix(std::hash<int>{}(zone.nonlinear_offset_adjustment ? 1 : 0));
signature_mix(std::hash<int>{}(zone.compact_offset_mode ? 1 : 0));
signature_mix(std::hash<int>{}(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<int>{}(zone.top_surface_contoning_pattern_filaments));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_color_lower_surfaces ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_td_adjustment_enabled ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_surface_scatter_enabled ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_beer_lambert_rgb_correction_enabled ? 1 : 0));
signature_mix(std::hash<int>{}(zone.nonlinear_offset_adjustment ? 1 : 0));
signature_mix(std::hash<int>{}(zone.compact_offset_mode ? 1 : 0));
signature_mix(std::hash<int>{}(zone.use_legacy_fixed_color_mode ? 1 : 0));

View File

@@ -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);