From d766854af723d4d89db7e3dbb3b2e4e406a9e033 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 15 Sep 2026 08:58:26 +0200 Subject: [PATCH] Bump and uvcheck shaders: project in the bake's world frame --- .../shaders/110/texture_displacement_bump.fs | 19 ++++++++------- .../110/texture_displacement_uvcheck.fs | 7 ++++-- .../shaders/140/texture_displacement_bump.fs | 23 +++++++++++-------- .../140/texture_displacement_uvcheck.fs | 7 ++++-- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/resources/shaders/110/texture_displacement_bump.fs b/resources/shaders/110/texture_displacement_bump.fs index 4686668467..5dff96d479 100644 --- a/resources/shaders/110/texture_displacement_bump.fs +++ b/resources/shaders/110/texture_displacement_bump.fs @@ -46,7 +46,8 @@ uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; uniform float midlevel; // the height that means "don't move"; needed by the parallax step -uniform vec3 eye_model_pos; // camera position in this volume's local space, for the view ray +uniform vec3 eye_model_pos; // camera position in the texture frame (world minus tex_anchor) +uniform vec3 tex_anchor; // the volume's origin in world space: the texture frame's origin uniform bool use_vertex_uv; // 2x3 affine (lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the dragged island's uv; see the // 140 variant. Identity when nothing is dragged. @@ -134,7 +135,9 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + // World millimetres throughout, like the bake - see the 140 variant. + vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); + vec3 tex_pos = world_pos.xyz - tex_anchor; // the frame the texture is projected in, as the bake does if (volume_mirrored) triangle_normal = -triangle_normal; @@ -155,8 +158,8 @@ void main() have_uv = true; float h = texture2D(height_tex, uv).r; float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); - vec3 sigmaS = dFdx(model_pos.xyz); - vec3 sigmaT = dFdy(model_pos.xyz); + vec3 sigmaS = dFdx(world_pos.xyz); + vec3 sigmaT = dFdy(world_pos.xyz); vec3 R1 = cross(sigmaT, triangle_normal); vec3 R2 = cross(triangle_normal, sigmaS); float det = dot(sigmaS, R1); @@ -171,9 +174,9 @@ void main() // Parallax occlusion mapping: march the view ray through the height shell and shade at the // first point where it drops below the displaced surface (see header). float amp = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); - vec3 view_dir = normalize(eye_model_pos - model_pos.xyz); + vec3 view_dir = normalize(eye_model_pos - tex_pos); float v_dot_n = dot(view_dir, triangle_normal); - vec2 uv = project_uv(model_pos.xyz, triangle_normal); + vec2 uv = project_uv(tex_pos, triangle_normal); // The shell the displaced surface lives inside, as signed heights along the normal. Taken from // both ends of h in [0, 1] so it stays correct for an inverted layer or a raised midlevel, @@ -192,11 +195,11 @@ void main() // by construction, and step inward; the crossing is what this pixel actually sees. float s = h_hi / v_dot_n; float ds = (h_hi - h_lo) / (v_dot_n * float(PARALLAX_STEPS)); - vec2 prev_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + vec2 prev_uv = project_uv(tex_pos + view_dir * s, triangle_normal); float prev_gap = h_hi - amp * (H_AT(prev_uv) - midlevel); // >= 0 by construction for (int i = 0; i < PARALLAX_STEPS; ++i) { s -= ds; - vec2 cur_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + vec2 cur_uv = project_uv(tex_pos + view_dir * s, triangle_normal); float gap = s * v_dot_n - amp * (H_AT(cur_uv) - midlevel); if (gap <= 0.0) { // Crossed between the last two samples - interpolating the hit is what stops it diff --git a/resources/shaders/110/texture_displacement_uvcheck.fs b/resources/shaders/110/texture_displacement_uvcheck.fs index fbfa6a0517..2679bc57e7 100644 --- a/resources/shaders/110/texture_displacement_uvcheck.fs +++ b/resources/shaders/110/texture_displacement_uvcheck.fs @@ -16,6 +16,7 @@ uniform bool volume_mirrored; uniform int mode; uniform float checker_freq; uniform float tiling_scale; +uniform vec3 tex_anchor; // the volume's origin in world space uniform float rotation_rad; uniform vec2 uv_offset; uniform bool use_vertex_uv; @@ -49,7 +50,9 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + // World space anchored at the volume's origin, like the bake and the bump preview. + vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); + vec3 tex_pos = world_pos.xyz - tex_anchor; if (volume_mirrored) triangle_normal = -triangle_normal; @@ -57,7 +60,7 @@ void main() if (mode == 1) { base = heatmap(distortion); } else { - vec2 uv = use_vertex_uv ? vertex_uv : project_uv(model_pos.xyz, triangle_normal); + vec2 uv = use_vertex_uv ? vertex_uv : project_uv(tex_pos, triangle_normal); vec2 c = floor(uv * checker_freq); float check = mod(c.x + c.y, 2.0); base = (check < 0.5) ? vec3(0.22, 0.23, 0.26) : vec3(0.82, 0.83, 0.86); diff --git a/resources/shaders/140/texture_displacement_bump.fs b/resources/shaders/140/texture_displacement_bump.fs index 4f8e3c8186..8127ac35a1 100644 --- a/resources/shaders/140/texture_displacement_bump.fs +++ b/resources/shaders/140/texture_displacement_bump.fs @@ -105,7 +105,8 @@ uniform float rotation_rad; uniform vec2 uv_offset; uniform bool invert; uniform float midlevel; // the height that means "don't move"; needed by the parallax step -uniform vec3 eye_model_pos; // camera position in this volume's local space, for the view ray +uniform vec3 eye_model_pos; // camera position in the texture frame (world minus tex_anchor) +uniform vec3 tex_anchor; // the volume's origin in world space: the texture frame's origin uniform bool use_vertex_uv; // true: sample at vertex_uv with a derived tangent frame (LSCM) // A 2x3 affine (columns packed as lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the uv of // the island currently being dragged in the UV editor (island_active > 0.5). Identity when nothing is @@ -123,7 +124,7 @@ in vec2 vertex_uv; out vec4 out_color; -// The two model-space axes the triplanar planar coordinate is read off, per dominant normal +// The two world-space axes the triplanar planar coordinate is read off, per dominant normal // component - same choice libslic3r's project_planar() makes, so planar.x runs along t, planar.y // along b. void projection_axes(vec3 n, out vec3 t, out vec3 b) @@ -200,7 +201,11 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + // Everything below runs in world millimetres, like the bake: a tile is tiling_scale mm on the + // printed part whatever the instance's scale or rotation, so the preview has to project from the + // world position and perturb the world normal. + vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); + vec3 tex_pos = world_pos.xyz - tex_anchor; // the frame the texture is projected in, as the bake does if (volume_mirrored) triangle_normal = -triangle_normal; @@ -231,8 +236,8 @@ void main() have_uv = true; float h = texture(height_tex, uv).r; float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); - vec3 sigmaS = dFdx(model_pos.xyz); - vec3 sigmaT = dFdy(model_pos.xyz); + vec3 sigmaS = dFdx(world_pos.xyz); + vec3 sigmaT = dFdy(world_pos.xyz); vec3 R1 = cross(sigmaT, triangle_normal); vec3 R2 = cross(triangle_normal, sigmaS); float det = dot(sigmaS, R1); @@ -250,9 +255,9 @@ void main() // Parallax occlusion mapping: march the view ray through the height shell and shade at the // first point where it drops below the displaced surface (see header). float amp = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0); - vec3 view_dir = normalize(eye_model_pos - model_pos.xyz); + vec3 view_dir = normalize(eye_model_pos - tex_pos); float v_dot_n = dot(view_dir, triangle_normal); - vec2 uv = project_uv(model_pos.xyz, triangle_normal); + vec2 uv = project_uv(tex_pos, triangle_normal); // The shell the displaced surface lives inside, as signed heights along the normal. Taken from // both ends of h in [0, 1] so it stays correct for an inverted layer or a raised midlevel, @@ -271,11 +276,11 @@ void main() // by construction, and step inward; the crossing is what this pixel actually sees. float s = h_hi / v_dot_n; float ds = (h_hi - h_lo) / (v_dot_n * float(PARALLAX_STEPS)); - vec2 prev_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + vec2 prev_uv = project_uv(tex_pos + view_dir * s, triangle_normal); float prev_gap = h_hi - amp * (H_AT(prev_uv) - midlevel); // >= 0 by construction for (int i = 0; i < PARALLAX_STEPS; ++i) { s -= ds; - vec2 cur_uv = project_uv(model_pos.xyz + view_dir * s, triangle_normal); + vec2 cur_uv = project_uv(tex_pos + view_dir * s, triangle_normal); float gap = s * v_dot_n - amp * (H_AT(cur_uv) - midlevel); if (gap <= 0.0) { // Crossed between the last two samples - interpolating the hit is what stops it diff --git a/resources/shaders/140/texture_displacement_uvcheck.fs b/resources/shaders/140/texture_displacement_uvcheck.fs index 8b430db52d..038c915f3f 100644 --- a/resources/shaders/140/texture_displacement_uvcheck.fs +++ b/resources/shaders/140/texture_displacement_uvcheck.fs @@ -25,6 +25,7 @@ uniform bool volume_mirrored; uniform int mode; // 0 checker, 1 distortion uniform float checker_freq; // checker squares per uv unit (one uv unit == one texture tile) uniform float tiling_scale; +uniform vec3 tex_anchor; // the volume's origin in world space uniform float rotation_rad; uniform vec2 uv_offset; uniform bool use_vertex_uv; @@ -61,7 +62,9 @@ void main() if (any(lessThan(clipping_planes_dots, ZERO))) discard; - vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); + // World space anchored at the volume's origin, like the bake and the bump preview. + vec3 triangle_normal = normalize(cross(dFdx(world_pos.xyz), dFdy(world_pos.xyz))); + vec3 tex_pos = world_pos.xyz - tex_anchor; if (volume_mirrored) triangle_normal = -triangle_normal; @@ -69,7 +72,7 @@ void main() if (mode == 1) { base = heatmap(distortion); } else { - vec2 uv = use_vertex_uv ? vertex_uv : project_uv(model_pos.xyz, triangle_normal); + vec2 uv = use_vertex_uv ? vertex_uv : project_uv(tex_pos, triangle_normal); vec2 c = floor(uv * checker_freq); float check = mod(c.x + c.y, 2.0); // Two distinct greys, plus a faint tint on one set so orientation is readable at a glance.