diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d9e443d4b..fdc396cba 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -191,6 +191,11 @@ path = "rust_basics/6_functions.rs" name = "7_modules" path = "rust_basics/7_modules/7_modules.rs" +# Shader +[[example]] +name = "fullscreen" +path = "shader/fullscreen.rs" + # Template [[example]] name = "template_app" diff --git a/examples/assets/shaders/fullscreen.wgsl b/examples/assets/shaders/fullscreen.wgsl new file mode 100644 index 000000000..8b5924385 --- /dev/null +++ b/examples/assets/shaders/fullscreen.wgsl @@ -0,0 +1,66 @@ +#import bevy_pbr::forward_io::VertexOutput +#import bevy_pbr::mesh_view_bindings::view +#import bevy_pbr::mesh_view_bindings::globals + +struct ShaderModel { + mouse: vec2, +}; + +fn hue(a: f32) -> vec3 { + let t = vec3(0.0, 1.0, 2.0) + a * 6.3; + return cos(t) * 0.5 + vec3(0.5, 0.5, 0.5); +} + +// fixes negative mod +fn safe_mod(x: f32, y: f32) -> f32 { + return x - y * floor(x/y); +} + +fn safe_mod_vec2(v: vec2, y: f32) -> vec2 { + return vec2( + safe_mod(v.x, y), + safe_mod(v.y, y) + ); +} + +@group(2) @binding(0) var shader_model: ShaderModel; + +@fragment +fn fragment(mesh: VertexOutput) -> @location(0) vec4 { + let resolution = view.viewport.zw; // (width, height) + let time = globals.time; + + let fragCoord = vec2(mesh.uv.x * resolution.x, (1.0 - mesh.uv.y) * resolution.y); + + let s = 15.0; + let t = safe_mod(time/60.0, 1.0); + + // Zoom + var z = shader_model.mouse.y / resolution.y; + if (z < 0.1) { + z = 0.0; + } + + let u = (2.0 * fragCoord - resolution) / resolution.y * s / (1.0 - z); + + let x = round(u.x); + let y = round(u.y); + + var m = 0.0; + if (t < 1.0 / 3.0) { + m = x * x + y * y; + } else if (t < 2.0 / 3.0) { + m = x * x - y * y; + } else { + m = x * y; + } + + let g = abs(safe_mod_vec2(u, 1.0) - vec2(0.5)); + var c = hue(m * t) * min(g.x, g.y); + + let uv_centered = fragCoord / resolution; + let v = abs(fract(uv_centered + 0.5) - 0.5) * 16.0; + c *= sqrt(min(v.x, v.y)); + + return vec4(c + c * c, 1.0); +} \ No newline at end of file diff --git a/examples/shader/fullscreen.rs b/examples/shader/fullscreen.rs new file mode 100644 index 000000000..a91efa242 --- /dev/null +++ b/examples/shader/fullscreen.rs @@ -0,0 +1,33 @@ +use nannou::prelude::*; + +fn main() { + nannou::app(model) + .simple_window(view) + // Register our custom shader model to make it available for use in our drawing + .shader_model::() + .run() +} + +struct Model {} + +// This struct defines the data that will be passed to your shader +#[shader_model(fragment = "shaders/fullscreen.wgsl")] +struct ShaderModel { + #[uniform(0)] + mouse: Vec2, +} + +fn model(app: &App) -> Model { + Model {} +} + +fn view(app: &App, _model: &Model, window: Entity) { + // Begin drawing and configure the draw instance to use this frame's shader model. + let draw = app.draw().shader_model(ShaderModel { + mouse: app.mouse(), + }); + + // Draw a rectangle the size of the window shaded by our shader. + let rect = app.window(window).rect(); + draw.rect().w_h(rect.w(), rect.h()); +}