Add see-through X-ray reveal overlay for players

Add a second, additive HDR emission color (_XRayColor) that reveals a
player through walls as a pulsing energy glow, driven during matches:

- Shader: XRayOverlay pass (ZTest Greater, additive) for occluded parts
  plus a matching additive term in the forward fragment for visible
  parts, so the glow shows everywhere and never double-blends. Exposes
  _XRayColor, _XRayPulseSpeed and _XRayPulseStrength for a breathing pulse.
- Render it after opaques via a RenderObjects feature (custom XRayReveal
  light-mode tag) on both PC and Mobile renderers, so the through-wall
  test is depth-sort independent and no longer drops out at some camera
  angles/distances.
- MeshPainter drives _XRayColor alpha through a framerate-independent
  fade (xrayFadeDuration) rather than a hard toggle.
- Reveal is driven per avatar on every client: all remaining un-found
  hiders glow during the Resolution phase, and a hider whose
  ConcealmentMeter EmbeddedPercent exceeds MatchManager.embeddedRevealPercent
  glows via an owner-authoritative NetworkVariable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-01 12:22:17 +02:00
1 parent d7104f9314
commit c54e0cf502
8 files changed
+338 -13

No files matched your search

+18
View File
@@ -51,6 +51,12 @@ half _HeightNormalIntensity;
half _OcclusionThreshold;
float4 _OcclusionMap_TexelSize;
// X-Ray reveal glow (rgb = HDR color, a = fade/opacity master). Consumed here for the VISIBLE parts;
// the separate XRayOverlay pass (SRPDefaultUnlit) covers the OCCLUDED parts. See CamoForwardFragment.
half4 _XRayColor;
half _XRayPulseSpeed; // 0 = steady; otherwise breathing pulse via _Time (see CamoForwardFragment)
half _XRayPulseStrength; // modulation depth of the pulse around the authored intensity
// Remaps the painted occlusion/height value into surface AO + a relief normal. Runs after URP's
// InitializeStandardLitSurfaceData (uv is already parallax-displaced) and before InitializeInputData,
// so the relief normal flows into inputData.normalWS through the tangent frame.
@@ -285,6 +291,18 @@ void CamoForwardFragment(
half4 color = UniversalFragmentCamo(inputData, surfaceData);
color.rgb = MixFog(color.rgb, inputData.fogCoord);
// X-Ray reveal glow on the VISIBLE parts. URP draws the SRPDefaultUnlit XRayOverlay pass BEFORE
// this UniversalForward pass (see DrawObjectsPass shader-tag order), so that pass alone can only
// survive on OCCLUDED pixels - this opaque forward write overwrites the visible ones. Adding the
// same additive term here reveals the glow on the visible parts too. Visible (here) and occluded
// (overlay, ZTest Greater) are mutually exclusive by depth => no double contribution. Added
// post-fog to match the overlay, which outputs the raw color.
// Breathing pulse (sin over _Time) on the glow master, matching the XRayOverlay pass exactly so the
// visible and occluded halves stay in phase. Speed 0 => term is 1 => identical to no pulse.
half xrayPulse = 1.0h + sin(_Time.y * _XRayPulseSpeed) * _XRayPulseStrength;
color.rgb += _XRayColor.rgb * (_XRayColor.a * xrayPulse);
color.a = OutputAlpha(color.a, IsSurfaceTypeTransparent(_Surface));
outColor = color;