bevy/crates
Nicola Papale 5d8351d9e7 Replace AHash with a good sequence for entity AABB colors (#9175)
# Objective

- #8960 isn't optimal for very distinct AABB colors, it can be improved

## Solution

We want a function that maps sequential values (entities concurrently
living in a scene _usually_ have ids that are sequential) into very
different colors (the hue component of the color, to be specific)

What we are looking for is a [so-called "low discrepancy"
sequence](https://en.wikipedia.org/wiki/Low-discrepancy_sequence). ie: a
function `f` such as for integers in a given range (eg: 101, 102, 103…),
`f(i)` returns a rational number in the [0..1] range, such as `|f(i) -
f(i±1)| ≈ 0.5` (maximum difference of images for neighboring preimages)

AHash is a good random hasher, but it has relatively high discrepancy,
so we need something else.
Known good low discrepancy sequences are:

#### The [Van Der Corput
sequence](https://en.wikipedia.org/wiki/Van_der_Corput_sequence)

<details><summary>Rust implementation</summary>

```rust
fn van_der_corput(bits: u64) -> f32 {
    let leading_zeros = if bits == 0 { 0 } else { bits.leading_zeros() };
    let nominator = bits.reverse_bits() >> leading_zeros;
    let denominator = bits.next_power_of_two();

    nominator as f32 / denominator as f32
}
```

</details>

#### The [Gold Kronecker
sequence](https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/)

<details><summary>Rust implementation</summary>

Note that the implementation suggested in the linked post assumes
floats, we have integers

```rust
fn gold_kronecker(bits: u64) -> f32 {
    const U64_MAX_F: f32 = u64::MAX as f32;
    // (u64::MAX / Φ) rounded down
    const FRAC_U64MAX_GOLDEN_RATIO: u64 = 11400714819323198485;
    bits.wrapping_mul(FRAC_U64MAX_GOLDEN_RATIO) as f32 / U64_MAX_F
}
```

</details>

### Comparison of the sequences

So they are both pretty good. Both only have a single (!) division and
two `u32 as f32` conversions.

- Kronecker is resilient to regular sequence (eg: 100, 102, 104, 106)
while this kills Van Der Corput (consider that potentially one entity
out of two spawned might be a mesh)

I made a small app to compare the two sequences, available at:
https://gist.github.com/nicopap/5dd9bd6700c6a9a9cf90c9199941883e

At the top, we have Van Der Corput, at the bottom we have the Gold
Kronecker. In the video, we spawn a vertical line at the position on
screen where the x coordinate is the image of the sequence. The
preimages are 1,2,3,4,… The ideal algorithm would always have the
largest possible gap between each line (imagine the screen x coordinate
as the color hue):


https://github.com/bevyengine/bevy/assets/26321040/349aa8f8-f669-43ba-9842-f9a46945e25c

Here, we repeat the experiment, but with with `entity.to_bits()` instead
of a sequence:


https://github.com/bevyengine/bevy/assets/26321040/516cea27-7135-4daa-a4e7-edfd1781d119

Notice how Van Der Corput tend to bunch the lines on a single side of
the screen. This is because we always skip odd-numbered entities.

Gold Kronecker seems always worse than Van Der Corput, but it is
resilient to finicky stuff like entity indices being multiples of a
number rather than purely sequential, so I prefer it over Van Der
Corput, since we can't really predict how distributed the entity indices
will be.

### Chosen implementation

You'll notice this PR's implementation is not the Golden ratio-based
Kronecker sequence as described in
[tueoqs](https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/).
Why?

tueoqs R function multiplies a rational/float and takes the fractional
part of the result `(x/Φ) % 1`. We start with an integer `u32`. So
instead of converting into float and dividing by Φ (mod 1) we directly
divide by Φ as integer (mod 2³²) both operations are equivalent, the
integer division (which is actually a multiplication by `u32::MAX / Φ`)
is probably faster.

## Acknowledgements

- `inspi` on discord linked me to
https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
and the wikipedia article.
- [this blog
post](https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/)
for the idea of multiplying the `u32` rather than the `f32`.
- `nakedible` for suggesting the `index()` over `to_bits()` which
considerably reduces generated code (goes from 50 to 11 instructions)
2023-08-09 18:02:02 -07:00
..
bevy_a11y Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_animation Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_app Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_asset doc(asset): fix asset trait example (#9105) 2023-08-09 18:00:45 -07:00
bevy_audio Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_core Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_core_pipeline Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_derive Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_diagnostic Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_dylib Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_dynamic_plugin Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_ecs Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_ecs_compile_fail_tests Resolve clippy issues for rust 1.70.0 (#8738) 2023-06-01 21:05:05 +00:00
bevy_encase_derive Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_gilrs Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_gizmos Replace AHash with a good sequence for entity AABB colors (#9175) 2023-08-09 18:02:02 -07:00
bevy_gltf Add GltfLoader::new. (#9120) 2023-08-09 18:01:11 -07:00
bevy_hierarchy Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_input Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_internal Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_log Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_macro_utils Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_macros_compile_fail_tests bevy_derive: Add #[deref] attribute (#8552) 2023-05-16 18:29:09 +00:00
bevy_math Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_mikktspace Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_pbr Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_ptr Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_reflect Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_reflect_compile_fail_tests bevy_reflect: FromReflect Ergonomics Implementation (#6056) 2023-06-29 01:31:34 +00:00
bevy_render fix module name for AssetPath shaders (#9186) 2023-08-09 18:01:46 -07:00
bevy_scene Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_sprite Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_tasks Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_text Fix for vertical text bounds and alignment (#9133) 2023-08-09 18:00:58 -07:00
bevy_time Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_transform Update GlobalTransform on insertion (#9081) 2023-08-09 18:00:29 -07:00
bevy_ui Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_utils Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_window Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00
bevy_winit Release 0.11.0 (#9080) 2023-07-09 08:43:47 +00:00