From a0b4968ad25573adcd609ad4f88a3ce2fc54b38f Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Mon, 22 Jun 2026 20:39:48 +0300 Subject: [PATCH] feat(render): relative-to-eye for mesh, image, face3d and hatch pipelines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch the remaining world-space shaders (mesh, image, face3d, hatch, hatch_batched) to the same relative-to-eye transform the wire pipeline uses: subtract the eye (high+low f32) per vertex and multiply by the rotation-only view-projection. Each shader's Uniforms struct is widened to the full shared layout so the appended RTE fields land at the correct offsets. Math is unchanged (proj·R·(pos−eye)); large coordinates no longer jitter on camera movement. Co-Authored-By: Claude Opus 4.8 --- src/shaders/face3d.wgsl | 19 +++++++++++++++---- src/shaders/hatch.wgsl | 21 +++++++++++++++------ src/shaders/hatch_batched.wgsl | 21 +++++++++++++++------ src/shaders/image.wgsl | 8 +++++++- src/shaders/mesh.wgsl | 25 +++++++++++++++---------- 5 files changed, 67 insertions(+), 27 deletions(-) diff --git a/src/shaders/face3d.wgsl b/src/shaders/face3d.wgsl index 10441dc39..520db70ec 100644 --- a/src/shaders/face3d.wgsl +++ b/src/shaders/face3d.wgsl @@ -5,9 +5,20 @@ // color [f32; 4] offset 12 16 B struct Uniforms { - view_proj: mat4x4, - viewport: vec2, - _pad: vec2, + view_proj: mat4x4, + camera_pos: vec4, + viewport_size: vec2, + world_per_pixel: f32, + lwdisplay_enable: f32, + flat_shade: f32, + transparency_enable: f32, + _pad: vec2, + // Relative-to-eye (double-single): see wire.wgsl. + view_rot: mat4x4, + eye_high: vec3, + _pad_eh: f32, + eye_low: vec3, + _pad_el: f32, }; @group(0) @binding(0) @@ -31,7 +42,7 @@ const DRAW_ORDER_BIAS: f32 = 0.001; @vertex fn vs_main(v: VertexIn) -> VertexOut { var out: VertexOut; - out.clip_pos = u.view_proj * vec4(v.position, 1.0); + out.clip_pos = u.view_rot * vec4((v.position - u.eye_high) - u.eye_low, 1.0); out.clip_pos.z = out.clip_pos.z - v.draw_depth * DRAW_ORDER_BIAS * out.clip_pos.w; out.color = v.color; return out; diff --git a/src/shaders/hatch.wgsl b/src/shaders/hatch.wgsl index f9569d96b..6ec6f3de5 100644 --- a/src/shaders/hatch.wgsl +++ b/src/shaders/hatch.wgsl @@ -13,11 +13,20 @@ // ── Group 0: frame uniforms (shared) ────────────────────────────────────── struct Uniforms { - view_proj: mat4x4, - camera_pos: vec4, - viewport_size: vec2, - world_per_pixel: f32, - _pad: f32, + view_proj: mat4x4, + camera_pos: vec4, + viewport_size: vec2, + world_per_pixel: f32, + lwdisplay_enable: f32, + flat_shade: f32, + transparency_enable: f32, + _pad: vec2, + // Relative-to-eye (double-single): see wire.wgsl. + view_rot: mat4x4, + eye_high: vec3, + _pad_eh: f32, + eye_low: vec3, + _pad_el: f32, } @group(0) @binding(0) var u: Uniforms; @@ -96,7 +105,7 @@ struct VOut { // fragment shader stays at full f32 precision so the in_polygon / // pattern math doesn't catastrophically cancel. let world = vec3(v.pos.x + h.origin.x, v.pos.y + h.origin.y, v.pos.z); - o.clip = u.view_proj * vec4(world, 1.0); + o.clip = u.view_rot * vec4((world - u.eye_high) - u.eye_low, 1.0); o.xz = vec2(v.pos.x, v.pos.y); return o; } diff --git a/src/shaders/hatch_batched.wgsl b/src/shaders/hatch_batched.wgsl index 49023c6e4..c80223bc3 100644 --- a/src/shaders/hatch_batched.wgsl +++ b/src/shaders/hatch_batched.wgsl @@ -18,11 +18,20 @@ // ── Group 0: shared frame uniforms (matches hatch.wgsl) ────────────────── struct Uniforms { - view_proj: mat4x4, - camera_pos: vec4, - viewport_size: vec2, - world_per_pixel: f32, - _pad: f32, + view_proj: mat4x4, + camera_pos: vec4, + viewport_size: vec2, + world_per_pixel: f32, + lwdisplay_enable: f32, + flat_shade: f32, + transparency_enable: f32, + _pad: vec2, + // Relative-to-eye (double-single): see wire.wgsl. + view_rot: mat4x4, + eye_high: vec3, + _pad_eh: f32, + eye_low: vec3, + _pad_el: f32, } @group(0) @binding(0) var u: Uniforms; @@ -126,7 +135,7 @@ fn corner_xy(c: u32, aabb: vec4) -> vec2 { let world = vec3(local.x + inst.world_origin.x, local.y + inst.world_origin.y, 0.0); - o.clip = u.view_proj * vec4(world, 1.0); + o.clip = u.view_rot * vec4((world - u.eye_high) - u.eye_low, 1.0); o.clip.z = o.clip.z - inst.draw_depth * DRAW_ORDER_BIAS * o.clip.w; o.xz = local; o.instance_index = v.instance_index; diff --git a/src/shaders/image.wgsl b/src/shaders/image.wgsl index a27769fda..e2dc8c182 100644 --- a/src/shaders/image.wgsl +++ b/src/shaders/image.wgsl @@ -12,6 +12,12 @@ struct Uniforms { flat_shade: f32, transparency_enable: f32, _pad: vec2, + // Relative-to-eye (double-single): see wire.wgsl. + view_rot: mat4x4, + eye_high: vec3, + _pad_eh: f32, + eye_low: vec3, + _pad_el: f32, }; @group(0) @binding(0) var u: Uniforms; @@ -46,7 +52,7 @@ struct VertOut { @vertex fn vs_main(in: VertIn) -> VertOut { var out: VertOut; - out.clip_pos = u.view_proj * vec4(in.pos, 1.0); + out.clip_pos = u.view_rot * vec4((in.pos - u.eye_high) - u.eye_low, 1.0); out.clip_pos.z = out.clip_pos.z - img_params.draw_depth * DRAW_ORDER_BIAS * out.clip_pos.w; out.uv = in.uv; return out; diff --git a/src/shaders/mesh.wgsl b/src/shaders/mesh.wgsl index 9b1faa486..b1eb5ba44 100644 --- a/src/shaders/mesh.wgsl +++ b/src/shaders/mesh.wgsl @@ -10,15 +10,20 @@ // flat shade (FlatShaded). struct Uniforms { - view_proj: mat4x4, - camera_pos: vec4, - viewport_size: vec2, - world_per_pixel: f32, - lwdisplay_enable: f32, - flat_shade: f32, - _pad0: f32, - _pad1: f32, - _pad2: f32, + view_proj: mat4x4, + camera_pos: vec4, + viewport_size: vec2, + world_per_pixel: f32, + lwdisplay_enable: f32, + flat_shade: f32, + transparency_enable: f32, + _pad: vec2, + // Relative-to-eye (double-single): see wire.wgsl. + view_rot: mat4x4, + eye_high: vec3, + _pad_eh: f32, + eye_low: vec3, + _pad_el: f32, }; @group(0) @binding(0) @@ -40,7 +45,7 @@ struct VertexOut { @vertex fn vs_main(v: VertexIn) -> VertexOut { var out: VertexOut; - out.clip_pos = u.view_proj * vec4(v.position, 1.0); + out.clip_pos = u.view_rot * vec4((v.position - u.eye_high) - u.eye_low, 1.0); out.color = v.color; out.normal = v.normal; out.world_pos = v.position;