`bevy_math`: `ViewFrustum` implementation is incorrect
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
## Bevy 0.19
bevy_math: The implementation of `ViewFrustum` seems to be incorrect.
But I'm puzzled as to why the rendering system can still work normally if there's a design error here. Hope it's just my misunderstanding.
In the right-handed coordinate system, a camera with a near plane distance of 1 and a far plane distance of 100 is set, looking towards the -Z direction. The calculated results are as follows:
```text
=== from_clip_from_world ===
0: Left: Vec4(0.8660254, -0.0, -0.5, 0.0)
1: Right: Vec4(-0.8660254, 0.0, -0.5, 0.0)
2: Top: Vec4(0.0, -0.8660254, -0.5, 0.0)
3: Bottom: Vec4(0.0, 0.8660254, -0.5, 0.0)
4: Near: Vec4(0.0, -0.0, -1.0, -0.5025126)
5: Far: Vec4(0.0, -0.0, -1.0, -1.0)
=== from_clip_from_world_custom_far ===
0: Left: Vec4(0.8660254, -0.0, -0.5, 0.0)
1: Right: Vec4(-0.8660254, 0.0, -0.5, 0.0)
2: Top: Vec4(0.0, -0.8660254, -0.5, 0.0)
3: Bottom: Vec4(0.0, 0.8660254, -0.5, 0.0)
4: Near: Vec4(0.0, -0.0, -1.0, -0.5025126)
5: Far: Vec4(0.0, 0.0, 1.0, 100.0)
```
1. The result of "Near" is incorrect.
2. In `from_clip_from_world`, the result of "Far" is incorrect. And even more exaggerated is that the normal vectors of the "Near" and "Far" planes are the same.
In the `from_clip_from_world_no_far` function, the `Near` term should be `row2` instead of `row3+row2`.
In the `from_clip_from_world` function, the `Far` value should be `row3 - row2`, instead of `row2`.
test code:
```rust
let mut clip_from_world = Mat4::perspective_rh(60.0_f32.to_radians(), 1.0, 1.0, 100.0);
clip_from_world.y_axis = -clip_from_world.y_axis; // Flip the Y axis downwards.
let frustum = ViewFrustum::from_clip_from_world(&clip_from_world);
std::eprintln!("\n=== from_clip_from_world ===");
for (i, name) in ["0: Left", "1: Right", "2: Top", "3: Bottom", "4: Near", "5: Far"].iter().enumerate() {
std::eprintln!("{}: {:?}", name, frustum.half_spaces[i].normal_d());
}
let frustum = ViewFrustum::from_clip_from_world_custom_far(
&clip_from_world,
&Vec3::ZERO,
&Vec3 { x: 0.0, y: 0.0, z: 1.0 },
100.0
);
std::eprintln!("\n=== from_clip_from_world_custom_far ===");
for (i, name) in ["0: Left", "1: Right", "2: Top", "3: Bottom", "4: Near", "5: Far"].iter().enumerate() {
std::eprintln!("{}: {:?}", name, frustum.half_spaces[i].normal_d());
}
```
Contributor guide
Research direction
Start by reading ViewFrustum::from_clip_from_world_no_far and ViewFrustum::from_clip_from_world, then run the Rust reproduction in the issue to inspect the extracted planes. Done means the near and far plane calculations match the stated row formulas and the reported normals and distances are correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- computer-graphics, game-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100