From 8813a338cd265d3a58fd14993891420767596a34 Mon Sep 17 00:00:00 2001 From: Baplar Date: Mon, 1 Jun 2026 23:33:56 +0200 Subject: [PATCH] Fix particles facing the wrong direction - On the CPU side, change the order of vertices to form counterclockwise triangles - On the GPU side, compensate this change by reversing relevant cross products --- .../shaders/materials/billboardunlit.wgsl | 33 ++++++++++--------- crates/renderide/src/particles/point.rs | 4 +-- crates/renderide/src/particles/tests.rs | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/crates/renderide/shaders/materials/billboardunlit.wgsl b/crates/renderide/shaders/materials/billboardunlit.wgsl index f89d5999d..782272baf 100644 --- a/crates/renderide/shaders/materials/billboardunlit.wgsl +++ b/crates/renderide/shaders/materials/billboardunlit.wgsl @@ -187,28 +187,31 @@ fn facing_basis(center_world: vec3, view_layer: u32, roll: f32, allow_roll: return RenderBufferBillboardBasis(right, up); } -fn local_particle_basis( +fn direction_stretch_particle_basis( d: dt::PerDrawUniforms, center_world: vec3, - pointdata: vec3, point_forward_upz: vec4, point_up_xy: vec2, view_layer: u32, - direction_stretch: bool, +) -> RenderBufferBillboardBasis { + let up = rmath::safe_normalize(mv::model_vector(d, point_forward_upz.xyz), vec3(0.0, 0.0, 1.0)); + let to_camera = rg::view_dir_for_world_pos(center_world, view_layer); + let right = rmath::safe_normalize(cross(up, to_camera), vec3(1.0, 0.0, 0.0)); + return RenderBufferBillboardBasis(right, up); +} + +fn local_particle_basis( + d: dt::PerDrawUniforms, + pointdata: vec3, + point_forward_upz: vec4, + point_up_xy: vec2, ) -> RenderBufferBillboardBasis { let raw_forward = rmath::safe_normalize(point_forward_upz.xyz, vec3(0.0, 0.0, 1.0)); let raw_up = rmath::safe_normalize(vec3(point_up_xy, point_forward_upz.w), vec3(0.0, 1.0, 0.0)); let world_forward = rmath::safe_normalize(mv::model_vector(d, raw_forward), vec3(0.0, 0.0, 1.0)); let world_up = rmath::safe_normalize(mv::model_vector(d, raw_up), vec3(0.0, 1.0, 0.0)); - if (direction_stretch) { - let to_camera = rg::view_dir_for_world_pos(center_world, view_layer); - let up = world_forward; - let right = rmath::safe_normalize(cross(to_camera, up), vec3(1.0, 0.0, 0.0)); - return RenderBufferBillboardBasis(right, up); - } - - var right = rmath::safe_normalize(cross(world_up, world_forward), vec3(1.0, 0.0, 0.0)); - var up = rmath::safe_normalize(cross(world_forward, right), world_up); + var right = rmath::safe_normalize(cross(world_forward, world_up), vec3(1.0, 0.0, 0.0)); + var up = rmath::safe_normalize(cross(right, world_forward), world_up); if (abs(pointdata.z) > 1e-4) { let rotated = rotate_render_buffer_axes(pointdata.z, right, up); right = rotated.right; @@ -227,13 +230,13 @@ fn render_buffer_billboard_basis( ) -> RenderBufferBillboardBasis { let alignment = pd::particle_alignment(d); if (alignment == 1u) { - return facing_basis(center_world, view_layer, pointdata.z, false); + return facing_basis(center_world, view_layer, pointdata.z, true); } if (alignment == 2u || alignment == 3u) { - return local_particle_basis(d, center_world, pointdata, point_forward_upz, point_up_xy, view_layer, false); + return local_particle_basis(d, pointdata, point_forward_upz, point_up_xy); } if (alignment == 4u) { - return local_particle_basis(d, center_world, pointdata, point_forward_upz, point_up_xy, view_layer, true); + return direction_stretch_particle_basis(d, center_world, point_forward_upz, point_up_xy, view_layer); } return view_plane_basis(view_layer, pointdata.z, true); } diff --git a/crates/renderide/src/particles/point.rs b/crates/renderide/src/particles/point.rs index 39dc31731..0a8411a28 100644 --- a/crates/renderide/src/particles/point.rs +++ b/crates/renderide/src/particles/point.rs @@ -310,11 +310,11 @@ fn fill_billboard_particle( indices, &[ base_vertex, - base_vertex + 2, base_vertex + 1, base_vertex + 2, - base_vertex + 3, + base_vertex + 2, base_vertex + 1, + base_vertex + 3, ], ); } diff --git a/crates/renderide/src/particles/tests.rs b/crates/renderide/src/particles/tests.rs index 16b8190e3..d2e0f112e 100644 --- a/crates/renderide/src/particles/tests.rs +++ b/crates/renderide/src/particles/tests.rs @@ -118,7 +118,7 @@ fn billboard_fill_writes_stable_point_indices() { fill_billboard_buffers(&points, glam::IVec2::new(2, 1), &mut vertices, &mut indices); let index_words: &[u32] = bytemuck::cast_slice(&indices); - assert_eq!(index_words, &[0, 2, 1, 2, 3, 1, 4, 6, 5, 6, 7, 5]); + assert_eq!(index_words, &[0, 1, 2, 2, 1, 3, 4, 5, 6, 6, 5, 7]); let first_vertex: &[f32] = bytemuck::cast_slice(&vertices[..generated_vertex_stride()]); assert_eq!(&first_vertex[..3], &[1.0, 2.0, 3.0]); assert_eq!(&first_vertex[3..5], &[0.5, 0.5]);