move wgsl color operations from bevy_pbr to bevy_render (#13209)

# Objective

`bevy_pbr/utils.wgsl` shader file contains mathematical constants and
color conversion functions. Both of those should be accessible without
enabling `bevy_pbr` feature. For example, tonemapping can be done in non
pbr scenario, and it uses color conversion functions.

Fixes #13207

## Solution

* Move mathematical constants (such as PI, E) from
`bevy_pbr/src/render/utils.wgsl` into `bevy_render/src/maths.wgsl`
* Move color conversion functions from `bevy_pbr/src/render/utils.wgsl`
into new file `bevy_render/src/color_operations.wgsl`

## Testing
Ran multiple examples, checked they are working:
* tonemapping
* color_grading
* 3d_scene
* animated_material
* deferred_rendering
* 3d_shapes
* fog
* irradiance_volumes
* meshlet
* parallax_mapping
* pbr
* reflection_probes
* shadow_biases
* 2d_gizmos
* light_gizmos
---

## Changelog
* Moved mathematical constants (such as PI, E) from
`bevy_pbr/src/render/utils.wgsl` into `bevy_render/src/maths.wgsl`
* Moved color conversion functions from `bevy_pbr/src/render/utils.wgsl`
into new file `bevy_render/src/color_operations.wgsl`

## Migration Guide
In user's shader code replace usage of mathematical constants from
`bevy_pbr::utils` to the usage of the same constants from
`bevy_render::maths`.
This commit is contained in:
arcashka 2024-05-04 13:30:23 +03:00 committed by GitHub
parent 40837501b4
commit 6027890a11
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 92 additions and 64 deletions

View file

@ -1,9 +1,10 @@
#import bevy_pbr::{
mesh_view_bindings,
forward_io::VertexOutput,
utils::PI,
}
#import bevy_render::maths::PI
#ifdef TONEMAP_IN_SHADER
#import bevy_core_pipeline::tonemapping::tone_mapping
#endif

View file

@ -1,7 +1,10 @@
#define_import_path bevy_core_pipeline::tonemapping
#import bevy_render::view::ColorGrading
#import bevy_pbr::utils::{PI_2, hsv_to_rgb, rgb_to_hsv};
#import bevy_render::{
view::ColorGrading,
color_operations::{hsv_to_rgb, rgb_to_hsv},
maths::PI_2
}
// hack !! not sure what to do with this
#ifdef TONEMAPPING_PASS

View file

@ -2,7 +2,12 @@
#import bevy_pbr::{
mesh_view_bindings as bindings,
utils::{PI_2, hsv_to_rgb, rand_f},
utils::rand_f,
}
#import bevy_render::{
color_operations::hsv_to_rgb,
maths::PI_2,
}
// NOTE: Keep in sync with bevy_pbr/src/light.rs

View file

@ -12,9 +12,10 @@
ambient,
irradiance_volume,
mesh_types::{MESH_FLAGS_SHADOW_RECEIVER_BIT, MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT},
utils::E,
}
#import bevy_render::maths::E
#ifdef ENVIRONMENT_MAP
#import bevy_pbr::environment_map
#endif

View file

@ -1,11 +1,12 @@
#define_import_path bevy_pbr::lighting
#import bevy_pbr::{
utils::PI,
mesh_view_types::POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE,
mesh_view_bindings as view_bindings,
}
#import bevy_render::maths::PI
// From the Filament design doc
// https://google.github.io/filament/Filament.html#table_symbols
// Symbol Definition

View file

@ -3,11 +3,13 @@
#import bevy_pbr::{
lighting,
prepass_utils,
utils::{PI, interleaved_gradient_noise},
utils::interleaved_gradient_noise,
utils,
mesh_view_bindings as view_bindings,
};
#import bevy_render::maths::PI
#import bevy_core_pipeline::tonemapping::{
approximate_inverse_tone_mapping
};

View file

