Skip to content

Commit

Permalink
added background shader
Browse files Browse the repository at this point in the history
  • Loading branch information
ramirezmike committed Jul 28, 2024
1 parent aa5e87f commit b7f0ee5
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 0 deletions.
91 changes: 91 additions & 0 deletions assets/shaders/ground.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#import bevy_sprite::mesh2d_vertex_output::VertexOutput

struct GroundShader {
camera_x: f32,
camera_y: f32,
random: f32,
time: f32,
};

@group(2) @binding(100)
var<uniform> input: GroundShader;

const ZOOM:f32 = 180.0;
const USE_BEAT_INPUT: bool = false;
const ANIMATE_SHADER: bool = false;
const GRID_LINE_COLOR: vec4f = vec4f(0.0);
const SPACE_COLOR_ALPHA: f32 = 0.3;
const CAMERA_OFFSET: f32 = 0.001953 / 2.0; // This number works well for tiling but I haven't figured out why yet, DON'T CHANGE IT
const GRID_RATIO:f32 = 10.;

const CONTRAST_FACTOR: f32 = 1.5;
const SATURATION_FACTOR: f32 = 0.5;
const BRIGHTNESS: f32 = 0.5;
const BACKGROUND_COLOR: vec3f = vec3<f32>(0.2, 0.0, 0.4);
const COLOR_1: vec3f = vec3<f32>(0.0, 0.5, 0.0); // GREEN
const COLOR_2: vec3f = vec3<f32>(0.0, 0.5, 1.0); // BLUE
const COLOR_3: vec3f = vec3<f32>(1.0, 1.0, 0.0); // YELLOW
const COLOR_4: vec3f = vec3<f32>(1.0, 0.0, 0.0); // RED

@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
let rand = max(select(1.0, input.random, USE_BEAT_INPUT), 0.0001);
var camera_x = input.camera_x * CAMERA_OFFSET;
var camera_y = input.camera_y * -CAMERA_OFFSET;

let uv = (in.uv.xy + vec2(camera_x, camera_y)) * ZOOM;

if is_line(uv) {
return GRID_LINE_COLOR;
}

let square = floor(uv * GRID_RATIO * 0.1);
var tile_color = get_color_from_vec2f(square / rand);

let average_color = average_rgb(tile_color, BACKGROUND_COLOR);
let contrasted_color = blend_towards(tile_color, average_color, CONTRAST_FACTOR);
let saturated_color = blend_towards(contrasted_color, rgb_to_grayscale(contrasted_color), SATURATION_FACTOR);
let brightened_color = blend_towards(saturated_color, vec3f(0.0), BRIGHTNESS);

let final_color = brightened_color * select(1.0, cos(input.time), ANIMATE_SHADER);

return vec4<f32>(final_color, SPACE_COLOR_ALPHA);
}

fn average_rgb(color1: vec3<f32>, color2: vec3<f32>) -> vec3<f32> {
return (color1 + color2) / 2.0;
}

fn blend_towards(color: vec3<f32>, toward_color: vec3<f32>, factor: f32) -> vec3<f32> {
return mix(color, toward_color, factor);
}

fn rgb_to_grayscale(color: vec3<f32>) -> vec3<f32> {
let grayscale_value = dot(color, vec3<f32>(0.299, 0.587, 0.114));
return vec3<f32>(grayscale_value, grayscale_value, grayscale_value);
}

fn get_color_from_vec2f(v: vec2<f32>) -> vec3<f32> {
let index = hash_vec2f(v);
return get_color(index);
}

fn get_color(index: u32) -> vec3<f32> {
switch (index) {
case 1u: { return COLOR_1; }
case 2u: { return COLOR_2; }
case 3u: { return COLOR_3; }
case 4u: { return COLOR_4; }
default: { return BACKGROUND_COLOR; }
}
}

fn hash_vec2f(v: vec2<f32>) -> u32 {
let pattern_size = 1000.0;
return u32(sin(cos(v.x) * tan(v.y)) * pattern_size) % 10;
}

