From 5264f0c24745b7544a15f0b93610a2ccef3135e5 Mon Sep 17 00:00:00 2001 From: lenn Date: Fri, 3 Jul 2026 15:30:25 +0800 Subject: [PATCH] Tune hand-tip and palm chip layout coordinates Adjust HandTipMatrix / HandPalmChip center, angle, and size in render.rs and propagate the spacing tweak through shader.wgsl. --- src/render.rs | 37 +++++---- static/wgsl/shader.wgsl | 172 ++++++++++++++++++++++++---------------- 2 files changed, 124 insertions(+), 85 deletions(-) diff --git a/src/render.rs b/src/render.rs index 9d25a2a..4883993 100644 --- a/src/render.rs +++ b/src/render.rs @@ -44,43 +44,43 @@ pub struct HandGatewayMode { const HAND_TIP_MATRICES: [HandTipMatrix; 5] = [ HandTipMatrix { - center_px: [260.0, 490.0], + center_px: [265.0, 495.0], size_px: [70.0, 120.0], - angle_rad: -0.45, + angle_rad: -0.40, }, HandTipMatrix { - center_px: [360.0, 255.0], + center_px: [359.0, 260.0], size_px: [70.0, 120.0], - angle_rad: -0.05, + angle_rad: -0.17, }, HandTipMatrix { - center_px: [485.0, 225.0], + center_px: [482.0, 228.0], size_px: [70.0, 120.0], - angle_rad: 0.0, + angle_rad: -0.03, }, HandTipMatrix { - center_px: [595.0, 260.0], + center_px: [596.0, 265.0], size_px: [70.0, 120.0], - angle_rad: 0.12, + angle_rad: 0.10, }, HandTipMatrix { - center_px: [705.0, 385.0], + center_px: [693.0, 370.0], size_px: [70.0, 120.0], - angle_rad: 0.28, + angle_rad: 0.22, }, ]; const HAND_PALM_CHIPS: [HandPalmChip; 2] = [ HandPalmChip { - center_px: [538.0, 608.0], - size_px: [248.0, 82.0], - angle_rad: 0.06, + center_px: [530.0, 593.0], + size_px: [224.0, 68.0], + angle_rad: 0.12, rows: 5, cols: 14, }, HandPalmChip { - center_px: [606.0, 780.0], - size_px: [72.0, 214.0], + center_px: [610.0, 778.0], + size_px: [60.0, 190.0], angle_rad: 0.05, rows: 11, cols: 4, @@ -90,6 +90,7 @@ const HAND_PALM_CHIPS: [HandPalmChip; 2] = [ const HAND_FINGER_SENSOR_CELLS: usize = 12 * 7; const HAND_PALM_HORIZONTAL_OFFSET: usize = HAND_FINGER_SENSOR_CELLS * 5; const HAND_PALM_VERTICAL_OFFSET: usize = HAND_PALM_HORIZONTAL_OFFSET + 5 * 14; +const HAND_TIP_DOT_LOCAL_Y_OFFSET_PX: f32 = 14.0; // Each entry pins one miniature matrix to a fingertip in hand.png. // Coordinates are authored in source-image pixels so they are easy to tune by eye. @@ -1144,7 +1145,7 @@ fn build_hand_membrane_instances(image_width: f32, image_height: f32) -> Vec f32 { return 1.0 - smoothstep(0.0, softness, dist); } +fn dot_matrix(uv: vec2f, grid: vec2f, dot_radius: f32, dot_softness: f32) -> f32 { + let cell = fract(uv * grid) - vec2f(0.5, 0.5); + return 1.0 - smoothstep(dot_radius, dot_radius + dot_softness, length(cell)); +} + fn fingertip_film_alpha( local: vec2f, half_width: f32, @@ -419,14 +424,28 @@ fn vs_hand_membrane(vertex: DotVertexInput, instance: DotInstanceInput) -> HandM fn fs_hand_membrane(in: HandMembraneVertexOutput) -> @location(0) vec4f { let p = in.local; - let halo_shape = fingertip_film_alpha(p, 0.72, -0.39, 0.060); - let panel = fingertip_film_alpha(p, 0.66, -0.38, 0.040); - let rim_inner = fingertip_film_alpha(p, 0.60, -0.36, 0.040); - let clear_inner = fingertip_film_alpha(p, 0.49, -0.31, 0.180); + let halo_shape = fingertip_film_alpha(p, 0.72, -0.20, 0.055); + let panel = fingertip_film_alpha(p, 0.66, -0.22, 0.035); + let inner = fingertip_film_alpha(p, 0.58, -0.25, 0.045); + let clear_inner = fingertip_film_alpha(p, 0.48, -0.24, 0.160); let halo = clamp(halo_shape - panel, 0.0, 1.0); - let border = clamp(panel - rim_inner, 0.0, 1.0); - let edge_fade = pow(clamp(1.0 - clear_inner, 0.0, 1.0), 1.15) * panel; + let rim = clamp(panel - inner, 0.0, 1.0); + let edge_fade = pow(clamp(1.0 - clear_inner, 0.0, 1.0), 1.20) * panel; + + // Strong pseudo extrusion / bevel. + let depth_offset = vec2f(0.045, 0.055); + let back_shape_1 = fingertip_film_alpha(p - depth_offset * 0.55, 0.66, -0.22, 0.045); + let back_shape_2 = fingertip_film_alpha(p - depth_offset, 0.66, -0.22, 0.060); + let extrusion = clamp(max(back_shape_1, back_shape_2) - panel, 0.0, 1.0); + + let bevel_offset = vec2f(0.028, 0.034); + let shifted_down_right = fingertip_film_alpha(p - bevel_offset, 0.66, -0.22, 0.035); + let bevel_light = clamp(panel - shifted_down_right, 0.0, 1.0); + let shifted_up_left = fingertip_film_alpha(p + bevel_offset, 0.66, -0.22, 0.035); + let bevel_dark = clamp(panel - shifted_up_left, 0.0, 1.0); + + let broad_bevel = edge_fade * clamp(0.58 - p.x * 0.20 - p.y * 0.18, 0.0, 1.0); // Micro lattice. let uv = clamp( @@ -437,56 +456,58 @@ fn fs_hand_membrane(in: HandMembraneVertexOutput) -> @location(0) vec4f { let grid = vec2f(18.0, 34.0); let cell_uv = fract(uv * grid) - vec2f(0.5, 0.5); - - let node = 1.0 - smoothstep( - 0.070, - 0.165, - length(cell_uv * vec2f(1.0, 1.08)), - ); - - let mesh_x = smoothstep(0.455, 0.495, abs(cell_uv.x)); - let mesh_y = smoothstep(0.455, 0.495, abs(cell_uv.y)); - let mesh = max(mesh_x, mesh_y); - let lattice = (node * 0.90 + mesh * 0.10) * rim_inner; - - // Blue glass highlights. - let side_glow = smoothstep(0.28, 0.66, abs(p.x)) * panel; - let top_light = smoothstep(0.10, 0.78, -p.y) * panel; + let dot_dist = length(cell_uv * vec2f(1.0, 1.06)); + let dot_aa = max(fwidth(dot_dist) * 1.35, 0.006); + let dots = (1.0 - smoothstep(0.105 - dot_aa, 0.105 + dot_aa, dot_dist)) * inner; let sheen_axis = p.x * 0.88 + p.y * 0.22 + 0.16; - let sheen = (1.0 - smoothstep(0.0, 0.11, abs(sheen_axis))) * panel; - let scan = (0.5 + 0.5 * sin((uv.y * 76.0 + uv.x * 9.0) * 6.28318)) * 0.025; + let sheen = (1.0 - smoothstep(0.018, 0.105, abs(sheen_axis))) * panel; + let shell_sheen = sheen * (0.26 + edge_fade * 0.74); // Temporary synthetic pressure hotspot. Real hand pressure dots are drawn above this pass. - let pressure_center = vec2f(-0.10, -0.15); - let pressure_dist = length((p - pressure_center) * vec2f(1.0, 0.72)); - let pressure = (1.0 - smoothstep(0.04, 0.37, pressure_dist)) * rim_inner; - let pressure_hot = (1.0 - smoothstep(0.015, 0.13, pressure_dist)) * rim_inner; + let pressure_center = vec2f(-0.08, -0.12); + let pressure_dist = length((p - pressure_center) * vec2f(1.0, 0.74)); + let pressure = (1.0 - smoothstep(0.05, 0.36, pressure_dist)) * inner; + let pressure_hot = (1.0 - smoothstep(0.02, 0.13, pressure_dist)) * inner; - let membrane_color = vec3f(0.006, 0.028, 0.110); - let inner_cyanblue = vec3f(0.012, 0.110, 0.320); - let lattice_color = vec3f(0.040, 0.420, 0.760); - let rim_color = vec3f(0.120, 0.720, 0.950); + let glass_base = vec3f(0.006, 0.055, 0.105); + let glass_cyan = vec3f(0.025, 0.42, 0.58); + let rim_color = vec3f(0.10, 0.88, 1.00); + let extrusion_color = vec3f(0.004, 0.080, 0.110); + let extrusion_edge_color = vec3f(0.015, 0.30, 0.38); + let bevel_highlight = vec3f(0.48, 1.00, 1.00); + let bevel_dark_color = vec3f(0.004, 0.035, 0.060); + let dot_color = vec3f(0.08, 0.48, 0.58); + let pressure_green = vec3f(0.01, 0.78, 0.23); + let pressure_hot_color = vec3f(0.20, 1.00, 0.42); - let pressure_green = vec3f(0.00, 0.66, 0.22); - let pressure_lime = vec3f(0.18, 1.00, 0.50); - - let color = membrane_color * panel * (0.36 + top_light * 0.08 + scan) - + inner_cyanblue * edge_fade * 0.72 - + lattice_color * lattice * 0.66 - + rim_color * (border * 0.90 + edge_fade * 0.16 + halo * 0.16 + side_glow * 0.08 + sheen * 0.10) - + pressure_green * pressure * 0.70 - + pressure_lime * pressure_hot * 0.82; + let color = extrusion_color * extrusion * 0.90 + + extrusion_edge_color * extrusion * halo_shape * 0.32 + + glass_base * panel * 0.26 + + glass_cyan * edge_fade * 0.46 + + glass_cyan * broad_bevel * 0.24 + + rim_color * rim * 0.88 + + bevel_highlight * bevel_light * 1.35 + + bevel_dark_color * bevel_dark * 0.95 + + bevel_highlight * shell_sheen * 0.28 + + rim_color * halo * 0.16 + + dot_color * dots * 0.68 + + pressure_green * dots * pressure * 0.82 + + pressure_hot_color * dots * pressure_hot * 0.90; let alpha = clamp( - panel * 0.08 - + edge_fade * 0.43 - + border * 0.20 - + halo * 0.04 - + lattice * 0.055 - + pressure * 0.05, + extrusion * 0.44 + + panel * 0.055 + + edge_fade * 0.30 + + rim * 0.34 + + bevel_light * 0.34 + + bevel_dark * 0.20 + + shell_sheen * 0.09 + + halo * 0.045 + + dots * 0.09 + + dots * pressure * 0.10, 0.0, - 0.78, + 0.90, ); return output_color(color, alpha); @@ -561,32 +582,47 @@ fn vs_hand_palm_chip(vertex: DotVertexInput, instance: DotInstanceInput) -> Hand @fragment fn fs_hand_palm_chip(in: HandPalmChipVertexOutput) -> @location(0) vec4f { - // Dark rounded tile: this is the inset chip body sitting inside the palm surface. - let panel = rounded_rect_alpha(in.local, 0.10, 0.040); - let inset = rounded_rect_alpha(in.local * vec2f(1.10, 1.08), 0.08, 0.052); - let rim = clamp(panel - inset * 0.72, 0.0, 1.0); + // Inactive palm matrix: only independent dots, with no filled patch underneath. + let panel = rounded_rect_alpha(in.local, 0.11, 0.040); + let inner = rounded_rect_alpha(in.local * vec2f(1.08, 1.08), 0.09, 0.052); - // Inactive chip pixels use the chip's real hand layout: - // horizontal 14 columns x 5 rows, or vertical 4 columns x 11 rows. let uv = clamp(in.local * 0.5 + vec2f(0.5, 0.5), vec2f(0.0, 0.0), vec2f(1.0, 1.0)); - let cell = abs(fract(uv * in.grid) - vec2f(0.5, 0.5)); - let micro_pixel = 1.0 - smoothstep(0.105, 0.178, length(cell * vec2f(1.04, 0.94))); + let patch_mask = smoothstep(0.0, 0.36, inner) * panel; - let top_bevel = smoothstep(-0.96, -0.18, -in.local.y) * 0.16; - let lower_shadow = smoothstep(0.20, 0.92, in.local.y) * 0.22; - let side_bevel = smoothstep(0.58, 0.96, abs(in.local.x)) * 0.12; - let scan = (0.5 + 0.5 * sin((uv.y * 36.0 + uv.x * 7.0) * 6.28318)) * 0.026; + let grid = max(in.grid * 1.70, vec2f(1.0, 1.0)); + let cell_uv = fract(uv * grid) - vec2f(0.5, 0.5); + let dot_dist = length(cell_uv); + let dot_aa = max(fwidth(dot_dist) * 1.5, 0.008); + let dots = 1.0 - smoothstep(0.135 - dot_aa, 0.135 + dot_aa, dot_dist); + let dot_glow = 1.0 - smoothstep(0.14, 0.28, dot_dist); + let visible_dots = dots * patch_mask; + let visible_glow = dot_glow * patch_mask; - let base = vec3f(0.004, 0.012, 0.018); - let glass = vec3f(0.012, 0.048, 0.064); - let pixel_color = vec3f(0.075, 0.300, 0.360); - let rim_color = vec3f(0.060, 0.560, 0.670); - let color = base * (0.92 - lower_shadow) - + glass * (0.52 + top_bevel + side_bevel + scan) - + pixel_color * micro_pixel * 0.70 - + rim_color * rim * 0.60; + // Synthetic low-level pressure only changes nearby dots; it does not fill the patch. + let horizontal = step(in.grid.y, in.grid.x); + let pressure_center = mix(vec2f(0.0, 0.0), vec2f(-0.10, 0.0), horizontal); + let pressure_scale = mix(vec2f(1.00, 0.82), vec2f(0.82, 1.00), horizontal); + let pressure_dist = length((in.local - pressure_center) * pressure_scale); + let pressure = (1.0 - smoothstep(0.06, 0.32, pressure_dist)) * patch_mask; + let pressure_hot = (1.0 - smoothstep(0.02, 0.14, pressure_dist)) * patch_mask; - let alpha = panel * (0.64 + micro_pixel * 0.18 + rim * 0.20); + let idle_dot_color = vec3f(0.18, 0.32, 0.38); + let active_dot_color = vec3f(0.02, 0.88, 0.34); + let hot_dot_color = vec3f(0.28, 1.00, 0.52); + + let dot_color = mix(idle_dot_color, active_dot_color, pressure); + + let color = dot_color * visible_dots * 1.25 + + hot_dot_color * visible_dots * pressure_hot * 0.85 + + active_dot_color * visible_glow * pressure * 0.14; + + let alpha = clamp( + visible_dots * (0.46 + pressure * 0.38) + + visible_dots * pressure_hot * 0.22 + + visible_glow * pressure * 0.045, + 0.0, + 0.96, + ); return output_color(color, alpha); }