MONKEY (rainshelter): receivers read the shelter map
wet_hook gains shelter_taps/shelter_cell/shelter_amount (bilinear over four cells; sheltered when a covered top is >= ~1 yd above the fragment, the roof's own surface stays open). terrain, wow_model and static_gx mirror the region after monkey and scale wet exposure by 1 - shelter (wet_puddles takes exposure, so no puddles under cover); enhanced_water suppresses rain rings under bridges/roofs via its row view. The lookup runs only while wetness > 0 and the header is active, so a dry frame is unchanged. Verified: the wet_hook + receiver/water lookup snippets composed and validated with naga_oil 0.20 / naga 27 (scratch harness; unpack2x16float avoided - it needs a float16 capability). Full receiver shaders were not compiled here (no GPU, no game data). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8t89gSUuo1uvbXKdh1mJt
This commit is contained in:
@@ -25,6 +25,8 @@
|
||||
#import bevy_pbr::mesh_view_bindings::{view, globals}
|
||||
// MONKEY (p0 MonkeyFrame): the programme block's struct, mirrored after the point table.
|
||||
#import benilla::monkey_frame
|
||||
// MONKEY (rainshelter): the shelter-grid lookup the surface receivers share.
|
||||
#import benilla::wet_hook
|
||||
// MONKEY (p0 fog hook): the one distance-fog law every receiver calls.
|
||||
#import benilla::fog_hook
|
||||
|
||||
@@ -66,6 +68,25 @@ fn water_monkey() -> monkey_frame::MonkeyFrame {
|
||||
return m;
|
||||
}
|
||||
|
||||
// MONKEY (rainshelter): the rain-occlusion grid right after the MonkeyFrame block
|
||||
// (`weather/shelter.rs`): two header rows, then the packed cell words, four to a row.
|
||||
const SHELTER_ROW: u32 = 549u;
|
||||
fn water_shelter_word(i: u32) -> u32 {
|
||||
return bitcast<u32>(water_light[SHELTER_ROW + 2u + i / 4u][i % 4u]);
|
||||
}
|
||||
// 1 open .. 0 under a bridge or a roof.
|
||||
fn water_rain_open(world_pos: vec3<f32>) -> f32 {
|
||||
let hdr = water_light[SHELTER_ROW];
|
||||
let cfg = water_light[SHELTER_ROW + 1u];
|
||||
let st = wet_hook::shelter_taps(world_pos, hdr, cfg);
|
||||
if (st.live <= 0.0) {
|
||||
return 1.0;
|
||||
}
|
||||
return 1.0 - wet_hook::shelter_amount(st, water_shelter_word(st.idx.x),
|
||||
water_shelter_word(st.idx.y), water_shelter_word(st.idx.z), water_shelter_word(st.idx.w),
|
||||
world_pos.y, cfg);
|
||||
}
|
||||
|
||||
// What the fragment stage hands over: the three varyings this module reads.
|
||||
struct WaterFragment {
|
||||
clip_position: vec4<f32>,
|
||||
@@ -256,13 +277,16 @@ fn ripple_hash(p: vec2<f32>) -> vec2<f32> {
|
||||
q += dot(q, q.yzx + 33.33);
|
||||
return fract((q.xx + q.yz) * q.zy);
|
||||
}
|
||||
fn rain_ripple_normal(world_xz: vec2<f32>, footprint: f32, distance: f32, room: bool) -> vec2<f32> {
|
||||
fn rain_ripple_normal(world_pos: vec3<f32>, footprint: f32, distance: f32, room: bool) -> vec2<f32> {
|
||||
let wet_row = water_monkey().wet_a;
|
||||
let rain = clamp(wet_row.x, 0.0, 1.0);
|
||||
if rain <= 0.0 || water.lane.x > 1.5 || room {
|
||||
return vec2<f32>(0.0);
|
||||
}
|
||||
let reach = (1.0 - smoothstep(35.0, 60.0, distance)) * (1.0 - smoothstep(0.05, 0.14, footprint));
|
||||
let world_xz = world_pos.xz;
|
||||
// MONKEY (rainshelter): no rings under a bridge or a roof.
|
||||
let reach = (1.0 - smoothstep(35.0, 60.0, distance)) * (1.0 - smoothstep(0.05, 0.14, footprint))
|
||||
* water_rain_open(world_pos);
|
||||
if reach <= 0.0 {
|
||||
return vec2<f32>(0.0);
|
||||
}
|
||||
@@ -595,7 +619,7 @@ fn enhanced_water(in: WaterFragment, shallow: vec4<f32>, deep: vec4<f32>) -> vec
|
||||
open_sea_fold = water_gerstner(p, t, shore).w;
|
||||
}
|
||||
// One surface gradient: the procedural bands and the shore break.
|
||||
let surf_grad = wave.yz + shore_grad + rain_ripple_normal(in.world_position.xz, footprint,
|
||||
let surf_grad = wave.yz + shore_grad + rain_ripple_normal(in.world_position.xyz, footprint,
|
||||
length(eye_pos), in.room_fog != 0u && water.lane.w > 0.5);
|
||||
var n = normalize(vec3<f32>(-surf_grad.x, 1.0, -surf_grad.y));
|
||||
if dot(n, to_view) < 0.0 { n = -n; }
|
||||
|
||||
@@ -64,6 +64,11 @@ struct WowLight {
|
||||
points: array<vec4<f32>, 512>,
|
||||
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
|
||||
monkey: monkey_frame::MonkeyFrame,
|
||||
// MONKEY (rainshelter): the rain-occlusion grid (`weather/shelter.rs`): `[origin_x, origin_z,
|
||||
// 1/cell, active]`, `[base_y, cells per side, 0, 0]`, then one packed word a cell (wet_hook.wgsl).
|
||||
shelter_hdr: vec4<f32>,
|
||||
shelter_cfg: vec4<f32>,
|
||||
shelter: array<u32, 16384>,
|
||||
};
|
||||
@group(#{MATERIAL_BIND_GROUP}) @binding(90) var<storage, read> wow_light: WowLight;
|
||||
|
||||
@@ -757,9 +762,20 @@ fn fragment(in: TerrainVsOut) -> @location(0) vec4<f32> {
|
||||
// specular = per-vertex sheen · gloss_mask · shadow → gated to ZERO in shadow (no sheen in shade)
|
||||
// (`tex·primary` is the MODULATE-1× diffuse; the sheen is added after, separate-specular.) Then
|
||||
// LDR-clamp; gamma/byte throughout; raw gamma out (GAMMA LANE, 0161).
|
||||
// MONKEY (rainshelter): nothing gets wet under a roof, porch or bridge (the shelter grid).
|
||||
var wet_open = 1.0;
|
||||
if (wow_light.monkey.wet_a.y > 0.0) {
|
||||
let wet_st = wet_hook::shelter_taps(in.world_position.xyz, wow_light.shelter_hdr,
|
||||
wow_light.shelter_cfg);
|
||||
if (wet_st.live > 0.0) {
|
||||
wet_open = 1.0 - wet_hook::shelter_amount(wet_st, wow_light.shelter[wet_st.idx.x],
|
||||
wow_light.shelter[wet_st.idx.y], wow_light.shelter[wet_st.idx.z],
|
||||
wow_light.shelter[wet_st.idx.w], in.world_position.y, wow_light.shelter_cfg);
|
||||
}
|
||||
}
|
||||
// MONKEY (wet): rain-darkened ground + puddles (wet_hook.wgsl); a dry frame returns `color`.
|
||||
let wet = wet_hook::wet_puddles(wet_hook::wet_surface(color, n_lit, in.world_position.xyz, 1.0,
|
||||
wow_light.monkey), n_lit, in.world_position.xyz, wow_light.monkey);
|
||||
let wet = wet_hook::wet_puddles(wet_hook::wet_surface(color, n_lit, in.world_position.xyz,
|
||||
wet_open, wow_light.monkey), n_lit, in.world_position.xyz, wet_open, wow_light.monkey);
|
||||
color = wet.albedo;
|
||||
let diffuse_term = color * primary * (0.3 * shadow_lit_eff + 0.7) * character_shadow_term;
|
||||
let spec_term = in.specular * specmask * spec_gate;
|
||||
|
||||
@@ -13,6 +13,57 @@
|
||||
|
||||
#import benilla::monkey_frame::MonkeyFrame
|
||||
|
||||
// MONKEY (rainshelter): the four cells around a fragment in the camera-centred shelter grid.
|
||||
// `hdr` = `[origin_x, origin_z, 1 / cell, active]`, `cfg` = `[base_y, cells per side, 0, 0]`;
|
||||
// `live` is 0 when the map is off or the fragment lies outside it (open sky).
|
||||
struct ShelterTaps {
|
||||
idx: vec4<u32>,
|
||||
frac: vec2<f32>,
|
||||
live: f32,
|
||||
}
|
||||
|
||||
fn shelter_taps(world_pos: vec3<f32>, hdr: vec4<f32>, cfg: vec4<f32>) -> ShelterTaps {
|
||||
var t: ShelterTaps;
|
||||
t.idx = vec4<u32>(0u);
|
||||
t.frac = vec2<f32>(0.0);
|
||||
t.live = 0.0;
|
||||
if (hdr.w < 0.5) {
|
||||
return t;
|
||||
}
|
||||
let g = (world_pos.xz - hdr.xy) * hdr.z - 0.5;
|
||||
let gf = floor(g);
|
||||
if (gf.x < 0.0 || gf.y < 0.0 || gf.x + 1.0 > cfg.y - 1.0 || gf.y + 1.0 > cfg.y - 1.0) {
|
||||
return t;
|
||||
}
|
||||
let side = u32(cfg.y);
|
||||
let first = u32(gf.y) * side + u32(gf.x);
|
||||
t.idx = vec4<u32>(first, first + 1u, first + side, first + side + 1u);
|
||||
t.frac = g - gf;
|
||||
t.live = 1.0;
|
||||
return t;
|
||||
}
|
||||
|
||||
// One cell: low half `(top − base_y) × 64 + 32768`, high half the soft cover as unorm16
|
||||
// (`shelter::pack_cell`). Sheltered when a covered top lies a yard or more above the fragment; the
|
||||
// roof's own surface (at `top`) stays open.
|
||||
fn shelter_cell(word: u32, y_rel: f32) -> f32 {
|
||||
let top = (f32(word & 0xffffu) - 32768.0) / 64.0;
|
||||
let cover = f32(word >> 16u) / 65535.0;
|
||||
return cover * smoothstep(0.5, 1.5, top - y_rel);
|
||||
}
|
||||
|
||||
// How sheltered the fragment is, 0 open .. 1 under cover, bilinear over the four cells.
|
||||
fn shelter_amount(t: ShelterTaps, wa: u32, wb: u32, wc: u32, wd: u32, world_y: f32,
|
||||
cfg: vec4<f32>) -> f32 {
|
||||
if (t.live <= 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
let y_rel = world_y - cfg.x;
|
||||
let near = mix(shelter_cell(wa, y_rel), shelter_cell(wb, y_rel), t.frac.x);
|
||||
let far = mix(shelter_cell(wc, y_rel), shelter_cell(wd, y_rel), t.frac.x);
|
||||
return clamp(mix(near, far, t.frac.y), 0.0, 1.0);
|
||||
}
|
||||
|
||||
struct WetSurface {
|
||||
albedo: vec3<f32>,
|
||||
// Gloss weight for `wet_sheen`: 0 dry, ~1 a soaked top face, up to 2 in a terrain puddle (the
|
||||
@@ -61,11 +112,13 @@ fn wet_noise(p: vec2<f32>) -> f32 {
|
||||
}
|
||||
|
||||
// Terrain bonus: puddle patches on flat ground once it is well soaked — darker, and a stronger
|
||||
// gloss weight so they read as standing water catching the sky.
|
||||
fn wet_puddles(s: WetSurface, n: vec3<f32>, world_pos: vec3<f32>, mf: MonkeyFrame) -> WetSurface {
|
||||
// gloss weight so they read as standing water catching the sky. MONKEY (rainshelter): `exposure`
|
||||
// as for `wet_surface`, so no puddle forms under cover.
|
||||
fn wet_puddles(s: WetSurface, n: vec3<f32>, world_pos: vec3<f32>, exposure: f32,
|
||||
mf: MonkeyFrame) -> WetSurface {
|
||||
let wet_row = mf.wet_a;
|
||||
var o = s;
|
||||
if (wet_row.y <= 0.0) {
|
||||
if (wet_row.y <= 0.0 || exposure <= 0.0) {
|
||||
return o;
|
||||
}
|
||||
let xz = world_pos.xz;
|
||||
@@ -73,7 +126,8 @@ fn wet_puddles(s: WetSurface, n: vec3<f32>, world_pos: vec3<f32>, mf: MonkeyFram
|
||||
let level = smoothstep(0.94, 0.99, n.y);
|
||||
// Puddles grow with the wetness: the threshold falls as the ground soaks.
|
||||
let edge = mix(0.8, 0.6, smoothstep(0.4, 1.0, wet_row.y));
|
||||
let puddle = smoothstep(edge, edge + 0.06, field) * level * smoothstep(0.35, 0.8, wet_row.y);
|
||||
let puddle = smoothstep(edge, edge + 0.06, field) * level * smoothstep(0.35, 0.8, wet_row.y)
|
||||
* clamp(exposure, 0.0, 1.0);
|
||||
o.albedo = o.albedo * (1.0 - 0.45 * puddle);
|
||||
o.boost = max(o.boost, 2.0 * puddle);
|
||||
return o;
|
||||
|
||||
@@ -126,6 +126,11 @@ struct WowLight {
|
||||
points: array<vec4<f32>, 512>,
|
||||
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
|
||||
monkey: monkey_frame::MonkeyFrame,
|
||||
// MONKEY (rainshelter): the rain-occlusion grid (`weather/shelter.rs`): `[origin_x, origin_z,
|
||||
// 1/cell, active]`, `[base_y, cells per side, 0, 0]`, then one packed word a cell (wet_hook.wgsl).
|
||||
shelter_hdr: vec4<f32>,
|
||||
shelter_cfg: vec4<f32>,
|
||||
shelter: array<u32, 16384>,
|
||||
// The interior-prop SH probes, 7 rows per slot (`MAX_PROP_PROBES` = 8192 slots). Only this
|
||||
// shader declares this tail; the other shaders bind the same buffer by its prefix.
|
||||
prop_probes: array<vec4<f32>, 57344>,
|
||||
@@ -1740,8 +1745,19 @@ fn fragment(in: WowVsOut, @builtin(front_facing) is_front: bool) -> WowFragOut {
|
||||
// Mod/Mod2x, additive and dry = untouched.
|
||||
let wet_dry = is_interior || interior_fogged || is_rig || is_emissive || is_mod || is_mod2x
|
||||
|| (u32(m.clutter_fade.z) & 4u) != 0u;
|
||||
let wet = wet_hook::wet_surface(rgb, n_lit, in.world_position.xyz, select(1.0, 0.0, wet_dry),
|
||||
wow_light.monkey);
|
||||
// MONKEY (rainshelter): nothing gets wet under a roof, porch or bridge (the shelter grid).
|
||||
var wet_open = 1.0;
|
||||
if (wow_light.monkey.wet_a.y > 0.0) {
|
||||
let wet_st = wet_hook::shelter_taps(in.world_position.xyz, wow_light.shelter_hdr,
|
||||
wow_light.shelter_cfg);
|
||||
if (wet_st.live > 0.0) {
|
||||
wet_open = 1.0 - wet_hook::shelter_amount(wet_st, wow_light.shelter[wet_st.idx.x],
|
||||
wow_light.shelter[wet_st.idx.y], wow_light.shelter[wet_st.idx.z],
|
||||
wow_light.shelter[wet_st.idx.w], in.world_position.y, wow_light.shelter_cfg);
|
||||
}
|
||||
}
|
||||
let wet = wet_hook::wet_surface(rgb, n_lit, in.world_position.xyz,
|
||||
select(1.0, 0.0, wet_dry) * wet_open, wow_light.monkey);
|
||||
rgb = wet.albedo;
|
||||
if (wet.boost > 0.0) {
|
||||
rgb = min(rgb + wet_hook::wet_sheen(wet.boost, n_lit, normalize(view.world_position - in.world_position.xyz),
|
||||
|
||||
@@ -52,6 +52,11 @@ struct WowLight {
|
||||
points: array<vec4<f32>, 512>,
|
||||
// MONKEY (p0 MonkeyFrame): the programme block after the point table (monkey_frame.wgsl).
|
||||
monkey: monkey_frame::MonkeyFrame,
|
||||
// MONKEY (rainshelter): the rain-occlusion grid (`weather/shelter.rs`): `[origin_x, origin_z,
|
||||
// 1/cell, active]`, `[base_y, cells per side, 0, 0]`, then one packed word a cell (wet_hook.wgsl).
|
||||
shelter_hdr: vec4<f32>,
|
||||
shelter_cfg: vec4<f32>,
|
||||
shelter: array<u32, 16384>,
|
||||
// lighting::prop_probes: 8192 slots of 7 rows; the buffer's later regions are not mirrored.
|
||||
prop_probes: array<vec4<f32>, 57344>,
|
||||
}
|
||||
@@ -1993,10 +1998,21 @@ fn fragment(in: GxVsOut) -> @location(0) vec4<f32> {
|
||||
if ((in.word & WORD_UNLIT) != 0u) {
|
||||
rgb = folded;
|
||||
}
|
||||
// MONKEY (rainshelter): nothing gets wet under a roof, porch or bridge (the shelter grid).
|
||||
var wet_open = 1.0;
|
||||
if (wow_light.monkey.wet_a.y > 0.0) {
|
||||
let wet_st = wet_hook::shelter_taps(in.world_position.xyz, wow_light.shelter_hdr,
|
||||
wow_light.shelter_cfg);
|
||||
if (wet_st.live > 0.0) {
|
||||
wet_open = 1.0 - wet_hook::shelter_amount(wet_st, wow_light.shelter[wet_st.idx.x],
|
||||
wow_light.shelter[wet_st.idx.y], wow_light.shelter[wet_st.idx.z],
|
||||
wow_light.shelter[wet_st.idx.w], in.world_position.y, wow_light.shelter_cfg);
|
||||
}
|
||||
}
|
||||
// MONKEY (wet): rain on sky-exposed surfaces (wet_hook.wgsl); interior, unlit and dry = untouched.
|
||||
let wet = wet_hook::wet_surface(rgb, n_lit, in.world_position.xyz,
|
||||
// MONKEY (fix-wet): lit WMO windows (WORD_WINDOW) are not darkened by rain either.
|
||||
select(1.0, 0.0, (in.word & (WORD_INTERIOR | WORD_UNLIT | WORD_WINDOW)) != 0u),
|
||||
select(1.0, 0.0, (in.word & (WORD_INTERIOR | WORD_UNLIT | WORD_WINDOW)) != 0u) * wet_open,
|
||||
wow_light.monkey);
|
||||
rgb = wet.albedo;
|
||||
if (wet.boost > 0.0) {
|
||||
|
||||
Reference in New Issue
Block a user