Add painted-height HUD sliders for AO shift and relief intensity

Wire three material-backed look sliders (occlusion AO shift, relief normal
intensity, parallax intensity) through the paint HUD, its layout/state
persistence, the .paint slot format (bumped to v4), owner-authoritative
network sync and the runtime reset, mirroring the existing camo-knob path so
they save, restore, replicate and reset alongside the brush values.

Change the occlusion AO mapping from a division threshold to a linear shift:
AO fades from 1 down to (1 - shift) at the deepest painted hole, so a 0 shift
no longer leaves black spots. Enable _NORMALMAP alongside _PARALLAXMAP so URP
transforms the painted relief normal into world space (otherwise it discards
normalTS and the relief is invisible), and pin the added keyword in the build
keep-alive material.

Stop the pipette from picking the occlusion/height value and turn its toggle
off after a pick. Default the knobs to 0.2 shift / 1.4 normal / 1.0 parallax
across the shader, painter, network and HUD fallbacks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-01 07:07:39 +02:00
1 parent fdcb4f8d8a
commit 59939b3496
11 files changed
+216 -49

No files matched your search

+17 -6
View File
@@ -40,10 +40,15 @@ half _SpecularDampen;
// The UV displacement is URP's stock single-step parallax (ParallaxOffset1Step already does
// height*amp - amp/2, i.e. the 0.5 centring) under the _PARALLAXMAP keyword. Here we add the
// matching per-pixel relief NORMAL from the height gradient (the dominant cue) and remap the ambient
// occlusion so only the LOWER half darkens: AO = saturate(occ*2) (occ>=0.5 -> no darkening, occ in
// [0,0.5] -> AO 1..0). Declared outside UnityPerMaterial like the camo knobs above (the player is a
// occlusion so only the LOWER half darkens, with the darkening DEPTH driven by _OcclusionThreshold
// (a shift, see below). Declared outside UnityPerMaterial like the camo knobs above (the player is a
// handful of instances, so the SRP-batcher break is irrelevant).
half _HeightNormalStrength;
// Runtime intensities tuned from the paint HUD. _HeightNormalIntensity scales the relief normal
// (default 1). _OcclusionThreshold is a SHIFT (0..1, default 0.5): the AO darkening depth at the
// deepest painted hole - 0 = no darkening at all, 1 = AO reaches full black at occ=0.
half _HeightNormalIntensity;
half _OcclusionThreshold;
float4 _OcclusionMap_TexelSize;
// Remaps the painted occlusion/height value into surface AO + a relief normal. Runs after URP's
@@ -52,9 +57,14 @@ float4 _OcclusionMap_TexelSize;
void ApplyPaintedHeightSurface(float2 uv, inout SurfaceData surfaceData)
{
#if defined(_OCCLUSIONMAP)
// Lower-half AO remap: occ>=0.5 stays fully lit, occ in [0,0.5] darkens toward 0.
// Only the lower (recessed) half of the painted height darkens. _OcclusionThreshold is a SHIFT =
// how DEEP the darkening reaches at the deepest hole: AO fades from 1 (occ>=0.5, neutral/hill)
// down to (1 - shift) at occ=0. shift=0 -> AO stays 1 everywhere (no darkening, no black spots),
// shift=0.5 -> AO in [0.5,1], shift=1 -> AO in [0,1] (full darkening). Linear over occ in [0,0.5].
half occ = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv).g;
surfaceData.occlusion = LerpWhiteTo(saturate(occ * 2.0h), _OcclusionStrength);
half depth = saturate(1.0h - occ * 2.0h); // 0 at occ>=0.5 (neutral/hill), 1 at occ=0 (deepest)
half ao = 1.0h - depth * _OcclusionThreshold; // darken toward (1 - shift) as the hole deepens
surfaceData.occlusion = LerpWhiteTo(ao, _OcclusionStrength);
#endif
#if defined(_PARALLAXMAP)
@@ -65,8 +75,9 @@ void ApplyPaintedHeightSurface(float2 uv, inout SurfaceData surfaceData)
half hR = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv + float2(texel.x, 0.0)).g;
half hD = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv - float2(0.0, texel.y)).g;
half hU = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv + float2(0.0, texel.y)).g;
half3 reliefTS = normalize(half3((hL - hR) * _HeightNormalStrength,
(hD - hU) * _HeightNormalStrength, 1.0h));
half strength = _HeightNormalStrength * _HeightNormalIntensity;
half3 reliefTS = normalize(half3((hL - hR) * strength,
(hD - hU) * strength, 1.0h));
surfaceData.normalTS = BlendNormalRNM(surfaceData.normalTS, reliefTS);
#endif
}
+5
View File
@@ -44,6 +44,11 @@ Shader "Custom/PlayerCamoLit"
_Parallax("Scale", Range(0.005, 0.08)) = 0.005
_ParallaxMap("Height Map", 2D) = "black" {}
_HeightNormalStrength("Painted Height Normal Strength", Range(0.0, 50.0)) = 8.0
// Runtime knobs for the painted-height look (1 = base, tuned live in the paint HUD):
_HeightNormalIntensity("Painted Height Normal Intensity", Range(0.0, 2.0)) = 1.4
// AO darkening DEPTH (shift): AO fades from 1 down to (1 - this) at the deepest painted hole.
// 0 = no darkening (no black spots), 1 = full black at occ=0.
_OcclusionThreshold("Painted Occlusion AO Shift", Range(0.0, 1.0)) = 0.2
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
_OcclusionMap("Occlusion", 2D) = "white" {}