Skip to content

Add SpectralColor type#24447

Open
coreh wants to merge 6 commits into
bevyengine:mainfrom
coreh:spectral-color
Open

Add SpectralColor type#24447
coreh wants to merge 6 commits into
bevyengine:mainfrom
coreh:spectral-color

Conversation

@coreh
Copy link
Copy Markdown
Contributor

@coreh coreh commented May 26, 2026

Note: This PR has been extracted from #14822


Objective

  • Add a special type for representing and working with spectral colors (composed of single wavelength light)

Solution

  • Introduces a new SpectralColor type, along with a CIE 1931 lookup table for 2-deg XYZ color matching function for conversion to tristimulus color spaces

Testing

  • Did you test these changes? If so, how? Yes, unit tests
  • Are there any parts that need more testing? Not that I know of
  • How can other people (reviewers) test your changes? Is there anything specific they need to know? They can use the snippet below to play with it
  • If relevant, what platforms did you test these changes on, and are there any important ones you can't test? macOS
use bevy_color::{SpectralColor, LinearRgba};

fn main() {
    let mut wavelength = 360.0;
    while wavelength <= 830.0 {
        let spectral = SpectralColor::wavelength(wavelength);
        let rgb = LinearRgba::from(spectral);
        println!(
            "{:>5.0} nm -> rgb({:.3}, {:.3}, {:.3})",
            wavelength, rgb.red, rgb.green, rgb.blue
        );
        wavelength += 1.0;
    }
}

@coreh coreh added C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes A-Color Color spaces and color math labels May 26, 2026
Copy link
Copy Markdown
Contributor

@amtep amtep left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope you don't mind that I reviewed a draft :) I have some doc fixes and one question.

Comment thread crates/bevy_color/src/spectral.rs Outdated
Comment thread crates/bevy_color/src/spectral.rs Outdated
Comment thread crates/bevy_color/src/spectral.rs Outdated

let xyza = Xyza::new(x, y, z, 1.0);

let mut linear = Color::from(xyza).to_linear();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked, and for rows 43 to 57 this generates a linear color with the red value above 1.0, with a max of 2.5166698. Is that okay? LinearRgba says [0.0, 1.0] in its docs for the fields.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so? Internally the shaders support and heavily use colors above 1 for highlights, etc, and for things like highlights, emissive, etc. Might be more of a convention than a strict rule? The conversion of LinearRgba to u8 channel values clamps it to 0..=255 per channel, which might be the one real downside here as hue can shift.

Some examples currently use use values > 1.0 directly, so this would not be introducing anything unusual:

emissive: LinearRgba::rgb(1000.0, 1000.0, 1000.0),

@alice-i-cecile alice-i-cecile added X-Uncontroversial This work is generally agreed upon S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels May 27, 2026
Comment thread crates/bevy_color/src/spectral.rs Outdated
/ SpectralColor::CIE_1931_NM_CMF_LOOKUP_TABLE_INCREMENT)
.floor() as usize;

let lerp = (value.wavelength - SpectralColor::CIE_1931_NM_CMF_LOOKUP_TABLE_START)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a fan of spec colors just looking through the lookup table interpolation here... wouldn't it be a bit cleaner to avoid the floating point modulo (%) and redundant math by leveraging .fract()? I use the same CIE 1931 table in a WGSL shader and ended up with this form (an experiment): 🙂
https://github.com/altunenes/cuneus/blob/main/examples/shaders/buddhabrot.wgsl

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yeah. I ended up using .floor(), storing an intermediate result and dropping the extra operation entirely.

There's also apparently a closed formula approximation (without lookup table) that @valaphee had mentioned on the original PR, but the link is now gone.

@coreh coreh changed the title WIP: Add SpectralColor type Add SpectralColor type May 31, 2026
@coreh coreh marked this pull request as ready for review May 31, 2026 23:01
@coreh coreh added S-Needs-Review Needs reviewer attention (from anyone!) to move forward and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Color Color spaces and color math C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward X-Uncontroversial This work is generally agreed upon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants