From acb504ec4a5ea6ed394993af4b14b5770d2f9ab0 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 28 Sep 2022 04:20:27 +0000 Subject: [PATCH] add globals to mesh view bind group (#5409) # Objective - It's often really useful to have access to the time when writing shaders. ## Solution - Add a UnifformBuffer in the mesh view bind group - This buffer contains the time, delta time and a wrapping frame count https://user-images.githubusercontent.com/8348954/180130314-97948c2a-2d11-423d-a9c4-fb5c9d1892c7.mp4 --- ## Changelog - Added a `GlobalsUniform` at position 9 of the mesh view bind group ## Notes The implementation is currently split between bevy_render and bevy_pbr because I was basing my implementation on the `ViewPlugin`. I'm not sure if that's the right way to structure it. I named this `globals` instead of just time because we could potentially add more things to it. ## References in other engines - Godot: - Global time since startup, in seconds, by default resets to 0 after 3600 seconds - Doesn't seem to have anything else - Unreal: - Generic time value that updates every frame. Can be paused or scaled. - Frame count node, doesn't seem to be an equivalent for shaders: - Unity: - time since startup in seconds. No mention of time wrapping. Stored as a `vec4(t/20, t, t*2, t*3)` where `t` is the value in seconds - Also has delta time, sin time and cos time - ShaderToy: - iTime is the time since startup in seconds. - iFrameRate - iTimeDelta - iFrame frame counter Co-authored-by: Charles --- assets/shaders/animate_shader.wgsl | 58 ++-- crates/bevy_core/src/lib.rs | 9 + crates/bevy_diagnostic/Cargo.toml | 1 + .../src/frame_time_diagnostics_plugin.rs | 22 +- crates/bevy_pbr/src/render/mesh.rs | 20 +- .../src/render/mesh_view_bindings.wgsl | 3 + .../bevy_pbr/src/render/mesh_view_types.wgsl | 11 + crates/bevy_render/src/globals.rs | 67 +++++ crates/bevy_render/src/lib.rs | 26 +- examples/shader/animate_shader.rs | 268 ++---------------- 10 files changed, 175 insertions(+), 310 deletions(-) create mode 100644 crates/bevy_render/src/globals.rs diff --git a/assets/shaders/animate_shader.wgsl b/assets/shaders/animate_shader.wgsl index 947c4a5420aff8..6726fa6263cc6e 100644 --- a/assets/shaders/animate_shader.wgsl +++ b/assets/shaders/animate_shader.wgsl @@ -1,39 +1,7 @@ #import bevy_pbr::mesh_types +// The time since startup data is in the globals binding which is part of the mesh_view_bindings import #import bevy_pbr::mesh_view_bindings -@group(1) @binding(0) -var mesh: Mesh; - -// NOTE: Bindings must come before functions that use them! -#import bevy_pbr::mesh_functions - -struct Vertex { - @location(0) position: vec3, - @location(1) normal: vec3, - @location(2) uv: vec2, -}; - -struct VertexOutput { - @builtin(position) clip_position: vec4, - @location(0) uv: vec2, -}; - -@vertex -fn vertex(vertex: Vertex) -> VertexOutput { - var out: VertexOutput; - out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); - out.uv = vertex.uv; - return out; -} - - -struct Time { - time_since_startup: f32, -}; -@group(2) @binding(0) -var time: Time; - - fn oklab_to_linear_srgb(c: vec3) -> vec3 { let L = c.x; let a = c.y; @@ -43,22 +11,28 @@ fn oklab_to_linear_srgb(c: vec3) -> vec3 { let m_ = L - 0.1055613458 * a - 0.0638541728 * b; let s_ = L - 0.0894841775 * a - 1.2914855480 * b; - let l = l_*l_*l_; - let m = m_*m_*m_; - let s = s_*s_*s_; + let l = l_ * l_ * l_; + let m = m_ * m_ * m_; + let s = s_ * s_ * s_; return vec3( - 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, - -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, - -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s, + 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, + -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, + -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s, ); } +struct FragmentInput { + #import bevy_pbr::mesh_vertex_output +} + @fragment -fn fragment(in: VertexOutput) -> @location(0) vec4 { +fn fragment(in: FragmentInput) -> @location(0) vec4 { let speed = 2.0; - let t_1 = sin(time.time_since_startup * speed) * 0.5 + 0.5; - let t_2 = cos(time.time_since_startup * speed); + // The globals binding contains various global values like time + // which is the time since startup in seconds + let t_1 = sin(globals.time * speed) * 0.5 + 0.5; + let t_2 = cos(globals.time * speed); let distance_to_center = distance(in.uv, vec2(0.5)) * 1.4; diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 7cc0735c98503f..8fc580848fce9b 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -4,6 +4,7 @@ mod name; mod task_pool_options; +use bevy_ecs::system::Resource; pub use bytemuck::{bytes_of, cast_slice, Pod, Zeroable}; pub use name::*; pub use task_pool_options::*; @@ -37,6 +38,8 @@ impl Plugin for CorePlugin { register_rust_types(app); register_math_types(app); + + app.init_resource::(); } } @@ -83,3 +86,9 @@ fn register_math_types(app: &mut App) { .register_type::() .register_type::(); } + +/// Keeps a count of rendered frames since the start of the app +/// +/// Wraps to 0 when it reaches the maximum u32 value +#[derive(Default, Resource, Clone, Copy)] +pub struct FrameCount(pub u32); diff --git a/crates/bevy_diagnostic/Cargo.toml b/crates/bevy_diagnostic/Cargo.toml index 295a969f32e14b..85ac1a5b08640a 100644 --- a/crates/bevy_diagnostic/Cargo.toml +++ b/crates/bevy_diagnostic/Cargo.toml @@ -16,3 +16,4 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.9.0-dev" } bevy_log = { path = "../bevy_log", version = "0.9.0-dev" } bevy_time = { path = "../bevy_time", version = "0.9.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" } +bevy_core = { path = "../bevy_core", version = "0.9.0-dev" } diff --git a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs index 9075cd4284d034..ec9ea2d9469c1e 100644 --- a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs @@ -1,21 +1,16 @@ use crate::{Diagnostic, DiagnosticId, Diagnostics}; use bevy_app::prelude::*; -use bevy_ecs::system::{Res, ResMut, Resource}; +use bevy_core::FrameCount; +use bevy_ecs::system::{Res, ResMut}; use bevy_time::Time; /// Adds "frame time" diagnostic to an App, specifically "frame time", "fps" and "frame count" #[derive(Default)] pub struct FrameTimeDiagnosticsPlugin; -#[derive(Resource)] -pub struct FrameTimeDiagnosticsState { - frame_count: u64, -} - impl Plugin for FrameTimeDiagnosticsPlugin { fn build(&self, app: &mut bevy_app::App) { app.add_startup_system(Self::setup_system) - .insert_resource(FrameTimeDiagnosticsState { frame_count: 0 }) .add_system(Self::diagnostic_system); } } @@ -36,12 +31,9 @@ impl FrameTimeDiagnosticsPlugin { pub fn diagnostic_system( mut diagnostics: ResMut, time: Res