fn is_line(uv: vec2<f32>)-> bool {
let i = step(fract(uv), vec2(1.0/GRID_RATIO));
return ((1.0-i.x) * (1.0-i.y)) < 0.5;
}
6 changes: 6 additions & 0 deletions src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod audio;
pub mod card;
pub mod cleanup;
pub mod combat;
pub mod ground;
pub mod spotlight;
pub mod sprite;
pub mod wave;
Expand All @@ -28,6 +29,7 @@ pub(super) fn plugin(app: &mut App) {
card::plugin,
cleanup::plugin,
combat::plugin,
ground::plugin,
spotlight::plugin,
sprite::plugin,
wave::plugin,
Expand All @@ -41,6 +43,7 @@ pub struct GameRoot {
pub enemies: Entity,
pub projectiles: Entity,
pub vfx: Entity,
pub background: Entity,
}

impl Configure for GameRoot {
Expand All @@ -57,12 +60,14 @@ impl FromWorld for GameRoot {
let enemies = world.spawn_with(root("Enemies")).id();
let projectiles = world.spawn_with(root("Projectiles")).id();
let vfx = world.spawn_with(root("Vfx")).id();
let background = world.spawn_with(root("Background")).id();

Self {
players,
enemies,
projectiles,
vfx,
background,
}
}
}
Expand All @@ -72,6 +77,7 @@ fn clear_game_root(mut commands: Commands, game_root: Res<GameRoot>) {
commands.entity(game_root.enemies).despawn_descendants();
commands.entity(game_root.projectiles).despawn_descendants();
commands.entity(game_root.vfx).despawn_descendants();
commands.entity(game_root.background).despawn_descendants();
}

fn root(name: impl Into<Cow<'static, str>>) -> impl EntityCommand<World> {
Expand Down
123 changes: 123 additions & 0 deletions src/game/ground.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use bevy::prelude::*;
use bevy::reflect::TypePath;
use bevy::render::render_resource::AsBindGroup;
use bevy::render::render_resource::ShaderRef;
use bevy::render::render_resource::ShaderType;
use bevy::sprite::Material2d;
use bevy::sprite::Material2dPlugin;
use bevy::sprite::MaterialMesh2dBundle;
use pyri_state::prelude::*;

use crate::core::UpdateSet;
use crate::game::audio::music::on_full_beat;
use crate::screen::Screen;
use crate::util::prelude::*;

pub(super) fn plugin(app: &mut App) {
app.configure::<Ground>();
}

const GROUND_Z_INDEX: f32 = -10.0;
const GROUND_MESH_SIZE: f32 = 1024.0;
const GROUND_SNAP_INTERVAL: f32 = GROUND_MESH_SIZE / 4.0;
const GROUND_SNAP: Vec2 = Vec2::splat(GROUND_SNAP_INTERVAL);

#[derive(Resource)]
pub struct Ground {
material: Handle<GroundMaterial>,
mesh: Handle<Mesh>,
}

impl Configure for Ground {
fn configure(app: &mut App) {
app.add_plugins(Material2dPlugin::<GroundMaterial>::default());
app.init_resource::<Self>();

app.add_systems(
Update,
Screen::Playing.on_update((
update_background,
update_background_beat
.in_set(UpdateSet::Update)
.run_if(on_full_beat(8)),
)),
);
}
}

impl FromWorld for Ground {
fn from_world(world: &mut World) -> Self {
let material = world
.resource_mut::<Assets<GroundMaterial>>()
.add(GroundMaterial::default());
let mesh = world
.resource_mut::<Assets<Mesh>>()
.add(Rectangle::default());

Self { material, mesh }
}
}

#[derive(Default, AsBindGroup, Asset, TypePath, Debug, Clone)]
pub struct GroundMaterial {
#[uniform(100)]
uniforms: Uniforms,
}

#[derive(ShaderType, Default, Clone, Debug)]
struct Uniforms {
pub camera_x: f32,
pub camera_y: f32,
pub random: f32,
pub time: f32,
}

impl Material2d for GroundMaterial {
fn fragment_shader() -> ShaderRef {
"shaders/ground.wgsl".into()
}
}

pub fn ground(mut entity: EntityWorldMut) {
let ground = r!(entity.world().get_resource::<Ground>());
let material = ground.material.clone();
let mesh = ground.mesh.clone();

entity.insert((
Name::new("Background"),
MaterialMesh2dBundle {
mesh: mesh.into(),
transform: Transform::from_translation(Vec2::ZERO.extend(GROUND_Z_INDEX))
.with_scale(Vec3::splat(GROUND_MESH_SIZE)),
material,
..default()
},
));
}

fn update_background(
mut ground_material: ResMut<Assets<GroundMaterial>>,
camera_query: Query<&Transform, (With<IsDefaultUiCamera>, Without<Handle<GroundMaterial>>)>,
mut ground_query: Query<&mut Transform, With<Handle<GroundMaterial>>>,
time: Res<Time>,
) {
for (_, material) in ground_material.iter_mut() {
for mut ground_transform in &mut ground_query {
for camera_transform in &camera_query {
let translation = ((camera_transform.translation.truncate() / GROUND_SNAP).floor()
* GROUND_SNAP)
.extend(GROUND_Z_INDEX);
ground_transform.translation = translation;
material.uniforms.camera_x = translation.x;
material.uniforms.camera_y = translation.y;
material.uniforms.time = time.elapsed_seconds();
}
}
}
}

fn update_background_beat(mut ground_material: ResMut<Assets<GroundMaterial>>) {
for (_, material) in ground_material.iter_mut() {
material.uniforms.random = rand::random();
}
}
4 changes: 4 additions & 0 deletions src/screen/playing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::core::pause::Pause;
use crate::game::actor::player::player;
use crate::game::audio::music::start_music;
use crate::game::audio::music::stop_music;
use crate::game::ground::ground;
use crate::game::spotlight::spotlight_lamp_spawner;
use crate::game::wave::wave;
use crate::game::GameRoot;
Expand Down Expand Up @@ -55,6 +56,9 @@ fn enter_playing(mut commands: Commands, game_root: Res<GameRoot>, ui_root: Res<
commands
.spawn_with(playing_hud(player))
.set_parent(ui_root.body);

// Spawn Background.
commands.spawn_with(ground).set_parent(game_root.background);
}

#[derive(AssetCollection, Resource, Reflect, Default)]
Expand Down

0 comments on commit b7f0ee5

Please sign in to comment.