From 07d16048b2a002eefd5f9dc2c8ab80606b33f7d1 Mon Sep 17 00:00:00 2001
From: pkuzic
Date: Sat, 10 Oct 2026 05:57:28 +0100
Subject: [PATCH] MONKEY (gfx seam W): retained character wetness is wet on
every facing (wet_body); rain keeps the up-facing law
Open graphics, cherry-pickable to lighting together with 517868a2.
Co-Authored-By: Claude Opus 5.5
---
.../benilla-assets/src/shaders/wet_hook.wgsl | 18 ++++++++++++++++++
.../benilla-assets/src/shaders/wow_model.wgsl | 6 +++---
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/crates/benilla-assets/src/shaders/wet_hook.wgsl b/crates/benilla-assets/src/shaders/wet_hook.wgsl
index c09903e4..8cbd1c6a 100644
--- a/crates/benilla-assets/src/shaders/wet_hook.wgsl
+++ b/crates/benilla-assets/src/shaders/wet_hook.wgsl
@@ -97,6 +97,24 @@ fn wet_surface(albedo: vec3, n: vec3, world_pos: vec3, exposure:
return o;
}
+// MONKEY (motion verify 2): a character's wetness. `retained` (waded/swum/soaked, the
+// monkey-surfacefx row) is NOT facing-weighted like rain on a surface: a body out of the water is
+// wet all round, so its sides (legs, arms, flanks: n.y ~ 0) darken fully and keep a gloss. `rain`
+// (exposed rain now) keeps the surface law (tops only). Same darken/saturate law as `wet_surface`.
+const WET_BODY_UNDERSIDE: f32 = 0.85; // undersides a touch less (dripped off, shaded)
+const WET_BODY_GLOSS: f32 = 0.7; // gloss weight on every facing at full retained wetness
+fn wet_body(albedo: vec3, n: vec3, rain: f32, retained: f32) -> WetSurface {
+ var o: WetSurface;
+ let w_rain = wet_amount(n, 1.0, rain);
+ let w_body = clamp(retained, 0.0, 1.0) * mix(WET_BODY_UNDERSIDE, 1.0, smoothstep(-0.6, 0.4, n.y));
+ let w = max(w_rain, w_body);
+ let luma = dot(albedo, vec3(0.299, 0.587, 0.114));
+ let sat = max(mix(vec3(luma), albedo, 1.0 + 0.3 * w), vec3(0.0));
+ o.albedo = sat * (1.0 - 0.36 * w);
+ o.boost = max(w_rain * smoothstep(0.35, 0.9, n.y), w_body * WET_BODY_GLOSS);
+ return o;
+}
+
fn wet_hash(p: vec2) -> f32 {
let q = fract(p * vec2(0.1031, 0.1030));
let r = q + dot(q, q.yx + 33.33);
diff --git a/crates/benilla-assets/src/shaders/wow_model.wgsl b/crates/benilla-assets/src/shaders/wow_model.wgsl
index 0d7b0207..892a08a4 100644
--- a/crates/benilla-assets/src/shaders/wow_model.wgsl
+++ b/crates/benilla-assets/src/shaders/wow_model.wgsl
@@ -1802,11 +1802,11 @@ fn fragment(in: WowVsOut, @builtin(front_facing) is_front: bool) -> WowFragOut {
let surface_pass = surface_alpha_mode <= 1u || m.model_flags.y > 0.5;
if (surface_slot != 0u && instance_wet > 0.0 && surface_pass && !is_wmo && !is_emissive
&& !is_mod && !is_mod2x && (u32(m.clutter_fade.z) & 4u) == 0u) {
- var surface_frame = wow_light.monkey;
// MONKEY: max of exposed rain and retained wetness, one darken/gloss application.
+ // MONKEY (motion verify 2): retained wetness is wet on every facing (`wet_body`); before,
+ // the rain law's facing weight left a swum body's vertical sides ~6 % darker and unglossed.
let rain_wet = select(clamp(wow_light.monkey.wet_a.y, 0.0, 1.0) * wet_open, 0.0, wet_dry);
- surface_frame.wet_a.y = max(rain_wet, instance_wet);
- wet = wet_hook::wet_surface(rgb, n_lit, in.world_position.xyz, 1.0, surface_frame);
+ wet = wet_hook::wet_body(rgb, n_lit, rain_wet, instance_wet);
}
rgb = wet.albedo;
if (wet.boost > 0.0) {