fix(render): detail overlay is fogged after the combine like retail; VM1 review fixes
Opus dual-lens review of05970306+388457a7(APPROVE WITH FIXES). Four items, all landed: 1. FOG (behavioural). Retail's D3D fixed-function fog stage runs AFTER the texture-stage pipeline, so the detail contribution must be fogged, not just the base. mesh_modern.frag already fogs the base colour (applyFog(rgb, vWorldPos)) before mesh_detail's replay draws over it; mesh_detail.frag previously emitted raw detail.rgb, understating fog by f*a*(fog-detail). Fix: mesh_detail.vert now outputs vWorldPos (mirroring mesh_modern.vert); mesh_detail.frag declares the identical SceneLighting UBO and applyFog function (copied verbatim, same binding/std140/math) and fogs detail.rgb before emitting it. This collapses algebraically to retail's fog-after-combine order: (1-a)*mix(base,fog,f) + a*mix(detail,fog,f) = mix(lerp(base,detail,a),fog,f) RetailDetailTextureContract gains ExpectedFogged(base,detail,opacity,fog, fogFactor); RetailDetailTextureContractTests pins the identity across 200 random samples within 1e-6. 2. EnvCellRenderer.Rhi.cs's DrawEnvCell-category comment still said "apply the 10-50 m positive-view-depth fade" — a stale claim from before VM1 removed the fade. Replaced with the mip-chain attenuation statement that mesh_detail.vert's header comment already carries. 3. Added the test the VM1 contract required but never had: TerrainAtlas .TryCreateDetailTexture uploads a full mip chain (MipLevelCount == RhiWorldTextureArray.MipLevelsFor(w,h), GenerateMipChain called) and registers with the repeat/linear world sampler, not single-level or clamped. Drives the private method directly (reflection) against a synthetic PFID_A8R8G8B8 RenderSurface through a minimal in-memory IDatReaderWriter fake, so the lane stays hermetic (no installed DAT). 4. #226 pseudocode note: noted that retail's stage-1 OUTPUT alpha (MODULATE(TEXTURE, CURRENT), 0x0059c549) — the framebuffer blend weight a delayed-alpha subset composites with — is not modelled; acdream instead draws a second pass weighted by detail.a*diffuseAlpha. Identical for opaque subsets, a bounded difference on translucent building/EnvCell subsets already covered by the existing AP-34 shared-alpha-queue divergence row. Also qualified the tmpmaterial.Diffuse.a = 1f (0x0059cb99) citation to name its exact branch (burnedInStaticLights < 0 && *(render_device+0x7e4) == 0); the other branch leaves diffuse FromVertex, but the opaque->1 / fading->opacity mapping still holds either way. Nit also folded in: EnvCellRendererTests' new SubmitRhi instance-alpha test is now a [Theory] over WbRenderPass.Opaque and .Transparent, pinning the bind-before-first-draw invariant on both passes. Regenerated mesh_detail's committed SPIR-V and the shader manifest (tools/compile-shaders.ps1); no other shader pair changed. Verified: dotnet build AcDream.slnx -c Release (0 warnings, 0 errors); dotnet test on AcDream.App.Tests (Release, hermetic lanes) green, including the shader manifest tests explicitly; AcDream.Core.Tests unaffected/green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
388457a735
commit
ae6513126e
11 changed files with 343 additions and 14 deletions
|
|
@ -105,9 +105,17 @@ for each replayed fragment:
|
|||
detail = sample(categoryTexture, baseUv * categoryTiling)
|
||||
diffuseAlpha = base_subset_diffuse_alpha // 1 for opaque; the
|
||||
// translucency-fade multiplier
|
||||
// for a fading subset
|
||||
// (tmpmaterial.Diffuse.a in
|
||||
// RenderMeshSubset)
|
||||
// for a fading subset.
|
||||
// tmpmaterial.Diffuse.a = 1f
|
||||
// (0x0059cb99) is the
|
||||
// burnedInStaticLights < 0 &&
|
||||
// *(render_device+0x7e4) == 0
|
||||
// branch in RenderMeshSubset;
|
||||
// the other branch leaves
|
||||
// diffuse FromVertex. Either
|
||||
// way the opaque->1 /
|
||||
// fading->opacity mapping
|
||||
// still holds.
|
||||
|
||||
// D3DPolyRender::SetSurface (0x0059c4d0) texture-stage setup:
|
||||
// stage 0 colour = MODULATE(TEXTURE, DIFFUSE) = base.rgb * diffuse.rgb
|
||||
|
|
@ -176,6 +184,18 @@ change base coverage/blend/depth behavior; it avoids making the checkbox
|
|||
reorder the default transparent scene. The connected acceptance matrix must
|
||||
still exercise overlapping transparent building/EnvCell surfaces.
|
||||
|
||||
Also unmodelled: retail's stage-1 OUTPUT alpha — `MODULATE(TEXTURE, CURRENT)`
|
||||
(`0x0059c549`) — which for a delayed-alpha subset becomes the framebuffer
|
||||
blend weight the alpha queue composites that subset with. acdream instead
|
||||
draws the base subset with its own alpha and a second, separately blended
|
||||
draw weighted by `detail.a * diffuseAlpha` (the pipeline in
|
||||
`VulkanViewportMapping.BlendFactorsOf(GpuBlendMode.RetailDetail)`). For
|
||||
opaque subsets this is identical (both reduce to the base's own alpha
|
||||
gating nothing else downstream); on translucent building/EnvCell subsets it
|
||||
is a bounded difference in how much the SUBSEQUENT alpha-queue compositing
|
||||
sees, already covered by the existing AP-34 shared-alpha-queue divergence
|
||||
row rather than a new one.
|
||||
|
||||
## Darkening, not brightening
|
||||
|
||||
The earlier version of this note read the two-pass fallback's `DEST_COLOR +
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue