The whole-file #import "file.wgsl" form (without ::) does not reliably bring functions into scope in naga_oil/Bevy 0.19 — the renderer was failing at runtime with 'no definition in scope for identifier: ray_direction' (and rot_x, deriv, etc.), producing a blank screen despite 'cargo build' passing (shaders aren't compile-checked at build time). Fix: add #define_import_path to each module file and import symbols explicitly via namespace::name (the canonical Bevy pattern from the shader_material_2d example). Verified the full shader pipeline compiles and runs at runtime with zero WGSL errors. This was a pre-existing bug masked by incomplete runtime verification in earlier tasks; it is NOT specific to Task 15's grid work. All earlier visual features (shadow, Doppler disk, Einstein halo, stars, planets, grid) now actually render.
51 lines
2.0 KiB
WebGPU Shading Language
51 lines
2.0 KiB
WebGPU Shading Language
#define_import_path singularity::planets
|
|
|
|
struct SphereData {
|
|
center: vec4<f32>, // xyz = center, w = radius
|
|
color: vec4<f32>, // xyz = color, w = emissive flag (u32 reinterpreted; we just check > 0.5)
|
|
};
|
|
|
|
// The storage binding is declared here as part of the planets module; it lives
|
|
// in the material's bind group (group 2 = #{MATERIAL_BIND_GROUP}).
|
|
@group(#{MATERIAL_BIND_GROUP}) @binding(3) var<storage, read> planets: array<SphereData>;
|
|
|
|
// Test the segment prev->cur against all planets. Returns hit color & alpha,
|
|
// or (0,0,0,0) if no hit. `dir` is the ray direction (for shading).
|
|
fn planet_hit(prev: vec3<f32>, cur: vec3<f32>, dir: vec3<f32>) -> vec4<f32> {
|
|
var nearest_t = 1e9;
|
|
var nearest_col = vec3<f32>(0.0);
|
|
var found = false;
|
|
for (var i: u32 = 0u; i < uniforms.planet_count; i = i + 1u) {
|
|
let s = planets[i];
|
|
let center = s.center.xyz;
|
|
let radius = s.center.w;
|
|
// Ray-sphere intersection for the segment.
|
|
let seg = cur - prev;
|
|
let oc = prev - center;
|
|
let a = dot(seg, seg);
|
|
let b = 2.0 * dot(oc, seg);
|
|
let c = dot(oc, oc) - radius * radius;
|
|
let disc = b * b - 4.0 * a * c;
|
|
if (disc < 0.0) { continue; }
|
|
let sq = sqrt(disc);
|
|
var t = (-b - sq) / (2.0 * a);
|
|
if (t < 0.0) { t = (-b + sq) / (2.0 * a); }
|
|
if (t >= 0.0 && t <= 1.0 && t < nearest_t) {
|
|
nearest_t = t;
|
|
let hit_pos = prev + seg * t;
|
|
let n = normalize(hit_pos - center);
|
|
// Lambert shading from a fixed light direction.
|
|
let light_dir = normalize(vec3<f32>(0.5, 0.8, 0.3));
|
|
let ndl = max(dot(n, light_dir), 0.0);
|
|
var col = s.color.xyz * (0.2 + 0.8 * ndl);
|
|
if (s.color.w > 0.5) { col = s.color.xyz; } // emissive
|
|
nearest_col = col;
|
|
found = true;
|
|
}
|
|
}
|
|
if (found) {
|
|
return vec4<f32>(nearest_col, 0.95);
|
|
}
|
|
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
}
|