fix(render): A7 Fix D D-3/D-4 — two-path lighting (objects plain-Lambert+sun, EnvCell wrap+no-sun) (#140)
mesh_modern unified all meshes into one calc_point_light path: it applied the bake's half-Lambert wrap to objects (lighting character backs from a torch behind them) and added the sun to EnvCell building shells (warm facade wash). Retail splits these: objects = hardware plain Lambert max(0,N.L) + sun; EnvCell walls = baked wrap, dynamics only, NO sun (minimize_envcell_lighting). Add a per-draw uLightingMode (WbDrawDispatcher=0 object, EnvCellRenderer=1 envcell) selecting the angular term (wrap vs plain Lambert) and gating the sun. Per-light cap + D-1 clamp unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
156dc453c9
commit
0980bea48d
3 changed files with 26 additions and 15 deletions
|
|
@ -122,6 +122,7 @@ uniform mat4 uViewProjection;
|
|||
// _opaqueDrawCount before the transparent MDI call, matching WorldBuilder's
|
||||
// uDrawIDOffset pattern in BaseObjectRenderManager.cs line 845.
|
||||
uniform int uDrawIDOffset;
|
||||
uniform int uLightingMode; // A7 Fix D: 0 = OBJECT (plain Lambert + sun), 1 = ENVCELL (half-Lambert wrap, no sun)
|
||||
|
||||
// SceneLighting UBO — binding=1 in the UBO namespace (GL keeps the SSBO and UBO
|
||||
// binding tables separate, so this coexists with the binding=1 BatchBuffer SSBO
|
||||
|
|
@ -157,16 +158,19 @@ vec3 pointContribution(vec3 N, vec3 worldPos, GlobalLight L) {
|
|||
float d = sqrt(distsq);
|
||||
float range = L.dirAndRange.w; // falloff_eff = Falloff × 1.3
|
||||
if (d >= range || range <= 1e-4) return vec3(0.0);
|
||||
// Half-Lambert WRAP: (1/1.5)·(N·D + 0.5·d). N·D = d·cosθ (D un-normalised); the
|
||||
// +0.5·d bias lets a face angled AWAY from the torch still catch light (retail's
|
||||
// soft terminator). wrap≤0 = fully shadowed. TwoLpr=1.5, WrapBias=0.5.
|
||||
float wrap = (1.0 / 1.5) * (dot(N, toL) + 0.5 * d);
|
||||
if (wrap <= 0.0) return vec3(0.0);
|
||||
// A7 Fix D D-3: angular term by lighting path. ENVCELL bake (mode 1) keeps the
|
||||
// half-Lambert wrap (lights surfaces angled away, retail calc_point_light); OBJECT
|
||||
// mode (0) uses plain Lambert max(0,N·L) so a torch BEHIND a character contributes
|
||||
// nothing (retail's hardware path). toL is un-normalised (length d).
|
||||
float angular = (uLightingMode == 1)
|
||||
? (1.0 / 1.5) * (dot(N, toL) + 0.5 * d) // half-Lambert wrap (EnvCell bake)
|
||||
: max(0.0, dot(N, toL)); // plain Lambert (object/hardware)
|
||||
if (angular <= 0.0) return vec3(0.0);
|
||||
// NORM branch (distance-cube): >1 m → distsq·d ≈ inverse-square soft far halo;
|
||||
// <1 m → just d (dodge the near singularity). "Punchy near, soft far."
|
||||
float norm = (distsq > 1.0) ? (distsq * d) : d;
|
||||
float intensity = L.colorAndIntensity.w;
|
||||
float scale = (1.0 - d / range) * intensity * (wrap / norm);
|
||||
float scale = (1.0 - d / range) * intensity * (angular / norm);
|
||||
if (kind == 2) {
|
||||
// Spotlight: hard-edged cos-cone gate layered on the point ramp.
|
||||
vec3 Ldir = toL / max(d, 1e-4);
|
||||
|
|
@ -183,15 +187,18 @@ vec3 pointContribution(vec3 N, vec3 worldPos, GlobalLight L) {
|
|||
vec3 accumulateLights(vec3 N, vec3 worldPos, int instanceIndex) {
|
||||
vec3 lit = uCellAmbient.xyz;
|
||||
|
||||
// SUN / directional — material-lit term (added with ambient, NOT into the
|
||||
// torch sum), unchanged.
|
||||
int activeLights = int(uCellAmbient.w);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (i >= activeLights) break;
|
||||
if (int(uLights[i].posAndKind.w) != 0) continue; // directional only
|
||||
vec3 Ldir = -uLights[i].dirAndRange.xyz;
|
||||
float ndl = max(0.0, dot(N, Ldir));
|
||||
lit += uLights[i].colorAndIntensity.xyz * uLights[i].colorAndIntensity.w * ndl;
|
||||
// SUN / directional — OBJECT path only (mode 0). retail's EnvCell path
|
||||
// (minimize_envcell_lighting) enables only dynamic lights, NEVER the sun, so
|
||||
// EnvCell walls (mode 1) get no directional sun wash (A7 Fix D D-4).
|
||||
if (uLightingMode == 0) {
|
||||
int activeLights = int(uCellAmbient.w);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (i >= activeLights) break;
|
||||
if (int(uLights[i].posAndKind.w) != 0) continue; // directional only
|
||||
vec3 Ldir = -uLights[i].dirAndRange.xyz;
|
||||
float ndl = max(0.0, dot(N, Ldir));
|
||||
lit += uLights[i].colorAndIntensity.xyz * uLights[i].colorAndIntensity.w * ndl;
|
||||
}
|
||||
}
|
||||
|
||||
// POINT / SPOT torches: their OWN accumulator (A7 Fix D, D-1). Retail's
|
||||
|
|
|
|||
|
|
@ -877,6 +877,7 @@ public sealed unsafe class EnvCellRenderer : IDisposable
|
|||
// WB EnvCellRenderManager.cs:406-409: uniform state setup.
|
||||
_shader.SetInt("uRenderPass", (int)renderPass);
|
||||
_shader.SetInt("uFilterByCell", 0);
|
||||
_shader.SetInt("uLightingMode", 1); // A7 Fix D D-3/D-4: EnvCell bake (wrap points, no sun)
|
||||
|
||||
// Phase U.4 ROOT-CAUSE FIX (cell-shell flicker / "transparent walls when
|
||||
// moving"): upload uViewProjection HERE rather than inheriting it from
|
||||
|
|
|
|||
|
|
@ -893,6 +893,9 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
|||
_indoorProbeFrameCounter++;
|
||||
var vp = camera.View * camera.Projection;
|
||||
_shader.SetMatrix4("uViewProjection", vp);
|
||||
// A7 Fix D D-3/D-4: object path — plain Lambert points + sun. MUST set
|
||||
// explicitly (shared GL uniform; EnvCellRenderer sets it to 1).
|
||||
_shader.SetInt("uLightingMode", 0);
|
||||
|
||||
// #128 self-heal: fresh re-request dedup per Draw pass.
|
||||
_missRequested.Clear();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue