Skip to content

fix neon texture banding: restore stock alpha refs, gate the dual pass#5005

Open
Zephkek wants to merge 1 commit into
multitheftauto:masterfrom
Zephkek:fix/neon-alpharef-restore
Open

fix neon texture banding: restore stock alpha refs, gate the dual pass#5005
Zephkek wants to merge 1 commit into
multitheftauto:masterfrom
Zephkek:fix/neon-alpharef-restore

Conversation

@Zephkek

@Zephkek Zephkek commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

GTA runs the world passes with ALPHAREF 140 (CRenderer::RenderEverythingBarRoads) and 100 (CVisibilityPlugins::RenderEntity), alpha func GREATER. #4751 patched both to 0 for #425, #4807 made them 1 and added a D3D level dual pass that overrides the alpha test for every blended z-writing draw. between the two, every low alpha gradient pixel vanilla throws away started rendering and the LV neon lines turned into the smeared bands reported against 1.7. 1.6 has neither change, so it renders like vanilla. this is also why disabling every render hook found nothing, there was nothing to find, it's a byte patch plus a device level draw split.

#4996 restored the RenderEntity ref and left the world pass at 1. its fallback reads materials[0].color.a at RenderOneNonRoad entry, but MTA multiplies element alpha into the materials later, inside the CObject::Render hook, and undoes it after the call. the check reads base values, never sees setElementAlpha, fires on stock models with low material alpha instead, and never restores what it writes. none of it matters for blended draws anyway, the dual pass swaps the test for GREATEREQUAL 128 / LESS 128 and the sub-ref gradient still renders in pass 2.

so:

  • the MemPuts are gone, both refs stay stock. world geometry renders like vanilla and 1.6
  • the dual pass only runs when the game has no meaningful alpha test of its own (ref at the DefinedState baseline of 2 or below). an active test at 100/140 already clips transparent pixels before z-write, like vanilla. running the dual pass over an active test is what kept the neon smeared no matter what the ref said
  • objects with element alpha A < 255 get the ref scaled to ref * A / 255 around their RenderOneNonRoad call, saved and restored. the materials shrink by the same factor, the test cuts the same pixels it always did, and ref * A / 255 < A means the object can't go invisible. both render paths go through RenderOneNonRoad so one hook covers everything. player peds, cars and bikes set ref 1 and restore it inside their own Render fns, they were never affected

binary side (RwDevice fp offsets +0x20/+0x24, get/set round trip, hook prologue relocation, RenderOneNonRoad caller graph, type field at +0x36) verified against 1.0 US in IDA.

Test plan

  1. LV neon lines render thin and sharp, same as vanilla / 1.6
  2. object 1337 + setElementAlpha 140 stays visible (Objects with alpha below 141 are invisible #425 repro), any alpha works, opaque and alpha flagged models
  3. vegetation and fence edges match vanilla, no soft fringes, no see-through z artifacts
  4. fading entities still get the dual pass z-write emulation
  5. nothing rendered after a low alpha object inherits the scaled ref

Checklist

  • Your code should follow the coding guidelines.
  • Smaller pull requests are easier to review. If your pull request is beefy, your pull request should be reviewable commit-by-commit.

GTA runs the world passes with ALPHAREF 140 (CRenderer::RenderEverythingBarRoads)
and 100 (CVisibilityPlugins::RenderEntity), alpha func GREATER. multitheftauto#4751 patched both
to 0 for multitheftauto#425, multitheftauto#4807 made them 1 and added a D3D level dual pass that overrides
the alpha test for every blended z-writing draw. between the two, every low alpha
gradient pixel vanilla throws away started rendering and the LV neon lines turned
into smeared bands. 1.6 has neither change, so it renders like vanilla.

multitheftauto#4996 restored the RenderEntity ref and left the world pass at 1. its fallback
reads materials[0].color.a at RenderOneNonRoad entry, but MTA multiplies element
alpha into the materials later, inside the CObject::Render hook, and undoes it
after the call. the check reads base values, never sees setElementAlpha, fires on
stock models with low material alpha instead, and never restores what it writes.
none of it matters for blended draws anyway, the dual pass swaps the test for
GREATEREQUAL 128 / LESS 128 and the sub-ref gradient still renders in pass 2.

so:

- the MemPuts are gone, both refs stay stock
- the dual pass only runs when the game has no meaningful alpha test of its own
  (ref at the DefinedState baseline of 2 or below). an active test at 100/140
  already clips transparent pixels before z-write, like vanilla
- objects with element alpha A < 255 get the ref scaled to ref * A / 255 around
  their RenderOneNonRoad call, saved and restored. setElementAlpha works at any
  value on both render paths and cutouts keep their vanilla shape

neon renders like 1.6 again, multitheftauto#425 stays fixed.
@Dryxio

Dryxio commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Wouldn't setElementAlpha on a boat or train make it fully invisible?

@Zephkek

Zephkek commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Wouldn't setElementAlpha on a boat or train make it fully invisible?

Yes they would, but that has nothing to do with this PR, this also happens on the current 1.6 release and every earlier one, can easily be fixed by just extending the same scaled ref override to vehicles ill open pr in a few minutes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants