From 2987be4fc5c695d726825d6f3aaa408a2e3bb5bd Mon Sep 17 00:00:00 2001 From: Torstein Grindvik Date: Sun, 12 Feb 2023 21:35:26 +0100 Subject: [PATCH 1/4] WIP: Noise stuff. Signed-off-by: Torstein Grindvik --- assets/shaders/auto-update-shader.sh | 12 ---- assets/shaders/math.wgsl | 46 ++++++++++++++ assets/shaders/value_noise.wgsl | 43 +++++++++++++ examples/noise/main.rs | 95 ++++++++++++++++++++++++++++ src/lib.rs | 3 + src/materials/mod.rs | 53 ++++++++++++++++ src/plugin.rs | 5 +- 7 files changed, 243 insertions(+), 14 deletions(-) delete mode 100644 assets/shaders/auto-update-shader.sh create mode 100644 assets/shaders/math.wgsl create mode 100644 assets/shaders/value_noise.wgsl create mode 100644 examples/noise/main.rs create mode 100644 src/materials/mod.rs diff --git a/assets/shaders/auto-update-shader.sh b/assets/shaders/auto-update-shader.sh deleted file mode 100644 index 3e252af..0000000 --- a/assets/shaders/auto-update-shader.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -for (( ; ; )) -do - sed -E 's/0\.5/0\.4/g' -i pixelate.wgsl - touch pixelate.wgsl - sleep 5 - - sed -E 's/0\.4/0\.5/g' -i pixelate.wgsl - touch pixelate.wgsl - sleep 5 -done diff --git a/assets/shaders/math.wgsl b/assets/shaders/math.wgsl new file mode 100644 index 0000000..19c7d84 --- /dev/null +++ b/assets/shaders/math.wgsl @@ -0,0 +1,46 @@ +#define_import_path bevy_vfx_bag::math + +// See Mark Jarzynski and Marc Olano, Hash Functions for GPU Rendering, Journal of Computer +// Graphics Techniques (JCGT), vol. 9, no. 3, 20–38, 2020 +fn pcg3d(v: vec3) -> vec3 { + var v = v * 1664525u + 1013904223u; + + v.x += v.y * v.z; + v.y += v.z * v.x; + v.z += v.x * v.y; + + v = v ^ (v >> 16u); + + v.x += v.y*v.z; + v.y += v.z*v.x; + v.z += v.x*v.y; + + return v; +} + +// https://github.com/gfx-rs/naga/issues/1908 +fn ldexp_workaround(v: f32, e: f32) -> f32 { + return v * exp2(e); +} +fn ldexp_workaround2(v: vec2, e: f32) -> vec2 { + return v * exp2(e); +} + +fn hash21(point: vec2) -> u32 { + return pcg3d(vec3(point.xy, 0u)).x; +} + +fn hash21f(point: vec2) -> f32 { + // https://www.pcg-random.org/using-pcg-c.html + // We get a random value in a u32's range. + // Divide it by 1/2^32 to produce a value in the [0, 1) range. + return ldexp_workaround(f32(hash21(point)), -32.0); +} + +fn hash22(point: vec2) -> vec2 { + return pcg3d(vec3(point.xy, 0u)).xy; +} + +fn hash22f(point: vec2) -> vec2 { + return ldexp_workaround2(vec2(hash22(point)), -32.0); +} diff --git a/assets/shaders/value_noise.wgsl b/assets/shaders/value_noise.wgsl new file mode 100644 index 0000000..5b30b97 --- /dev/null +++ b/assets/shaders/value_noise.wgsl @@ -0,0 +1,43 @@ +#define_import_path bevy_vfx_bag::value_noise +#import bevy_vfx_bag::math + +fn value_noise(coords: vec2) -> f32 { + let index = vec2(floor(coords)); + let frac = fract(coords); + + // Sometimes a smoothstepped frac is used instead. + let interpolant = frac; + + let noise_xy00 = hash21f(index + vec2(0u, 0u)); + let noise_xy10 = hash21f(index + vec2(1u, 0u)); + let noise_xy01 = hash21f(index + vec2(0u, 1u)); + let noise_xy11 = hash21f(index + vec2(1u, 1u)); + + // Gives us the noise at the correct point in the x direction + // between the upper corners + let noise_x0_lerp = mix(f32(noise_xy00), f32(noise_xy10), interpolant.x); + + // x direction lower corners + let noise_x1_lerp = mix(f32(noise_xy01), f32(noise_xy11), interpolant.x); + + // Lastly lerp between the values found in the y direction. + let noise = mix(noise_x0_lerp, noise_x1_lerp, interpolant.y); + + return noise; +} + +struct CustomMaterial { + scale: f32, + offset_x: f32, + offset_y: f32, +}; + +@group(1) @binding(0) +var material: CustomMaterial; + +@fragment +fn fragment( + #import bevy_pbr::mesh_vertex_output +) -> @location(0) vec4 { + return vec4(vec3(value_noise((material.scale * uv) + vec2(material.offset_x, material.offset_y))), 1.0); +} diff --git a/examples/noise/main.rs b/examples/noise/main.rs new file mode 100644 index 0000000..a87b535 --- /dev/null +++ b/examples/noise/main.rs @@ -0,0 +1,95 @@ +#[path = "../examples_common.rs"] +mod examples_common; + +use bevy::{ + prelude::*, + reflect::TypeUuid, + render::render_resource::{AsBindGroup, ShaderRef, ShaderType}, +}; + +use bevy_vfx_bag::BevyVfxBagPlugin; + +fn main() { + let mut app = App::new(); + + app.add_plugin(examples_common::SaneDefaultsPlugin) + .add_plugin(BevyVfxBagPlugin::default()) + .add_plugin(MaterialPlugin::::default()) + .add_startup_system(startup) + .add_system(update) + .run(); +} + +fn startup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // cube + + for i in 1..=4 { + let i = i as f32; + let uv = Uv { + scale: i * 4., + ..default() + }; + + commands.spawn(MaterialMeshBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + transform: Transform::from_xyz(-4. + (i * 2.), 0.5, 0.0), + material: materials.add(CustomMaterial { uv }), + ..default() + }); + } + + // camera + commands.spawn(Camera3dBundle { + transform: Transform::from_xyz(-0.5, 0.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }); +} + +impl Material for CustomMaterial { + fn fragment_shader() -> ShaderRef { + "shaders/value_noise.wgsl".into() + } +} + +#[derive(Debug, ShaderType, Clone)] +struct Uv { + scale: f32, + offset_x: f32, + offset_y: f32, +} + +impl Default for Uv { + fn default() -> Self { + Self { + scale: 1., + offset_x: 0., + offset_y: 0., + } + } +} + +// This is the struct that will be passed to your shader +#[derive(AsBindGroup, TypeUuid, Debug, Clone, Default)] +#[uuid = "9dc460be-ab02-11ed-905b-325096b39f47"] +pub struct CustomMaterial { + #[uniform(0)] + uv: Uv, +} + +fn update( + time: Res