fix(shaders): use #define_import_path + namespaced imports
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.
This commit is contained in:
parent
661d28a6a1
commit
5fa2621e6a
@ -1,10 +1,13 @@
|
|||||||
#import bevy_sprite::mesh2d_vertex_output::VertexOutput
|
#import bevy_sprite::mesh2d_vertex_output::VertexOutput
|
||||||
#import "shaders/ray_gen.wgsl"
|
// Bevy/naga_oil imports: each module file uses #define_import_path, then we
|
||||||
#import "shaders/geodesic_schwarzschild.wgsl"
|
// import individual symbols via `namespace::name` (or `namespace::{a, b}`).
|
||||||
#import "shaders/stars.wgsl"
|
// Whole-file imports without `::` do NOT reliably bring functions into scope.
|
||||||
#import "shaders/disk.wgsl"
|
#import singularity::ray_gen::ray_direction
|
||||||
#import "shaders/planets.wgsl"
|
#import singularity::geodesic::{deriv, classify_ray}
|
||||||
#import "shaders/grid.wgsl"
|
#import singularity::stars::{hash13, star_color}
|
||||||
|
#import singularity::disk::{rot_x, disk_hit, disk_color}
|
||||||
|
#import singularity::planets::{SphereData, planets, planet_hit}
|
||||||
|
#import singularity::grid::{flamm_depth, grid_hit}
|
||||||
|
|
||||||
struct BlackHoleUniforms {
|
struct BlackHoleUniforms {
|
||||||
eye: vec4<f32>,
|
eye: vec4<f32>,
|
||||||
@ -41,12 +44,15 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||||||
uv.x *= aspect;
|
uv.x *= aspect;
|
||||||
let dir = ray_direction(uv);
|
let dir = ray_direction(uv);
|
||||||
|
|
||||||
|
// Work in disk-local space: rotate eye + dir by -disk_tilt around X so the
|
||||||
|
// disk lies on y=0. (disk_hit/disk_color assume disk-local coords.)
|
||||||
var pos = rot_x(uniforms.eye.xyz, -uniforms.disk_tilt);
|
var pos = rot_x(uniforms.eye.xyz, -uniforms.disk_tilt);
|
||||||
var d = normalize(rot_x(dir, -uniforms.disk_tilt));
|
var d = normalize(rot_x(dir, -uniforms.disk_tilt));
|
||||||
|
|
||||||
let dt = max(length(uniforms.eye.xyz), 20.0) / f32(uniforms.steps);
|
let dt = max(length(uniforms.eye.xyz), 20.0) / f32(uniforms.steps);
|
||||||
let steps = uniforms.steps;
|
let steps = uniforms.steps;
|
||||||
|
|
||||||
|
// Front-to-back compositing.
|
||||||
var accum_color = vec3<f32>(0.0);
|
var accum_color = vec3<f32>(0.0);
|
||||||
var accum_alpha = 0.0;
|
var accum_alpha = 0.0;
|
||||||
|
|
||||||
@ -54,9 +60,12 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||||||
for (var i: u32 = 0u; i < steps; i = i + 1u) {
|
for (var i: u32 = 0u; i < steps; i = i + 1u) {
|
||||||
let r = length(pos);
|
let r = length(pos);
|
||||||
if (r < uniforms.rs) {
|
if (r < uniforms.rs) {
|
||||||
|
// Captured: whatever we've composited so far is the result.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (r > 1000.0) {
|
if (r > 1000.0) {
|
||||||
|
// Escaped: add background stars along the (disk-local) final dir.
|
||||||
|
// Rotate back to world for the star sample.
|
||||||
let world_dir = normalize(rot_x(d, uniforms.disk_tilt));
|
let world_dir = normalize(rot_x(d, uniforms.disk_tilt));
|
||||||
let star = star_color(world_dir, uniforms.star_intensity);
|
let star = star_color(world_dir, uniforms.star_intensity);
|
||||||
accum_color += (1.0 - accum_alpha) * star;
|
accum_color += (1.0 - accum_alpha) * star;
|
||||||
@ -64,6 +73,7 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RK4 step (single step), then test disk crossing on the segment.
|
||||||
let k1 = deriv(pos, d);
|
let k1 = deriv(pos, d);
|
||||||
let k2 = deriv(pos + k1.dpos * dt * 0.5, normalize(d + k1.ddir * dt * 0.5));
|
let k2 = deriv(pos + k1.dpos * dt * 0.5, normalize(d + k1.ddir * dt * 0.5));
|
||||||
let k3 = deriv(pos + k2.dpos * dt * 0.5, normalize(d + k2.ddir * dt * 0.5));
|
let k3 = deriv(pos + k2.dpos * dt * 0.5, normalize(d + k2.ddir * dt * 0.5));
|
||||||
@ -72,10 +82,11 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||||||
let new_dir = normalize(d + (k1.ddir + 2.0*k2.ddir + 2.0*k3.ddir + k4.ddir) * dt / 6.0);
|
let new_dir = normalize(d + (k1.ddir + 2.0*k2.ddir + 2.0*k3.ddir + k4.ddir) * dt / 6.0);
|
||||||
|
|
||||||
if (disk_hit(prev, new_pos)) {
|
if (disk_hit(prev, new_pos)) {
|
||||||
|
// Approximate the crossing point by interpolating to y=0.
|
||||||
let ty = prev.y / (prev.y - new_pos.y);
|
let ty = prev.y / (prev.y - new_pos.y);
|
||||||
let hit = mix(prev, new_pos, vec3<f32>(ty));
|
let hit = mix(prev, new_pos, vec3<f32>(ty));
|
||||||
let dc = disk_color(hit, new_dir);
|
let dc = disk_color(hit, new_dir);
|
||||||
let a = 0.85;
|
let a = 0.85; // disk is nearly opaque
|
||||||
accum_color += (1.0 - accum_alpha) * dc * a;
|
accum_color += (1.0 - accum_alpha) * dc * a;
|
||||||
accum_alpha += (1.0 - accum_alpha) * a;
|
accum_alpha += (1.0 - accum_alpha) * a;
|
||||||
if (accum_alpha > 0.99) { break; }
|
if (accum_alpha > 0.99) { break; }
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// Disk plane is the xz-plane in world space, tilted by `disk_tilt` around the
|
// Disk plane is the xz-plane in world space, tilted by `disk_tilt` around the
|
||||||
// x-axis. We work in "disk-local" coordinates by rotating the ray.
|
// x-axis. We work in "disk-local" coordinates by rotating the ray.
|
||||||
|
#define_import_path singularity::disk
|
||||||
|
|
||||||
// Rotate a vector around the X axis by angle a.
|
// Rotate a vector around the X axis by angle a.
|
||||||
fn rot_x(v: vec3<f32>, a: f32) -> vec3<f32> {
|
fn rot_x(v: vec3<f32>, a: f32) -> vec3<f32> {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
#define_import_path singularity::geodesic
|
||||||
|
|
||||||
const R_ESCAPE: f32 = 1000.0;
|
const R_ESCAPE: f32 = 1000.0;
|
||||||
|
|
||||||
struct Deriv {
|
struct Deriv {
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// (negative y in disk-local space). Dips below the disk toward the center —
|
// (negative y in disk-local space). Dips below the disk toward the center —
|
||||||
// the classic gravity-well visualization. Traced through curved spacetime, so
|
// the classic gravity-well visualization. Traced through curved spacetime, so
|
||||||
// grid lines near the hole bend dramatically.
|
// grid lines near the hole bend dramatically.
|
||||||
|
#define_import_path singularity::grid
|
||||||
|
|
||||||
fn flamm_depth(r: f32) -> f32 {
|
fn flamm_depth(r: f32) -> f32 {
|
||||||
if (r <= uniforms.rs) { return 0.0; }
|
if (r <= uniforms.rs) { return 0.0; }
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
|
#define_import_path singularity::planets
|
||||||
|
|
||||||
struct SphereData {
|
struct SphereData {
|
||||||
center: vec4<f32>, // xyz = center, w = radius
|
center: vec4<f32>, // xyz = center, w = radius
|
||||||
color: vec4<f32>, // xyz = color, w = emissive flag (u32 reinterpreted; we just check > 0.5)
|
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>;
|
@group(#{MATERIAL_BIND_GROUP}) @binding(3) var<storage, read> planets: array<SphereData>;
|
||||||
|
|
||||||
// Test the segment prev->cur against all planets. Returns hit color & alpha,
|
// Test the segment prev->cur against all planets. Returns hit color & alpha,
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
// Builds a world-space camera ray direction for the current pixel.
|
// Builds a world-space camera ray direction for the current pixel.
|
||||||
// `uv` is the pixel coordinate normalized to [-1,1] with aspect correction.
|
// `uv` is the pixel coordinate normalized to [-1,1] with aspect correction.
|
||||||
|
#define_import_path singularity::ray_gen
|
||||||
|
|
||||||
fn ray_direction(uv: vec2<f32>) -> vec3<f32> {
|
fn ray_direction(uv: vec2<f32>) -> vec3<f32> {
|
||||||
// NOTE: `fov` is packed into the `.w` of `up` in BlackHoleUniforms
|
// NOTE: `fov` is packed into the `.w` of `up` in BlackHoleUniforms
|
||||||
// (the Rust struct lays out `up: Vec3` + `fov: f32` as one vec4 block).
|
// (the Rust struct lays out `up: Vec3` + `fov: f32` as one vec4 block).
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
// Hash-based procedural stars on the unit sphere. Returns RGB radiance.
|
// Hash-based procedural stars on the unit sphere. Returns RGB radiance.
|
||||||
|
#define_import_path singularity::stars
|
||||||
|
|
||||||
fn hash13(p: vec3<f32>) -> f32 {
|
fn hash13(p: vec3<f32>) -> f32 {
|
||||||
var q = vec3<f32>(dot(p, vec3<f32>(127.1, 311.7, 74.7)),
|
var q = vec3<f32>(dot(p, vec3<f32>(127.1, 311.7, 74.7)),
|
||||||
dot(p, vec3<f32>(269.5, 183.3, 246.1)),
|
dot(p, vec3<f32>(269.5, 183.3, 246.1)),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user