Render preview for 2D gradients in shader. Misc bugfixes.
- Make sure prime tower preview is updated when settings change - Manage color data dialog now closes with escape key - Textured objects using halftone dithering now show their original texture while preview is being generated - Show checkboard error overlay in world space (when zone has invalid config).
This commit is contained in:
285
resources/shaders/110/painted_surface_gradient_preview.fs
Normal file
285
resources/shaders/110/painted_surface_gradient_preview.fs
Normal file
@ -0,0 +1,285 @@
|
||||
#version 110
|
||||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
const float INVALID_TEXTURE_CHECKER_SCALE = 0.2;
|
||||
const int MAX_GRADIENT_COMPONENTS = 10;
|
||||
const float EPSILON = 0.000001;
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
int type;
|
||||
vec4 xy_data;
|
||||
vec2 z_data;
|
||||
};
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
uniform float texture_preview_mix;
|
||||
uniform bool invalid_texture_mapping;
|
||||
uniform PrintVolumeDetection print_volume;
|
||||
|
||||
uniform int gradient_component_count;
|
||||
uniform vec3 gradient_component_colors[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_distances_mm[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_angles_deg[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_strength_factors[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_minimum_offset_factors[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_max_component_distance_mm;
|
||||
uniform float gradient_max_width_delta_limit_mm;
|
||||
uniform int gradient_angle_mode;
|
||||
uniform bool gradient_rotation_enabled;
|
||||
uniform float gradient_rotations;
|
||||
uniform float gradient_repeats;
|
||||
uniform bool gradient_reverse_repeats;
|
||||
uniform bool gradient_clockwise;
|
||||
uniform int gradient_fade_mode;
|
||||
uniform vec3 gradient_center;
|
||||
uniform float gradient_z_min;
|
||||
uniform float gradient_z_max;
|
||||
|
||||
varying vec2 intensity;
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying vec4 world_pos;
|
||||
varying vec3 world_normal;
|
||||
|
||||
float normalize_angle(float angle)
|
||||
{
|
||||
float out_angle = mod(angle, 360.0);
|
||||
if (out_angle < 0.0)
|
||||
out_angle += 360.0;
|
||||
return out_angle;
|
||||
}
|
||||
|
||||
float angular_distance_deg(float a, float b)
|
||||
{
|
||||
float d = abs(normalize_angle(a) - normalize_angle(b));
|
||||
return min(d, 360.0 - d);
|
||||
}
|
||||
|
||||
float angular_distance_cw(float from_deg, float to_deg)
|
||||
{
|
||||
float d = normalize_angle(to_deg) - normalize_angle(from_deg);
|
||||
if (d < 0.0)
|
||||
d += 360.0;
|
||||
return d;
|
||||
}
|
||||
|
||||
float repeated_rotation_progress(float progress01, float repeats, bool reverse_repeats)
|
||||
{
|
||||
float p = clamp(progress01, 0.0, 1.0);
|
||||
float r = max(1.0, repeats);
|
||||
if (r <= 1.0 + EPSILON)
|
||||
return p;
|
||||
|
||||
float repeated_pos = p * r;
|
||||
float segment_idx = floor(repeated_pos);
|
||||
float local = repeated_pos - segment_idx;
|
||||
|
||||
if (p >= 1.0 - EPSILON) {
|
||||
segment_idx = max(0.0, ceil(r) - 1.0);
|
||||
local = 1.0;
|
||||
}
|
||||
|
||||
if (reverse_repeats && mod(segment_idx, 2.0) >= 1.0)
|
||||
local = 1.0 - local;
|
||||
return clamp(local, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float offset_fade_factor(int fade_mode, float progress01)
|
||||
{
|
||||
float p = clamp(progress01, 0.0, 1.0);
|
||||
if (fade_mode == 1)
|
||||
return p;
|
||||
if (fade_mode == 2)
|
||||
return 1.0 - p;
|
||||
if (fade_mode == 3)
|
||||
return 1.0 - abs(2.0 * p - 1.0);
|
||||
if (fade_mode == 4)
|
||||
return abs(2.0 * p - 1.0);
|
||||
if (fade_mode == 5)
|
||||
return 2.0 * p - 1.0;
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
float component_angular_influence(int component_idx, float theta_deg)
|
||||
{
|
||||
int count = min(gradient_component_count, MAX_GRADIENT_COMPONENTS);
|
||||
if (count <= 0)
|
||||
return 0.0;
|
||||
if (count == 1)
|
||||
return 1.0;
|
||||
|
||||
float self_angle = normalize_angle(gradient_angles_deg[component_idx]);
|
||||
float prev_angle = self_angle;
|
||||
float next_angle = self_angle;
|
||||
float prev_to_self_deg = 360.0;
|
||||
float self_to_next_deg = 360.0;
|
||||
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
if (i >= count || i == component_idx)
|
||||
continue;
|
||||
float other_angle = normalize_angle(gradient_angles_deg[i]);
|
||||
float prev_distance = angular_distance_cw(other_angle, self_angle);
|
||||
float next_distance = angular_distance_cw(self_angle, other_angle);
|
||||
if (prev_distance < prev_to_self_deg) {
|
||||
prev_to_self_deg = prev_distance;
|
||||
prev_angle = other_angle;
|
||||
}
|
||||
if (next_distance < self_to_next_deg) {
|
||||
self_to_next_deg = next_distance;
|
||||
next_angle = other_angle;
|
||||
}
|
||||
}
|
||||
|
||||
if (prev_to_self_deg <= 0.001 || self_to_next_deg <= 0.001) {
|
||||
float total_weight = 0.0;
|
||||
float active_weight = 0.0;
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
if (i >= count)
|
||||
continue;
|
||||
float weight = max(0.0, 1.0 - angular_distance_deg(theta_deg, gradient_angles_deg[i]) / 180.0);
|
||||
total_weight += weight;
|
||||
if (i == component_idx)
|
||||
active_weight += weight;
|
||||
}
|
||||
if (total_weight <= EPSILON)
|
||||
return 0.0;
|
||||
return clamp(active_weight / total_weight, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float theta_norm = normalize_angle(theta_deg);
|
||||
float prev_to_theta_deg = angular_distance_cw(prev_angle, theta_norm);
|
||||
if (prev_to_theta_deg <= prev_to_self_deg + 0.0001)
|
||||
return clamp(prev_to_theta_deg / prev_to_self_deg, 0.0, 1.0);
|
||||
|
||||
float self_to_theta_deg = angular_distance_cw(self_angle, theta_norm);
|
||||
if (self_to_theta_deg <= self_to_next_deg + 0.0001)
|
||||
return clamp(1.0 - self_to_theta_deg / self_to_next_deg, 0.0, 1.0);
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float variable_width_delta(float inset_strength, float max_width_delta_limit_mm, float minimum_offset_factor, float strength_factor)
|
||||
{
|
||||
if (max_width_delta_limit_mm <= 0.0)
|
||||
return 0.0;
|
||||
|
||||
float desired_width_factor = 1.0 - clamp(inset_strength, 0.0, 1.0);
|
||||
float min_width_factor = clamp(minimum_offset_factor, 0.0, 1.0);
|
||||
float adjusted_width_factor = min_width_factor + desired_width_factor * clamp(strength_factor, 0.0, 1.0) * (1.0 - min_width_factor);
|
||||
return clamp(max_width_delta_limit_mm * (1.0 - adjusted_width_factor), 0.0, max_width_delta_limit_mm);
|
||||
}
|
||||
|
||||
vec3 surface_gradient_color()
|
||||
{
|
||||
int count = min(gradient_component_count, MAX_GRADIENT_COMPONENTS);
|
||||
if (count <= 0)
|
||||
return uniform_color.rgb;
|
||||
|
||||
float z_span = gradient_z_max - gradient_z_min;
|
||||
float z_progress = z_span > EPSILON ? clamp((world_pos.z - gradient_z_min) / z_span, 0.0, 1.0) : 0.0;
|
||||
|
||||
float rotation_deg = 0.0;
|
||||
if (gradient_rotation_enabled) {
|
||||
float repeated = repeated_rotation_progress(z_progress, max(1.0, gradient_repeats), gradient_reverse_repeats);
|
||||
float direction = gradient_clockwise ? -1.0 : 1.0;
|
||||
rotation_deg = direction * 360.0 * gradient_rotations * repeated;
|
||||
}
|
||||
|
||||
vec2 direction_vec = vec2(0.0);
|
||||
if (gradient_angle_mode == 1)
|
||||
direction_vec = world_normal.xy;
|
||||
if (dot(direction_vec, direction_vec) <= EPSILON) {
|
||||
vec3 radial = world_pos.xyz - gradient_center;
|
||||
direction_vec = radial.xy;
|
||||
}
|
||||
if (dot(direction_vec, direction_vec) <= EPSILON)
|
||||
direction_vec = vec2(1.0, 0.0);
|
||||
|
||||
float theta_deg = normalize_angle(degrees(atan(direction_vec.y, direction_vec.x)) - rotation_deg);
|
||||
float fade_factor = abs(offset_fade_factor(gradient_fade_mode, z_progress));
|
||||
float influences[MAX_GRADIENT_COMPONENTS];
|
||||
float edge_reaches[MAX_GRADIENT_COMPONENTS];
|
||||
float min_reach = 1000000.0;
|
||||
float max_reach = -1000000.0;
|
||||
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
influences[i] = 0.0;
|
||||
edge_reaches[i] = 0.0;
|
||||
if (i < count)
|
||||
influences[i] = component_angular_influence(i, theta_deg);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
if (i >= count)
|
||||
continue;
|
||||
float raw_inset_mm = 0.0;
|
||||
for (int j = 0; j < MAX_GRADIENT_COMPONENTS; ++j) {
|
||||
if (j >= count || i == j)
|
||||
continue;
|
||||
raw_inset_mm += gradient_distances_mm[j] * influences[j];
|
||||
}
|
||||
float inset_strength = clamp(raw_inset_mm / max(gradient_max_component_distance_mm, EPSILON), 0.0, 1.0);
|
||||
float width_delta_mm = variable_width_delta(inset_strength * fade_factor,
|
||||
gradient_max_width_delta_limit_mm,
|
||||
gradient_minimum_offset_factors[i],
|
||||
gradient_strength_factors[i]);
|
||||
edge_reaches[i] = clamp(gradient_max_width_delta_limit_mm - width_delta_mm, 0.0, gradient_max_width_delta_limit_mm);
|
||||
min_reach = min(min_reach, edge_reaches[i]);
|
||||
max_reach = max(max_reach, edge_reaches[i]);
|
||||
}
|
||||
|
||||
vec3 mixed_color = vec3(0.0);
|
||||
float total_weight = 0.0;
|
||||
float reach_span = max_reach - min_reach;
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
if (i >= count)
|
||||
continue;
|
||||
float weight = reach_span > EPSILON ? clamp((edge_reaches[i] - min_reach) / reach_span, 0.0, 1.0) : 1.0;
|
||||
mixed_color += gradient_component_colors[i] * weight;
|
||||
total_weight += weight;
|
||||
}
|
||||
if (total_weight <= EPSILON)
|
||||
return gradient_component_colors[0];
|
||||
return clamp(mixed_color / total_weight, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float invalid_texture_mapping_checker()
|
||||
{
|
||||
vec3 normal_axes = abs(world_normal);
|
||||
vec2 checker_pos = world_pos.xy;
|
||||
if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z)
|
||||
checker_pos = world_pos.yz;
|
||||
else if (normal_axes.y > normal_axes.z)
|
||||
checker_pos = world_pos.xz;
|
||||
return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
discard;
|
||||
|
||||
vec4 color = uniform_color;
|
||||
float mix_factor = clamp(texture_preview_mix, 0.0, 1.0);
|
||||
color.rgb = mix(color.rgb, surface_gradient_color(), mix_factor);
|
||||
if (invalid_texture_mapping) {
|
||||
float checker = invalid_texture_mapping_checker();
|
||||
vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker);
|
||||
color.rgb = mix(color.rgb, checker_color, 0.62);
|
||||
}
|
||||
|
||||
vec3 pv_check_min = ZERO;
|
||||
vec3 pv_check_max = ZERO;
|
||||
if (print_volume.type == 0) {
|
||||
pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x);
|
||||
pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y);
|
||||
}
|
||||
else if (print_volume.type == 1) {
|
||||
float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy);
|
||||
pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x);
|
||||
pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y);
|
||||
}
|
||||
|
||||
color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb;
|
||||
gl_FragColor = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a);
|
||||
}
|
||||
46
resources/shaders/110/painted_surface_gradient_preview.vs
Normal file
46
resources/shaders/110/painted_surface_gradient_preview.vs
Normal file
@ -0,0 +1,46 @@
|
||||
#version 110
|
||||
|
||||
#define INTENSITY_CORRECTION 0.6
|
||||
|
||||
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
|
||||
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SHININESS 20.0
|
||||
|
||||
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
||||
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
|
||||
|
||||
#define INTENSITY_AMBIENT 0.3
|
||||
|
||||
uniform mat4 view_model_matrix;
|
||||
uniform mat4 projection_matrix;
|
||||
uniform mat3 view_normal_matrix;
|
||||
uniform mat4 volume_world_matrix;
|
||||
uniform vec2 z_range;
|
||||
uniform vec4 clipping_plane;
|
||||
|
||||
attribute vec3 v_position;
|
||||
attribute vec3 v_normal;
|
||||
|
||||
varying vec2 intensity;
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying vec4 world_pos;
|
||||
varying vec3 world_normal;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 eye_normal = normalize(view_normal_matrix * v_normal);
|
||||
|
||||
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
|
||||
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
|
||||
vec4 position = view_model_matrix * vec4(v_position, 1.0);
|
||||
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
|
||||
|
||||
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
|
||||
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
|
||||
|
||||
world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0)));
|
||||
gl_Position = projection_matrix * position;
|
||||
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
const float UV_EDGE_EPSILON = 0.000001;
|
||||
const float INVALID_TEXTURE_CHECKER_SCALE = 0.2;
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
@ -19,6 +20,7 @@ uniform PrintVolumeDetection print_volume;
|
||||
varying vec2 intensity;
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying vec4 world_pos;
|
||||
varying vec3 world_normal;
|
||||
varying vec2 tex_coord;
|
||||
|
||||
float texture_preview_coord(float uv)
|
||||
@ -34,6 +36,17 @@ vec2 texture_preview_coord(vec2 uv)
|
||||
return vec2(texture_preview_coord(uv.x), texture_preview_coord(uv.y));
|
||||
}
|
||||
|
||||
float invalid_texture_mapping_checker()
|
||||
{
|
||||
vec3 normal_axes = abs(world_normal);
|
||||
vec2 checker_pos = world_pos.xy;
|
||||
if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z)
|
||||
checker_pos = world_pos.yz;
|
||||
else if (normal_axes.y > normal_axes.z)
|
||||
checker_pos = world_pos.xz;
|
||||
return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
@ -44,7 +57,7 @@ void main()
|
||||
float mix_factor = clamp(texture_preview_mix, 0.0, 1.0);
|
||||
color.rgb = mix(color.rgb, texture_color.rgb, mix_factor);
|
||||
if (invalid_texture_mapping) {
|
||||
float checker = mod(floor(tex_coord.x * 24.0) + floor(tex_coord.y * 24.0), 2.0);
|
||||
float checker = invalid_texture_mapping_checker();
|
||||
vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker);
|
||||
color.rgb = mix(color.rgb, checker_color, 0.62);
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ attribute vec2 v_tex_coord;
|
||||
varying vec2 intensity;
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying vec4 world_pos;
|
||||
varying vec3 world_normal;
|
||||
varying vec2 tex_coord;
|
||||
|
||||
void main()
|
||||
@ -41,6 +42,7 @@ void main()
|
||||
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
|
||||
|
||||
world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0)));
|
||||
tex_coord = v_tex_coord;
|
||||
gl_Position = projection_matrix * position;
|
||||
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#version 110
|
||||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
const float INVALID_TEXTURE_CHECKER_SCALE = 0.2;
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
@ -17,8 +18,20 @@ uniform PrintVolumeDetection print_volume;
|
||||
varying vec2 intensity;
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying vec4 world_pos;
|
||||
varying vec3 world_normal;
|
||||
varying vec4 vertex_color;
|
||||
|
||||
float invalid_texture_mapping_checker()
|
||||
{
|
||||
vec3 normal_axes = abs(world_normal);
|
||||
vec2 checker_pos = world_pos.xy;
|
||||
if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z)
|
||||
checker_pos = world_pos.yz;
|
||||
else if (normal_axes.y > normal_axes.z)
|
||||
checker_pos = world_pos.xz;
|
||||
return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
@ -28,7 +41,7 @@ void main()
|
||||
float mix_factor = clamp(texture_preview_mix, 0.0, 1.0);
|
||||
color.rgb = mix(color.rgb, vertex_color.rgb, mix_factor);
|
||||
if (invalid_texture_mapping) {
|
||||
float checker = mod(floor(world_pos.x * 4.0) + floor(world_pos.y * 4.0) + floor(world_pos.z * 4.0), 2.0);
|
||||
float checker = invalid_texture_mapping_checker();
|
||||
vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker);
|
||||
color.rgb = mix(color.rgb, checker_color, 0.62);
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ attribute vec4 v_color;
|
||||
varying vec2 intensity;
|
||||
varying vec3 clipping_planes_dots;
|
||||
varying vec4 world_pos;
|
||||
varying vec3 world_normal;
|
||||
varying vec4 vertex_color;
|
||||
|
||||
void main()
|
||||
@ -41,6 +42,7 @@ void main()
|
||||
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
|
||||
|
||||
world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0)));
|
||||
vertex_color = v_color;
|
||||
gl_Position = projection_matrix * position;
|
||||
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||
|
||||
287
resources/shaders/140/painted_surface_gradient_preview.fs
Normal file
287
resources/shaders/140/painted_surface_gradient_preview.fs
Normal file
@ -0,0 +1,287 @@
|
||||
#version 140
|
||||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
const float INVALID_TEXTURE_CHECKER_SCALE = 0.2;
|
||||
const int MAX_GRADIENT_COMPONENTS = 10;
|
||||
const float EPSILON = 0.000001;
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
int type;
|
||||
vec4 xy_data;
|
||||
vec2 z_data;
|
||||
};
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
uniform float texture_preview_mix;
|
||||
uniform bool invalid_texture_mapping;
|
||||
uniform PrintVolumeDetection print_volume;
|
||||
|
||||
uniform int gradient_component_count;
|
||||
uniform vec3 gradient_component_colors[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_distances_mm[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_angles_deg[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_strength_factors[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_minimum_offset_factors[MAX_GRADIENT_COMPONENTS];
|
||||
uniform float gradient_max_component_distance_mm;
|
||||
uniform float gradient_max_width_delta_limit_mm;
|
||||
uniform int gradient_angle_mode;
|
||||
uniform bool gradient_rotation_enabled;
|
||||
uniform float gradient_rotations;
|
||||
uniform float gradient_repeats;
|
||||
uniform bool gradient_reverse_repeats;
|
||||
uniform bool gradient_clockwise;
|
||||
uniform int gradient_fade_mode;
|
||||
uniform vec3 gradient_center;
|
||||
uniform float gradient_z_min;
|
||||
uniform float gradient_z_max;
|
||||
|
||||
in vec2 intensity;
|
||||
in vec3 clipping_planes_dots;
|
||||
in vec4 world_pos;
|
||||
in vec3 world_normal;
|
||||
|
||||
out vec4 out_color;
|
||||
|
||||
float normalize_angle(float angle)
|
||||
{
|
||||
float out_angle = mod(angle, 360.0);
|
||||
if (out_angle < 0.0)
|
||||
out_angle += 360.0;
|
||||
return out_angle;
|
||||
}
|
||||
|
||||
float angular_distance_deg(float a, float b)
|
||||
{
|
||||
float d = abs(normalize_angle(a) - normalize_angle(b));
|
||||
return min(d, 360.0 - d);
|
||||
}
|
||||
|
||||
float angular_distance_cw(float from_deg, float to_deg)
|
||||
{
|
||||
float d = normalize_angle(to_deg) - normalize_angle(from_deg);
|
||||
if (d < 0.0)
|
||||
d += 360.0;
|
||||
return d;
|
||||
}
|
||||
|
||||
float repeated_rotation_progress(float progress01, float repeats, bool reverse_repeats)
|
||||
{
|
||||
float p = clamp(progress01, 0.0, 1.0);
|
||||
float r = max(1.0, repeats);
|
||||
if (r <= 1.0 + EPSILON)
|
||||
return p;
|
||||
|
||||
float repeated_pos = p * r;
|
||||
float segment_idx = floor(repeated_pos);
|
||||
float local = repeated_pos - segment_idx;
|
||||
|
||||
if (p >= 1.0 - EPSILON) {
|
||||
segment_idx = max(0.0, ceil(r) - 1.0);
|
||||
local = 1.0;
|
||||
}
|
||||
|
||||
if (reverse_repeats && mod(segment_idx, 2.0) >= 1.0)
|
||||
local = 1.0 - local;
|
||||
return clamp(local, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float offset_fade_factor(int fade_mode, float progress01)
|
||||
{
|
||||
float p = clamp(progress01, 0.0, 1.0);
|
||||
if (fade_mode == 1)
|
||||
return p;
|
||||
if (fade_mode == 2)
|
||||
return 1.0 - p;
|
||||
if (fade_mode == 3)
|
||||
return 1.0 - abs(2.0 * p - 1.0);
|
||||
if (fade_mode == 4)
|
||||
return abs(2.0 * p - 1.0);
|
||||
if (fade_mode == 5)
|
||||
return 2.0 * p - 1.0;
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
float component_angular_influence(int component_idx, float theta_deg)
|
||||
{
|
||||
int count = min(gradient_component_count, MAX_GRADIENT_COMPONENTS);
|
||||
if (count <= 0)
|
||||
return 0.0;
|
||||
if (count == 1)
|
||||
return 1.0;
|
||||
|
||||
float self_angle = normalize_angle(gradient_angles_deg[component_idx]);
|
||||
float prev_angle = self_angle;
|
||||
float next_angle = self_angle;
|
||||
float prev_to_self_deg = 360.0;
|
||||
float self_to_next_deg = 360.0;
|
||||
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
if (i >= count || i == component_idx)
|
||||
continue;
|
||||
float other_angle = normalize_angle(gradient_angles_deg[i]);
|
||||
float prev_distance = angular_distance_cw(other_angle, self_angle);
|
||||
float next_distance = angular_distance_cw(self_angle, other_angle);
|
||||
if (prev_distance < prev_to_self_deg) {
|
||||
prev_to_self_deg = prev_distance;
|
||||
prev_angle = other_angle;
|
||||
}
|
||||
if (next_distance < self_to_next_deg) {
|
||||
self_to_next_deg = next_distance;
|
||||
next_angle = other_angle;
|
||||
}
|
||||
}
|
||||
|
||||
if (prev_to_self_deg <= 0.001 || self_to_next_deg <= 0.001) {
|
||||
float total_weight = 0.0;
|
||||
float active_weight = 0.0;
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
if (i >= count)
|
||||
continue;
|
||||
float weight = max(0.0, 1.0 - angular_distance_deg(theta_deg, gradient_angles_deg[i]) / 180.0);
|
||||
total_weight += weight;
|
||||
if (i == component_idx)
|
||||
active_weight += weight;
|
||||
}
|
||||
if (total_weight <= EPSILON)
|
||||
return 0.0;
|
||||
return clamp(active_weight / total_weight, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float theta_norm = normalize_angle(theta_deg);
|
||||
float prev_to_theta_deg = angular_distance_cw(prev_angle, theta_norm);
|
||||
if (prev_to_theta_deg <= prev_to_self_deg + 0.0001)
|
||||
return clamp(prev_to_theta_deg / prev_to_self_deg, 0.0, 1.0);
|
||||
|
||||
float self_to_theta_deg = angular_distance_cw(self_angle, theta_norm);
|
||||
if (self_to_theta_deg <= self_to_next_deg + 0.0001)
|
||||
return clamp(1.0 - self_to_theta_deg / self_to_next_deg, 0.0, 1.0);
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float variable_width_delta(float inset_strength, float max_width_delta_limit_mm, float minimum_offset_factor, float strength_factor)
|
||||
{
|
||||
if (max_width_delta_limit_mm <= 0.0)
|
||||
return 0.0;
|
||||
|
||||
float desired_width_factor = 1.0 - clamp(inset_strength, 0.0, 1.0);
|
||||
float min_width_factor = clamp(minimum_offset_factor, 0.0, 1.0);
|
||||
float adjusted_width_factor = min_width_factor + desired_width_factor * clamp(strength_factor, 0.0, 1.0) * (1.0 - min_width_factor);
|
||||
return clamp(max_width_delta_limit_mm * (1.0 - adjusted_width_factor), 0.0, max_width_delta_limit_mm);
|
||||
}
|
||||
|
||||
vec3 surface_gradient_color()
|
||||
{
|
||||
int count = min(gradient_component_count, MAX_GRADIENT_COMPONENTS);
|
||||
if (count <= 0)
|
||||
return uniform_color.rgb;
|
||||
|
||||
float z_span = gradient_z_max - gradient_z_min;
|
||||
float z_progress = z_span > EPSILON ? clamp((world_pos.z - gradient_z_min) / z_span, 0.0, 1.0) : 0.0;
|
||||
|
||||
float rotation_deg = 0.0;
|
||||
if (gradient_rotation_enabled) {
|
||||
float repeated = repeated_rotation_progress(z_progress, max(1.0, gradient_repeats), gradient_reverse_repeats);
|
||||
float direction = gradient_clockwise ? -1.0 : 1.0;
|
||||
rotation_deg = direction * 360.0 * gradient_rotations * repeated;
|
||||
}
|
||||
|
||||
vec2 direction_vec = vec2(0.0);
|
||||
if (gradient_angle_mode == 1)
|
||||
direction_vec = world_normal.xy;
|
||||
if (dot(direction_vec, direction_vec) <= EPSILON) {
|
||||
vec3 radial = world_pos.xyz - gradient_center;
|
||||
direction_vec = radial.xy;
|
||||
}
|
||||
if (dot(direction_vec, direction_vec) <= EPSILON)
|
||||
direction_vec = vec2(1.0, 0.0);
|
||||
|
||||
float theta_deg = normalize_angle(degrees(atan(direction_vec.y, direction_vec.x)) - rotation_deg);
|
||||
float fade_factor = abs(offset_fade_factor(gradient_fade_mode, z_progress));
|
||||
float influences[MAX_GRADIENT_COMPONENTS];
|
||||
float edge_reaches[MAX_GRADIENT_COMPONENTS];
|
||||
float min_reach = 1000000.0;
|
||||
float max_reach = -1000000.0;
|
||||
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
influences[i] = 0.0;
|
||||
edge_reaches[i] = 0.0;
|
||||
if (i < count)
|
||||
influences[i] = component_angular_influence(i, theta_deg);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
if (i >= count)
|
||||
continue;
|
||||
float raw_inset_mm = 0.0;
|
||||
for (int j = 0; j < MAX_GRADIENT_COMPONENTS; ++j) {
|
||||
if (j >= count || i == j)
|
||||
continue;
|
||||
raw_inset_mm += gradient_distances_mm[j] * influences[j];
|
||||
}
|
||||
float inset_strength = clamp(raw_inset_mm / max(gradient_max_component_distance_mm, EPSILON), 0.0, 1.0);
|
||||
float width_delta_mm = variable_width_delta(inset_strength * fade_factor,
|
||||
gradient_max_width_delta_limit_mm,
|
||||
gradient_minimum_offset_factors[i],
|
||||
gradient_strength_factors[i]);
|
||||
edge_reaches[i] = clamp(gradient_max_width_delta_limit_mm - width_delta_mm, 0.0, gradient_max_width_delta_limit_mm);
|
||||
min_reach = min(min_reach, edge_reaches[i]);
|
||||
max_reach = max(max_reach, edge_reaches[i]);
|
||||
}
|
||||
|
||||
vec3 mixed_color = vec3(0.0);
|
||||
float total_weight = 0.0;
|
||||
float reach_span = max_reach - min_reach;
|
||||
for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) {
|
||||
if (i >= count)
|
||||
continue;
|
||||
float weight = reach_span > EPSILON ? clamp((edge_reaches[i] - min_reach) / reach_span, 0.0, 1.0) : 1.0;
|
||||
mixed_color += gradient_component_colors[i] * weight;
|
||||
total_weight += weight;
|
||||
}
|
||||
if (total_weight <= EPSILON)
|
||||
return gradient_component_colors[0];
|
||||
return clamp(mixed_color / total_weight, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float invalid_texture_mapping_checker()
|
||||
{
|
||||
vec3 normal_axes = abs(world_normal);
|
||||
vec2 checker_pos = world_pos.xy;
|
||||
if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z)
|
||||
checker_pos = world_pos.yz;
|
||||
else if (normal_axes.y > normal_axes.z)
|
||||
checker_pos = world_pos.xz;
|
||||
return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
discard;
|
||||
|
||||
vec4 color = uniform_color;
|
||||
float mix_factor = clamp(texture_preview_mix, 0.0, 1.0);
|
||||
color.rgb = mix(color.rgb, surface_gradient_color(), mix_factor);
|
||||
if (invalid_texture_mapping) {
|
||||
float checker = invalid_texture_mapping_checker();
|
||||
vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker);
|
||||
color.rgb = mix(color.rgb, checker_color, 0.62);
|
||||
}
|
||||
|
||||
vec3 pv_check_min = ZERO;
|
||||
vec3 pv_check_max = ZERO;
|
||||
if (print_volume.type == 0) {
|
||||
pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x);
|
||||
pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y);
|
||||
}
|
||||
else if (print_volume.type == 1) {
|
||||
float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy);
|
||||
pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x);
|
||||
pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y);
|
||||
}
|
||||
|
||||
color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb;
|
||||
out_color = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a);
|
||||
}
|
||||
46
resources/shaders/140/painted_surface_gradient_preview.vs
Normal file
46
resources/shaders/140/painted_surface_gradient_preview.vs
Normal file
@ -0,0 +1,46 @@
|
||||
#version 140
|
||||
|
||||
#define INTENSITY_CORRECTION 0.6
|
||||
|
||||
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
|
||||
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SHININESS 20.0
|
||||
|
||||
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
||||
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
|
||||
|
||||
#define INTENSITY_AMBIENT 0.3
|
||||
|
||||
uniform mat4 view_model_matrix;
|
||||
uniform mat4 projection_matrix;
|
||||
uniform mat3 view_normal_matrix;
|
||||
uniform mat4 volume_world_matrix;
|
||||
uniform vec2 z_range;
|
||||
uniform vec4 clipping_plane;
|
||||
|
||||
in vec3 v_position;
|
||||
in vec3 v_normal;
|
||||
|
||||
out vec2 intensity;
|
||||
out vec3 clipping_planes_dots;
|
||||
out vec4 world_pos;
|
||||
out vec3 world_normal;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 eye_normal = normalize(view_normal_matrix * v_normal);
|
||||
|
||||
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
|
||||
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
|
||||
vec4 position = view_model_matrix * vec4(v_position, 1.0);
|
||||
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
|
||||
|
||||
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
|
||||
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
|
||||
|
||||
world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0)));
|
||||
gl_Position = projection_matrix * position;
|
||||
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
const float UV_EDGE_EPSILON = 0.000001;
|
||||
const float INVALID_TEXTURE_CHECKER_SCALE = 0.2;
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
@ -19,6 +20,7 @@ uniform PrintVolumeDetection print_volume;
|
||||
in vec2 intensity;
|
||||
in vec3 clipping_planes_dots;
|
||||
in vec4 world_pos;
|
||||
in vec3 world_normal;
|
||||
in vec2 tex_coord;
|
||||
|
||||
out vec4 out_color;
|
||||
@ -36,6 +38,17 @@ vec2 texture_preview_coord(vec2 uv)
|
||||
return vec2(texture_preview_coord(uv.x), texture_preview_coord(uv.y));
|
||||
}
|
||||
|
||||
float invalid_texture_mapping_checker()
|
||||
{
|
||||
vec3 normal_axes = abs(world_normal);
|
||||
vec2 checker_pos = world_pos.xy;
|
||||
if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z)
|
||||
checker_pos = world_pos.yz;
|
||||
else if (normal_axes.y > normal_axes.z)
|
||||
checker_pos = world_pos.xz;
|
||||
return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
@ -46,7 +59,7 @@ void main()
|
||||
float mix_factor = clamp(texture_preview_mix, 0.0, 1.0);
|
||||
color.rgb = mix(color.rgb, texture_color.rgb, mix_factor);
|
||||
if (invalid_texture_mapping) {
|
||||
float checker = mod(floor(tex_coord.x * 24.0) + floor(tex_coord.y * 24.0), 2.0);
|
||||
float checker = invalid_texture_mapping_checker();
|
||||
vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker);
|
||||
color.rgb = mix(color.rgb, checker_color, 0.62);
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ in vec2 v_tex_coord;
|
||||
out vec2 intensity;
|
||||
out vec3 clipping_planes_dots;
|
||||
out vec4 world_pos;
|
||||
out vec3 world_normal;
|
||||
out vec2 tex_coord;
|
||||
|
||||
void main()
|
||||
@ -41,6 +42,7 @@ void main()
|
||||
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
|
||||
|
||||
world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0)));
|
||||
tex_coord = v_tex_coord;
|
||||
gl_Position = projection_matrix * position;
|
||||
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#version 140
|
||||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
const float INVALID_TEXTURE_CHECKER_SCALE = 0.2;
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
@ -17,10 +18,22 @@ uniform PrintVolumeDetection print_volume;
|
||||
in vec2 intensity;
|
||||
in vec3 clipping_planes_dots;
|
||||
in vec4 world_pos;
|
||||
in vec3 world_normal;
|
||||
in vec4 vertex_color;
|
||||
|
||||
out vec4 out_color;
|
||||
|
||||
float invalid_texture_mapping_checker()
|
||||
{
|
||||
vec3 normal_axes = abs(world_normal);
|
||||
vec2 checker_pos = world_pos.xy;
|
||||
if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z)
|
||||
checker_pos = world_pos.yz;
|
||||
else if (normal_axes.y > normal_axes.z)
|
||||
checker_pos = world_pos.xz;
|
||||
return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
@ -30,7 +43,7 @@ void main()
|
||||
float mix_factor = clamp(texture_preview_mix, 0.0, 1.0);
|
||||
color.rgb = mix(color.rgb, vertex_color.rgb, mix_factor);
|
||||
if (invalid_texture_mapping) {
|
||||
float checker = mod(floor(world_pos.x * 4.0) + floor(world_pos.y * 4.0) + floor(world_pos.z * 4.0), 2.0);
|
||||
float checker = invalid_texture_mapping_checker();
|
||||
vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker);
|
||||
color.rgb = mix(color.rgb, checker_color, 0.62);
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ in vec4 v_color;
|
||||
out vec2 intensity;
|
||||
out vec3 clipping_planes_dots;
|
||||
out vec4 world_pos;
|
||||
out vec3 world_normal;
|
||||
out vec4 vertex_color;
|
||||
|
||||
void main()
|
||||
@ -41,6 +42,7 @@ void main()
|
||||
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
|
||||
|
||||
world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0)));
|
||||
vertex_color = v_color;
|
||||
gl_Position = projection_matrix * position;
|
||||
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||
|
||||
Reference in New Issue
Block a user