@ -2,10 +2,10 @@
#import bevy_pbr::{
mesh_view_bindings as view_bindings,
utils::{PI, interleaved_gradient_noise},
utils::interleaved_gradient_noise,
utils,
}
#import bevy_render::maths::orthonormalize
#import bevy_render::maths::{orthonormalize, PI}
// Do the lookup, using HW 2x2 PCF and comparison
fn sample_shadow_map_hardware(light_local: vec2<f32>, depth: f32, array_index: i32) -> f32 {

View file

@ -3,10 +3,14 @@
#import bevy_pbr::{
mesh_view_types::POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE,
mesh_view_bindings as view_bindings,
utils::{hsv_to_rgb, PI_2},
shadow_sampling::{SPOT_SHADOW_TEXEL_SIZE, sample_shadow_cubemap, sample_shadow_map}
}
#import bevy_render::{
color_operations::hsv_to_rgb,
maths::PI_2
}
const flip_z: vec3<f32> = vec3<f32>(1.0, 1.0, -1.0);
fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {

View file

@ -2,55 +2,6 @@
#import bevy_pbr::rgb9e5
const PI: f32 = 3.141592653589793; // π
const PI_2: f32 = 6.283185307179586; // 2π
const HALF_PI: f32 = 1.57079632679; // π/2
const FRAC_PI_3: f32 = 1.0471975512; // π/3
const E: f32 = 2.718281828459045; // exp(1)
// Converts HSV to RGB.
//
// Input: H [0, 2π), S [0, 1], V [0, 1].
// Output: R [0, 1], G [0, 1], B [0, 1].
//
// <https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB_alternative>
fn hsv_to_rgb(hsv: vec3<f32>) -> vec3<f32> {
let n = vec3(5.0, 3.0, 1.0);
let k = (n + hsv.x / FRAC_PI_3) % 6.0;
return hsv.z - hsv.z * hsv.y * max(vec3(0.0), min(k, min(4.0 - k, vec3(1.0))));
}
// Converts RGB to HSV.
//
// Input: R [0, 1], G [0, 1], B [0, 1].
// Output: H [0, 2π), S [0, 1], V [0, 1].
//
// <https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB>
fn rgb_to_hsv(rgb: vec3<f32>) -> vec3<f32> {
let x_max = max(rgb.r, max(rgb.g, rgb.b)); // i.e. V
let x_min = min(rgb.r, min(rgb.g, rgb.b));
let c = x_max - x_min; // chroma
var swizzle = vec3<f32>(0.0);
if (x_max == rgb.r) {
swizzle = vec3(rgb.gb, 0.0);
} else if (x_max == rgb.g) {
swizzle = vec3(rgb.br, 2.0);
} else {
swizzle = vec3(rgb.rg, 4.0);
}
let h = FRAC_PI_3 * (((swizzle.x - swizzle.y) / c + swizzle.z) % 6.0);
// Avoid division by zero.
var s = 0.0;
if (x_max > 0.0) {
s = c / x_max;
}
return vec3(h, s, x_max);
}
// Generates a random u32 in range [0, u32::MAX].
//
// `state` is a mutable reference to a u32 used as the seed.

View file

@ -5,13 +5,12 @@
// Source code heavily based on XeGTAO v1.30 from Intel
// https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli
#import bevy_pbr::{
gtao_utils::fast_acos,
utils::{PI, HALF_PI},
}
#import bevy_pbr::gtao_utils::fast_acos
#import bevy_render::{
view::View,
globals::Globals,
maths::{PI, HALF_PI},
}
@group(0) @binding(0) var preprocessed_depth: texture_2d<f32>;

View file

@ -1,6 +1,6 @@
#define_import_path bevy_pbr::gtao_utils
#import bevy_pbr::utils::{PI, HALF_PI}
#import bevy_render::maths::{PI, HALF_PI}
// Approximates single-bounce ambient occlusion to multi-bounce ambient occlusion
// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf#page=78

View file

@ -0,0 +1,47 @@
#define_import_path bevy_render::color_operations
#import bevy_render::maths::FRAC_PI_3
// Converts HSV to RGB.
//
// Input: H [0, 2π), S [0, 1], V [0, 1].
// Output: R [0, 1], G [0, 1], B [0, 1].
//
// <https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB_alternative>
fn hsv_to_rgb(hsv: vec3<f32>) -> vec3<f32> {
let n = vec3(5.0, 3.0, 1.0);
let k = (n + hsv.x / FRAC_PI_3) % 6.0;
return hsv.z - hsv.z * hsv.y * max(vec3(0.0), min(k, min(4.0 - k, vec3(1.0))));
}
// Converts RGB to HSV.
//
// Input: R [0, 1], G [0, 1], B [0, 1].
// Output: H [0, 2π), S [0, 1], V [0, 1].
//
// <https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB>
fn rgb_to_hsv(rgb: vec3<f32>) -> vec3<f32> {
let x_max = max(rgb.r, max(rgb.g, rgb.b)); // i.e. V
let x_min = min(rgb.r, min(rgb.g, rgb.b));
let c = x_max - x_min; // chroma
var swizzle = vec3<f32>(0.0);
if (x_max == rgb.r) {
swizzle = vec3(rgb.gb, 0.0);
} else if (x_max == rgb.g) {
swizzle = vec3(rgb.br, 2.0);
} else {
swizzle = vec3(rgb.rg, 4.0);
}
let h = FRAC_PI_3 * (((swizzle.x - swizzle.y) / c + swizzle.z) % 6.0);
// Avoid division by zero.
var s = 0.0;
if (x_max > 0.0) {
s = c / x_max;
}
return vec3(h, s, x_max);
}

View file

@ -236,6 +236,8 @@ pub struct RenderApp;
pub const INSTANCE_INDEX_SHADER_HANDLE: Handle<Shader> =
Handle::weak_from_u128(10313207077636615845);
pub const MATHS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(10665356303104593376);
pub const COLOR_OPERATIONS_SHADER_HANDLE: Handle<Shader> =
Handle::weak_from_u128(1844674407370955161);
impl Plugin for RenderPlugin {
/// Initializes the renderer, sets up the [`RenderSet`] and creates the rendering sub-app.
@ -359,6 +361,12 @@ impl Plugin for RenderPlugin {
fn finish(&self, app: &mut App) {
load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl);
load_internal_asset!(
app,
COLOR_OPERATIONS_SHADER_HANDLE,
"color_operations.wgsl",
Shader::from_wgsl
);
if let Some(future_renderer_resources) =
app.world_mut().remove_resource::<FutureRendererResources>()
{

View file

@ -1,5 +1,11 @@
#define_import_path bevy_render::maths
const PI: f32 = 3.141592653589793; // π
const PI_2: f32 = 6.283185307179586; // 2π
const HALF_PI: f32 = 1.57079632679; // π/2
const FRAC_PI_3: f32 = 1.0471975512; // π/3
const E: f32 = 2.718281828459045; // exp(1)
fn affine2_to_square(affine: mat3x2<f32>) -> mat3x3<f32> {
return mat3x3<f32>(
vec3<f32>(affine[0].xy, 0.0),