diff --git a/crates/bevy_pbr/src/volumetric_fog/render.rs b/crates/bevy_pbr/src/volumetric_fog/render.rs index e0ddc3ccefe74..288871d0a7c73 100644 --- a/crates/bevy_pbr/src/volumetric_fog/render.rs +++ b/crates/bevy_pbr/src/volumetric_fog/render.rs @@ -818,7 +818,7 @@ fn get_far_planes(view_from_local: &Affine3A) -> [Vec4; 3] { continue; } - let view_position = view_from_local.transform_point3a(-local_normal * 0.5); + let view_position = view_from_local.transform_point3a(local_normal * 0.5); let plane_coords = view_normal.extend(-view_normal.dot(view_position)); far_planes[next_index] = plane_coords; @@ -890,3 +890,24 @@ fn calculate_fog_volume_clip_from_local_transforms( vec4(0.0, 0.0, z_near, z_near), ) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_far_planes_identity_has_correct_z_plane_position() { + let far_planes = get_far_planes(&Affine3A::IDENTITY); + + let z_plane = far_planes[0]; + assert_eq!(z_plane.truncate(), Vec3::Z); + assert!((z_plane.w + 0.5).abs() < 1e-6); + + let z_face_point = Vec3::new(0.0, 0.0, 0.5); + let plane_eval = z_plane.truncate().dot(z_face_point) + z_plane.w; + assert!(plane_eval.abs() < 1e-6); + + assert_eq!(far_planes[1], Vec4::ZERO); + assert_eq!(far_planes[2], Vec4::ZERO); + } +} diff --git a/crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl b/crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl index 12294cd088b1a..4e2471f378ca8 100644 --- a/crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl +++ b/crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl @@ -142,6 +142,10 @@ fn fragment(@builtin(position) position: vec4) -> @location(0) vec4 { // faces of the AABB, this is the current fragment's depth. let view_start_pos = position_ndc_to_view(frag_coord_to_ndc(frag_coord)); + // Calculate the ray direction in view space (needed for AABB intersection) + let Rd_ndc = vec3(frag_coord_to_ndc(position).xy, 1.0); + let Rd_view = normalize(position_ndc_to_view(Rd_ndc)); + // Calculate the end position of the ray. This requires us to raytrace the // three back faces of the AABB to find the one that our ray intersects. var end_depth_view = 0.0; @@ -150,13 +154,20 @@ fn fragment(@builtin(position) position: vec4) -> @location(0) vec4 { let other_plane_a = volumetric_fog.far_planes[(plane_index + 1) % 3]; let other_plane_b = volumetric_fog.far_planes[(plane_index + 2) % 3]; - // Calculate the intersection of the ray and the plane. The ray must - // intersect in front of us (t > 0). - let t = -plane.w / dot(plane.xyz, view_start_pos.xyz); + // Calculate the intersection of the ray and the plane using proper ray-plane intersection. + // Ray: P = view_start_pos + Rd_view * t + // Plane: dot(plane.xyz, P) + plane.w = 0 + let denom = dot(plane.xyz, Rd_view); + if (abs(denom) < 0.0001) { + // Ray is parallel to plane + continue; + } + let t = -(dot(plane.xyz, view_start_pos.xyz) + plane.w) / denom; if (t < 0.0) { + // Intersection is behind the ray start continue; } - let hit_pos = view_start_pos.xyz * t; + let hit_pos = view_start_pos.xyz + Rd_view * t; // The intersection point must be in front of the other backfaces. let other_sides = vec2( @@ -183,16 +194,13 @@ fn fragment(@builtin(position) position: vec4) -> @location(0) vec4 { let directional_light_count = lights.n_directional_lights; - // Calculate the ray origin (`Ro`) and the ray direction (`Rd`) in NDC, - // view, and world coordinates. - let Rd_ndc = vec3(frag_coord_to_ndc(position).xy, 1.0); - let Rd_view = normalize(position_ndc_to_view(Rd_ndc)); + // Calculate the ray origin (`Ro`) and the ray direction (`Rd`) in world coordinates. var Ro_world = position_view_to_world(view_start_pos.xyz); let Rd_world = normalize(position_ndc_to_world(Rd_ndc) - view.world_position); // Offset by jitter. let jitter = interleaved_gradient_noise(position.xy, globals.frame_count) * jitter_strength; - Ro_world += Rd_world * jitter; + Ro_world += Rd_world * jitter * step_size_world; // Use Beer's law [1] [2] to calculate the maximum amount of light that each // directional light could contribute, and modulate that value by the light