feat: Implement background shader and visual effects
- Added a new WGSL shader for background rendering with volumetric fog effects. - Created components for game entities including Circle, Velocity, Explosion, and ChainReaction. - Introduced game state management with GameState enum and LevelState resource. - Implemented event system for explosion requests and circle destruction notifications. - Developed plugins for game logic, input handling, movement, reactions, scoring, and UI. - Configured game settings through GameConfig resource with adjustable parameters. - Enhanced UI with configuration options and game over screens. - Integrated spatial grid for efficient collision detection and explosion handling.
This commit is contained in:
169
assets/shaders/background.wgsl
Normal file
169
assets/shaders/background.wgsl
Normal file
@@ -0,0 +1,169 @@
|
||||
#import bevy_sprite::mesh2d_view_bindings::globals
|
||||
|
||||
struct BackgroundMaterial {
|
||||
resolution: vec2<f32>,
|
||||
time_offset: f32,
|
||||
}
|
||||
|
||||
@group(2) @binding(0) var<uniform> material: BackgroundMaterial;
|
||||
|
||||
// --- CONFIGURATION ---
|
||||
const RAYS_COUNT: i32 = 32; // Increase to 64 if on PC for smoother quality
|
||||
const RENDER_DISTANCE: f32 = 5.0; // How far rays travel
|
||||
const NEAR_PLANE: f32 = 1.5; // Higher = Zoomed in (Less "Fisheye/Disco")
|
||||
const ROTATION_SPEED: f32 = 0.1;
|
||||
const JITTERING: f32 = 0.05;
|
||||
|
||||
// Colors
|
||||
const COLOR1: vec3<f32> = vec3<f32>(0.1, 0.0, 0.3); // Deep Nebula Purple
|
||||
const COLOR2: vec3<f32> = vec3<f32>(0.8, 0.2, 0.5); // Hot Pink Highlights
|
||||
|
||||
// --- NOISE FUNCTIONS ---
|
||||
fn hash(v: vec3<f32>) -> f32 {
|
||||
return fract(sin(dot(v, vec3<f32>(11.51721, 67.12511, 9.7561))) * 1551.4172);
|
||||
}
|
||||
|
||||
fn getNoiseFromVec3(v: vec3<f32>) -> f32 {
|
||||
let rootV = floor(v);
|
||||
let f = smoothstep(vec3<f32>(0.0), vec3<f32>(1.0), fract(v));
|
||||
|
||||
let n000 = hash(rootV);
|
||||
let n001 = hash(rootV + vec3<f32>(0.0, 0.0, 1.0));
|
||||
let n010 = hash(rootV + vec3<f32>(0.0, 1.0, 0.0));
|
||||
let n011 = hash(rootV + vec3<f32>(0.0, 1.0, 1.0));
|
||||
let n100 = hash(rootV + vec3<f32>(1.0, 0.0, 0.0));
|
||||
let n101 = hash(rootV + vec3<f32>(1.0, 0.0, 1.0));
|
||||
let n110 = hash(rootV + vec3<f32>(1.0, 1.0, 0.0));
|
||||
let n111 = hash(rootV + vec3<f32>(1.0, 1.0, 1.0));
|
||||
|
||||
let n = mix(vec4<f32>(n000, n010, n100, n110), vec4<f32>(n001, n011, n101, n111), f.z);
|
||||
let n_xy = mix(vec2<f32>(n.x, n.z), vec2<f32>(n.y, n.w), f.y);
|
||||
return mix(n_xy.x, n_xy.y, f.x);
|
||||
}
|
||||
|
||||
fn volumetricFog(v: vec3<f32>, noiseMod: f32) -> f32 {
|
||||
var noise: f32 = 0.0;
|
||||
var alpha: f32 = 1.0;
|
||||
var point: vec3<f32> = v;
|
||||
|
||||
// 5 Octaves of noise
|
||||
for(var i: i32 = 0; i < 5; i++) {
|
||||
noise += getNoiseFromVec3(point) * alpha;
|
||||
point *= 2.0;
|
||||
alpha *= 0.5;
|
||||
}
|
||||
|
||||
noise *= 0.575;
|
||||
|
||||
// Add time-based movement to edges
|
||||
let edge = 0.1 + getNoiseFromVec3(v * 0.5 + vec3<f32>(globals.time * 0.03)) * 0.8;
|
||||
|
||||
// Apply "Beat" modulation (noiseMod)
|
||||
let modNoise = (0.5 - abs(edge * (1.0 + noiseMod * 0.05) - noise)) * 2.0;
|
||||
|
||||
// Contrast Curve
|
||||
return (smoothstep(0.0, 0.9, modNoise * modNoise) + (1.0 - smoothstep(1.3, 0.6, modNoise))) * 0.2;
|
||||
}
|
||||
|
||||
fn fogMarch(rayStart: vec3<f32>, rayDirection: vec3<f32>, disMod: f32) -> vec3<f32> {
|
||||
var stepLength = RENDER_DISTANCE / f32(RAYS_COUNT);
|
||||
var fog = vec3<f32>(0.0);
|
||||
var point = rayStart;
|
||||
|
||||
for(var i: i32 = 0; i < RAYS_COUNT; i++) {
|
||||
point += rayDirection * stepLength;
|
||||
fog += volumetricFog(point, disMod)
|
||||
* mix(COLOR1, COLOR2 * (1.0 + disMod * 0.5), getNoiseFromVec3((point + vec3<f32>(12.51, 52.167, 1.146)) * 0.5))
|
||||
* getNoiseFromVec3(point * 0.2 + 20.0) * 2.0; // "Holes" in fog
|
||||
stepLength *= 1.05; // Exponential step (fewer samples close up)
|
||||
}
|
||||
|
||||
// "Dither" the end result by the far-plane noise to hide banding
|
||||
let farNoise = pow(getNoiseFromVec3((rayStart + rayDirection * RENDER_DISTANCE)), 2.0);
|
||||
fog = (fog / f32(RAYS_COUNT)) * (farNoise * 3.0 + disMod * 0.5);
|
||||
|
||||
return fog;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment(
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) uv: vec2<f32>,
|
||||
) -> @location(0) vec4<f32> {
|
||||
let time = globals.time + material.time_offset;
|
||||
|
||||
// 1. Aspect Ratio Correction
|
||||
// Map UVs from 0..1 to -1..1
|
||||
let centered = (uv - 0.5) * 2.0;
|
||||
// Fix aspect ratio by scaling Y (Width is dominant)
|
||||
let aspect_y = material.resolution.y / material.resolution.x;
|
||||
let final_uv = vec2<f32>(centered.x, centered.y * aspect_y);
|
||||
|
||||
// 2. Camera Rotation Angles
|
||||
let angleY = sin(time * ROTATION_SPEED * 2.0);
|
||||
let angleX = cos(time * 0.712 * ROTATION_SPEED);
|
||||
let angleZ = sin(time * 1.779 * ROTATION_SPEED);
|
||||
|
||||
// 3. Rotation Matrices - COPIED EXACTLY FROM ORIGINAL GLSL
|
||||
// The original author used a non-standard axis alignment (sin on diagonal).
|
||||
// We construct these column-by-column to match GLSL memory layout.
|
||||
|
||||
// GLSL: mat3(1, 0, 0, 0, sin, cos, 0, -cos, sin)
|
||||
let rotX = mat3x3<f32>(
|
||||
vec3<f32>(1.0, 0.0, 0.0), // Col 0
|
||||
vec3<f32>(0.0, sin(angleX), cos(angleX)), // Col 1
|
||||
vec3<f32>(0.0, -cos(angleX), sin(angleX)) // Col 2
|
||||
);
|
||||
|
||||
// GLSL: mat3(sin, cos, 0, -cos, sin, 0, 0, 0, 1)
|
||||
let rotZ = mat3x3<f32>(
|
||||
vec3<f32>(sin(angleZ), cos(angleZ), 0.0), // Col 0
|
||||
vec3<f32>(-cos(angleZ), sin(angleZ), 0.0), // Col 1
|
||||
vec3<f32>(0.0, 0.0, 1.0) // Col 2
|
||||
);
|
||||
|
||||
// GLSL: mat3(sin, 0, cos, 0, 1, 0, -cos, 0, sin)
|
||||
let rotY = mat3x3<f32>(
|
||||
vec3<f32>(sin(angleY), 0.0, -cos(angleY)), // Col 0
|
||||
vec3<f32>(0.0, 1.0, 0.0), // Col 1
|
||||
vec3<f32>(cos(angleY), 0.0, sin(angleY)) // Col 2
|
||||
);
|
||||
|
||||
// 4. Ray Construction
|
||||
// Combine rotations
|
||||
let rotation = rotY * rotZ * rotX;
|
||||
|
||||
// Forward movement (breathing effect)
|
||||
let near_plane_mod = NEAR_PLANE * (1.0 + sin(time * 0.2) * 0.4);
|
||||
|
||||
// Original Vector: X=Right, Y=Forward (Depth), Z=Vertical
|
||||
let ray_dir_local = normalize(vec3<f32>(final_uv.x, near_plane_mod, final_uv.y));
|
||||
|
||||
// Apply Rotation
|
||||
let rayDirection = rotation * ray_dir_local;
|
||||
|
||||
// 5. Camera Position
|
||||
let cameraCenter = vec3<f32>(
|
||||
sin(time * 0.2) * 10.0,
|
||||
time * 0.2 * 10.0,
|
||||
cos(time * 0.78 * 0.2 + 2.14) * 10.0
|
||||
);
|
||||
|
||||
// 6. Ray Start with Jitter
|
||||
let jitter = (hash(vec3<f32>(final_uv + 4.0, fract(time) + 2.0)) - 0.5) * JITTERING;
|
||||
let rayStart = cameraCenter + rayDirection * jitter;
|
||||
|
||||
// 7. Render
|
||||
// Fake "Audio Reactivity" using sine wave
|
||||
let beat = smoothstep(0.6, 0.9, pow(sin(time * 4.0) * 0.5 + 0.5, 2.0) * 0.06);
|
||||
var fog = fogMarch(rayStart, rayDirection, beat);
|
||||
|
||||
// Post-Process
|
||||
fog *= 2.5 * 1.2; // Brightness
|
||||
fog += 0.07 * mix(COLOR1, COLOR2, 0.5); // Base Color
|
||||
|
||||
// Tone mapping to prevent burnout
|
||||
fog = sqrt(smoothstep(vec3<f32>(0.0), vec3<f32>(1.5), fog));
|
||||
|
||||
return vec4<f32>(fog, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user