Merge pull request #1704

This commit is contained in:
Brandon Thomas
2026-10-18 09:35:35 -04:00
6 changed files with 184 additions and 26 deletions
+6 -1
View File
@@ -320,7 +320,12 @@ fn pack(s: &Scene) -> [Vec<u8>; 7] {
let lit = !(s.lights.is_empty() && s.env.is_none() && s.ambient == [0.0; 3]);
globals.extend([e.rotation, lit as u32 as f32, 0.0, 0.0]);
let mats = s.materials.iter().flat_map(|m| {
let flags = (m.double_sided as u32) | (m.unlit as u32) << 1 | (m.accepts_lights as u32) << 2 | (m.receives_shadows as u32) << 3 | (m.alpha_mode << 8);
let flags = (m.double_sided as u32)
| (m.unlit as u32) << 1
| (m.accepts_lights as u32) << 2
| (m.receives_shadows as u32) << 3
| (m.shadows_only as u32) << 4
| (m.alpha_mode << 8);
[
m.base[0],
m.base[1],
+55 -16
View File
@@ -288,6 +288,53 @@ struct FOut {
@location(1) depth: u32,
};
// shade::to_light: unit direction to the light (xyz) and its distance (w).
fn to_light(kind: u32, lpos: vec3<f32>, ldir: vec3<f32>, pos: vec3<f32>) -> vec4<f32> {
if (kind == 0u) {
return vec4<f32>(-ldir, 1e30);
}
let d = lpos - pos;
let len = sqrt(dot(d, d));
if (len < 1e-6) {
return vec4<f32>(0.0, 0.0, -1.0, 0.0);
}
return vec4<f32>(d / len, len);
}
// shade::shadow_only: the strongest shadow any light casts on the fragment.
fn shadow_only_amount(unlit: bool, normal: vec3<f32>, front: bool, pos: vec3<f32>) -> f32 {
var amount = 0.0;
if (unlit) {
return amount;
}
var n = norm3(normal);
if (!front) {
n = -n;
}
let nl_count = u32(g.fwd.w);
for (var li = 0u; li < nl_count; li = li + 1u) {
let lb = li * LIGHT_STRIDE;
let l0 = lights[lb];
let l1 = lights[lb + 1u];
let l2 = lights[lb + 2u];
let l3 = lights[lb + 3u];
let l4 = lights[lb + 4u];
let kind = u32(l0.w);
let tl = to_light(kind, l0.xyz, l1.xyz, pos);
let to_l = tl.xyz;
let dist = tl.w;
if (attenuation(kind, u32(l3.x), l3.y, l3.z, l2.w, l1.w, l1.xyz, to_l, dist) <= 0.0) {
continue;
}
let n_l = dot(n, to_l);
if (n_l <= 0.0) {
continue;
}
amount = max(amount, 1.0 - light_shadow(li, kind, l0.xyz, i32(l3.w), l4.x, pos, n_l));
}
return amount;
}
@fragment
fn fs(i: VOut, @builtin(front_facing) front: bool) -> FOut {
let mb = i.material * MAT_STRIDE;
@@ -302,6 +349,7 @@ fn fs(i: VOut, @builtin(front_facing) front: bool) -> FOut {
let unlit = (flags & 2u) != 0u;
let accepts_lights = (flags & 4u) != 0u;
let receives_shadows = (flags & 8u) != 0u;
let shadows_only = (flags & 16u) != 0u;
let alpha_mode = (flags >> 8u) & 3u;
if (!front && !double_sided) {
discard;
@@ -330,6 +378,10 @@ fn fs(i: VOut, @builtin(front_facing) front: bool) -> FOut {
}
var out: FOut;
out.depth = bitcast<u32>(dot(g.view_z.xyz, i.pos) + g.view_z.w);
if (shadows_only) {
out.color = vec4<f32>(0.0, 0.0, 0.0, alpha * shadow_only_amount(unlit || !accepts_lights, i.normal, front, i.pos));
return out;
}
if (unlit || !accepts_lights || g.env2.y < 0.5) {
let c = rgb + emissive;
out.color = vec4<f32>(c * alpha, alpha);
@@ -381,22 +433,9 @@ fn fs(i: VOut, @builtin(front_facing) front: bool) -> FOut {
let l3 = lights[lb + 3u];
let l4 = lights[lb + 4u];
let kind = u32(l0.w);
var to_l: vec3<f32>;
var dist: f32;
if (kind == 0u) {
to_l = -l1.xyz;
dist = 1e30;
} else {
let d = l0.xyz - i.pos;
let len = sqrt(dot(d, d));
if (len < 1e-6) {
to_l = vec3<f32>(0.0, 0.0, -1.0);
dist = 0.0;
} else {
to_l = d / len;
dist = len;
}
}
let tl = to_light(kind, l0.xyz, l1.xyz, i.pos);
let to_l = tl.xyz;
let dist = tl.w;
let k = attenuation(kind, u32(l3.x), l3.y, l3.z, l2.w, l1.w, l1.xyz, to_l, dist);
if (k <= 0.0) {
continue;
+12
View File
@@ -155,6 +155,18 @@ fn gpu_matches_cpu_unlit_and_full_frames() {
assert!((diff as f64) < 0.02 * cpu.data.len() as f64, "{diff} pixels differ");
}
#[test]
fn gpu_matches_cpu_shadow_catcher() {
// #1691: the floor plane as a shadow catcher ("Accepts Shadows: Only").
let Some(g) = gpu() else { return };
let (mut p, cid) = scene_project();
let floor = p.comp_mut(cid).unwrap().layers.last_mut().unwrap();
set(floor, "materialOptions/acceptsShadows", Value::Enum(2));
let s = scene(&p, cid);
assert!(s.materials.iter().any(|m| m.shadows_only) && !s.shadows.is_empty());
compare(&s, &g);
}
#[test]
fn gpu_raster_perf_smoke() {
let Some(g) = gpu() else { return };