Skip to content

Spectral Color Lighting #14822

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open

Spectral Color Lighting #14822

wants to merge 20 commits into from

Conversation

coreh
Copy link
Contributor

@coreh coreh commented Aug 19, 2024

Objective

Lighting calculations in the PBR shader currently take place independently over R, G and B channels. (As is usual for 3D engines!)

This works well for approximating the common case of polychromatic light sources that cover a broad spectrum (e.g. daylight) but falls short when attempting to render scenes with monochromatic light sources, with spectral colors of a single wavelength (e.g. sodium vapor light).

This PR adds initial support for more accurately rendering of monochromatic light sources, using a new blending implementation based on hue and value. (Instead of the existing multiplicative blending implementation). Since this more complex blending has a cost associated and isn't needed for most applications, this feature is behind a spectral_lighting feature flag.

Solution

  • Add a new SpectralColor struct, describing a physically-based spectral color, with a single wavelength (in nanometers) and luminance (in candelas per square meter)
  • Add logic for converting from SpectralColor to Color, using the CIE 1931 XYZ color matching functions; (via a lookup table)
  • Add a new spectral_lighting feature flag, and a matching SPECTRAL_LIGHTING shader def;
  • Add a new monochromaticity field to PointLight, SpotLight, DirectionalLight, AmbientLight, to control how monochromatic a given light source is;
  • Add a new monochromaticity_blend() function, to blend colors in a way that more accurately matches the physical behavior of spectral lights. (Existing polychromatic lights are handled as a special case)
  • Update all lights to use monochromaticity_blend() whenever the SPECTRAL_LIGHTING shader def is enabled;

Some notes about the monochromaticity_blend() implementation:

  • Sending color information to the shaders in XYZ or spectral format would complicate the uniforms and require lots of changes to the PBR implementation, so the inputs of this function are still in (linear) RGB format
  • Since the conversion from linear RGB to spectral would be expensive/complex, we internally use HSV as a crude approximation of the color spectrum
  • All material base colors are assumed to be polychromatic (Adding support for monochromaticity for materials is relatively straightforward, but not super useful)
  • We use hue distance, plus value and saturation to calculate the final output. Our hue distance importantly does not wrap around. (Since magenta is a non-spectral light, we end up using hues values in 240° and beyond for violet and ultraviolet instead, and these shouldn't produce a response from reds)
  • A triangle function is used as an approximation of a gaussian function, to calculate the polychromatic RGB response to a monochromatic light based on the hue distance

Testing

  • Did you test these changes? If so, how?
  • Are there any parts that need more testing?
  • How can other people (reviewers) test your changes? Is there anything specific they need to know?
  • If relevant, what platforms did you test these changes on, and are there any important ones you can't test?

Showcase

With spectral color / monochromatic lighting:

image

Without spectral color / monochromatic lighting:

image
spectral.mov

Migration Guide

  • When using the spectral_lighting feature flag, the AmbientLight resource will have one extra property (monochromaticity). We recommend adding ..default() to AmbientLight (as is usual for other light types with more properties) to make sure your code compiles with the flag.

Copy link
Contributor

The generated examples/README.md is out of sync with the example metadata in Cargo.toml or the example readme template. Please run cargo run -p build-templated-pages -- update examples to update it, and commit the file change.

@TrialDragon TrialDragon added C-Feature A new feature, making something new possible A-Color Color spaces and color math labels Aug 19, 2024
@alice-i-cecile alice-i-cecile added A-Rendering Drawing game state to the screen S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Aug 19, 2024
Copy link
Contributor

The generated examples/README.md is out of sync with the example metadata in Cargo.toml or the example readme template. Please run cargo run -p build-templated-pages -- update examples to update it, and commit the file change.

Copy link
Contributor

You added a new feature but didn't update the readme. Please run cargo run -p build-templated-pages -- update features to update it, and commit the file change.

@coreh coreh marked this pull request as ready for review August 20, 2024 02:17
@tychedelia tychedelia added the D-Complex Quite challenging from either a design or technical perspective. Ask for help! label Aug 23, 2024
@IQuick143 IQuick143 added the M-Needs-Release-Note Work that should be called out in the blog due to impact label Aug 26, 2024
///
/// Source: <http://cvrl.ioo.ucl.ac.uk/plotcmfs.php>
#[allow(clippy::excessive_precision)]
const CIE_1931_XYZ_CMF_LOOKUP_TABLE: [[f32; 3]; 97] = [
Copy link
Contributor

@valaphee valaphee Aug 29, 2024

Choose a reason for hiding this comment

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

Might be better to use the approximations https://github.com/cadacoon/polylight/blob/main/src/color.rs#L88

there is probably not much of a speed difference on modern CPUs.

otherwise, really cool to see

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That link is not working for me 😕 Maybe it's a private repo, or they removed it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah sorry, corrected it

Copy link
Contributor

@JMS55 JMS55 left a comment

Choose a reason for hiding this comment

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

Example looks fine. Code mostly looks fine minus my comments. I'm going to assume the math is all correct.

I'm happy to merge this once my nits are addressed, meshlet support is fixed, and we remove the cargo feature.

@@ -316,6 +316,9 @@ pbr_multi_layer_material_textures = [
# Enable support for anisotropy texture in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs
pbr_anisotropy_texture = ["bevy_internal/pbr_anisotropy_texture"]

# Enable support for spectral colors (monochromatic light) in lights
spectral_lighting = ["bevy_internal/spectral_lighting"]
Copy link
Contributor

Choose a reason for hiding this comment

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

Why make this a cargo feature? Imo, the less features we have, the better, and we should only use them for things that can't be changed at runtime.

Can we make this a field on PbrPlugin instead?

use bevy_math::FloatExt;
use std::ops::Mul;

/// A color produced by monochromatic light (of a single wavelength)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// A color produced by monochromatic light (of a single wavelength)
/// A color produced by monochromatic light (of a single wavelength).

Maybe(?). Also in all the fields and other places.

@@ -255,6 +255,9 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
shader_defs.push("WEBGL2".into());

#[cfg(feature = "spectral_lighting")]
shader_defs.push("SPECTRAL_LIGHTING".into());
Copy link
Contributor

Choose a reason for hiding this comment

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

Shaderdefs need to be copied to the meshlet material pipeline stuff.

(Note to maintainers: This seems like a frequent problem with people forgetting meshlet stuff, idk how to prevent it other than me watching out for it 😓)

@@ -78,6 +78,7 @@ The default feature set enables most of the expected features of a game engine,
|serialize|Enable serialization support through serde|
|shader_format_glsl|Enable support for shaders in GLSL|
|shader_format_spirv|Enable support for shaders in SPIR-V|
|spectral_lighting|Enable support for spectral colors (monochromatic light) in lights|
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than monochromatic light, single wavelength light might be a better description? Idk what the usual terminology is.

@BenjaminBrienen
Copy link
Contributor

@coreh are you still interested in working on this?

@coreh
Copy link
Contributor Author

coreh commented Jan 23, 2025

Yeah. Sorry I went a bit radio silent on this, was busy with a couple other things. Looks like there's a lot of conflicts, but I have a more up-to-date version (as of the latest bevy release) of this on the integration branch of my fork of that I can extract and add force-push here.

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 A-Rendering Drawing game state to the screen C-Feature A new feature, making something new possible D-Complex Quite challenging from either a design or technical perspective. Ask for help! M-Needs-Release-Note Work that should be called out in the blog due to impact S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

8 participants