From 7dff91a92353377428aa0b739bcbe7517dfe3947 Mon Sep 17 00:00:00 2001
From: pkuzic
Date: Fri, 25 Sep 2026 02:52:47 +0100
Subject: [PATCH 1/4] MONKEY (ao): depth-only half-res SSAO pass before the
water copy, cvar ambientOcclusion + Advanced Graphics row
Co-Authored-By: Claude Opus 5.5
---
LIGHTING.md | 1 +
crates/benilla-app/assets/ui/OptionsFrame.xml | 36 ++
crates/benilla-app/src/capture/scenarios.rs | 15 +
crates/benilla-app/src/cvars.rs | 5 +
crates/benilla-app/src/game_plugins.rs | 2 +
crates/benilla-app/src/lib.rs | 2 +
crates/benilla-app/src/shaders/ssao.wgsl | 174 +++++++
crates/benilla-app/src/ssao.rs | 484 ++++++++++++++++++
.../src/ui_script/options_tests.rs | 40 +-
crates/benilla-app/src/video.rs | 6 +
crates/benilla-world/src/liquid/mod.rs | 3 +-
.../benilla-world/src/liquid/scene_depth.rs | 3 +-
12 files changed, 768 insertions(+), 3 deletions(-)
create mode 100644 crates/benilla-app/src/shaders/ssao.wgsl
create mode 100644 crates/benilla-app/src/ssao.rs
diff --git a/LIGHTING.md b/LIGHTING.md
index 2122d3df..6dd43bc3 100644
--- a/LIGHTING.md
+++ b/LIGHTING.md
@@ -24,6 +24,7 @@ behind each constant.
| Moon shadows | at night the same shadow rig re-aims at the moon and casts a faint shadow; dims only the night sky term, never point lights | `moonShadowStrength` |
| Ground-effect spells | Flamestrike, Rain of Fire, Consecration, Flare and fire traps light the ground for their duration; frost and nature areas stay dark | `spellLightGain` |
| Volumetric fog | near-field haze that converges on the zone fog colour (clear within 10 yd, full by 150 yd; mistier at dawn and in bad weather, faint indoors) and sun/moon light shafts through gaps, sampled from the shadow map; own fullscreen pass after the main pass (`benilla-app/src/volumetric_fog.rs`) | Advanced Graphics → Volumetric Fog (Off/Low/High), cvar `volumetricFog`, env `WOW_VOLFOG=0\|1\|2` |
+| Ambient occlusion | soft contact shadows under props, in wall-floor corners and around feet; depth-only SSAO at half resolution (depth-reconstructed normals, 6/12 spiral taps, 4x4 depth-aware blur, bilateral upsample) multiplied into the opaque scene after the opaque pass and before the water copy, so water, particles and UI are untouched; fades out 35-80 yd, spares sky and bright pixels (`benilla-app/src/ssao.rs`, `benilla-app/src/shaders/ssao.wgsl`) | Advanced Graphics → Ambient Occlusion (Off/Low/High; default Off, High preset 2), cvar `ambientOcclusion`, env `WOW_AO=0\|1\|2`, `WOW_AO_DEBUG=1` shows the AO term alone |
| Night and interior level | global dimming of the night sky term and of interior ambient | `nightGain`, `interiorGain`, `interiorBakeFloor` |
Players reach all of it from **Options -> Advanced Graphics** (a Lighting Quality preset Off / Low / Medium / High plus the individual rows; Off is the original client look). The dev build has a panel for all of it: **Ctrl+Shift+D → Lighting & shadows**, with Dim / Default /
diff --git a/crates/benilla-app/assets/ui/OptionsFrame.xml b/crates/benilla-app/assets/ui/OptionsFrame.xml
index 71e19e82..4e660703 100644
--- a/crates/benilla-app/assets/ui/OptionsFrame.xml
+++ b/crates/benilla-app/assets/ui/OptionsFrame.xml
@@ -519,6 +519,8 @@
spellLights = "Spell Lights",
nightDarkness = "Night Darkness",
interiorDarkness = "Interior Darkness",
+ -- MONKEY (ao): localised contact-shadow row.
+ ambientOcclusion = "Ambient Occlusion",
yards = "%d yd",
tips = {
LIGHTING_QUALITY =
@@ -611,6 +613,13 @@
"Scales interior fixture light and room ambient, including the light from "
.. "lamps and candles. Lower values make both dimmer.\n\n"
.. "1.0 leaves these inputs unchanged. Free at any setting.",
+ -- MONKEY (ao): what the contact shadows touch and what they spare.
+ AMBIENT_OCCLUSION =
+ "Soft contact shadows where surfaces meet: under props and furniture, in the corners "
+ .. "between walls and floors, and around characters' feet. Low uses fewer samples; "
+ .. "High is smoother and reaches a little further.\n\n"
+ .. "Fades out with distance and spares the sky, water, lit windows and flames. "
+ .. "Off leaves the image unchanged.",
},
},
ruRU = {
@@ -643,6 +652,8 @@
spellLights = "Свет от заклинаний",
nightDarkness = "Темнота ночи",
interiorDarkness = "Темнота помещений",
+ -- MONKEY (ao): Russian contact-shadow row.
+ ambientOcclusion = "Затенение окружения",
yards = "%d ярд.",
tips = {
LIGHTING_QUALITY =
@@ -743,6 +754,13 @@
"Масштабирует свет светильников и фоновое освещение помещений, включая "
.. "свет ламп и свечей. Меньшие значения ослабляют и то и другое.\n\n"
.. "1.0 оставляет эти составляющие без изменений. Ничего не стоит при любом значении.",
+ -- MONKEY (ao): Russian contact-shadow explanation.
+ AMBIENT_OCCLUSION =
+ "Мягкие контактные тени там, где поверхности соприкасаются: под предметами и мебелью, "
+ .. "в углах между стенами и полом, у ног персонажей. «Низкое» использует меньше выборок; "
+ .. "«Высокое» — более плавное и захватывает чуть больше.\n\n"
+ .. "Ослабевает с расстоянием и не затрагивает небо, воду, освещённые окна и пламя. "
+ .. "«Выкл» оставляет изображение без изменений.",
},
},
}
@@ -801,6 +819,8 @@
"RowInteriorLight", "RowInteriorShadows", "RowExteriorShadows", "RowTorchSoft",
"RowFireLight", "RowFireFlicker", "RowSpellLights", "RowLavaGlow",
"RowNightDarkness", "RowInteriorDarkness",
+ -- MONKEY (ao): appended so the other rows keep their indices.
+ "RowAmbientOcclusion",
},
-- Player Guild Names is a child of Player Names, as in 1.12 (l.702-708).
Nameplates = {
@@ -2827,6 +2847,22 @@
OptionsSliderRow_Setup(self, 0.2, 1.5, 0.05, "darkness")
+
+
+
+
+
+
+
+ OptionsRow_OnLoad(self, "ambientOcclusion", BENILLA_ADVGFX.ambientOcclusion,
+ "BENILLA_TOOLTIP_AMBIENT_OCCLUSION")
+ OptionsDropdownRow_Setup(self, {
+ { text = BENILLA_ADVGFX.fogOff, value = "0" },
+ { text = BENILLA_ADVGFX.qLow, value = "1" },
+ { text = BENILLA_ADVGFX.qHigh, value = "2" },
+ })
+
+