- 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.
169 lines
5.9 KiB
WebGPU Shading Language
169 lines
5.9 KiB
WebGPU Shading Language
#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);
|
|
} |