Rework the camo shader to flatten toward darkness, drop FormSoftness
Rebuild the camo controls around hiding a painted player in a dark scene. _HalfLambert is now a two-segment curve (0 = no effect / URP Lit, 0.5 = classic half-lambert soft terminator, 1 = fully flat) and a companion _HalfLambertBrightness (-1..1) additively shifts the flattened diffuse so the body tone matches the scene. _SpecularDampen still scales down the direct highlight. The view-coupled _FormSoftness normal-bend is removed entirely (it flipped light/dark with the camera); all knobs are now term-based and view-independent, and stay byte-identical to URP/Lit at their no-op values. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
67e3696339
commit
b5bd1fdb8b
2 files changed
+46
-33
No files matched your search
@@ -1,37 +1,37 @@
|
||||
#ifndef PLAYER_CAMO_INCLUDED
|
||||
#define PLAYER_CAMO_INCLUDED
|
||||
|
||||
// Camouflage lighting for the player avatar. Goal: keep the player lit by the scene (so its
|
||||
// brightness matches the surroundings) but de-emphasise the 3D FORM, so a correctly painted
|
||||
// avatar is harder to spot. Three independent, tunable knobs - all 0 => byte-identical URP/Lit:
|
||||
// Camouflage lighting for the player avatar. Goal: keep the player painted to match its
|
||||
// surroundings while de-emphasising the 3D FORM, so a correctly painted avatar is harder to spot.
|
||||
// Knobs - _HalfLambert at 0 and _HalfLambertBrightness at 0 => byte-identical URP/Lit:
|
||||
//
|
||||
// _FormSoftness (0..1) : bends the per-pixel normal toward the view direction before shading.
|
||||
// Flattens diffuse + specular + reflections together -> the silhouette
|
||||
// shading that reveals curvature softens from every light angle.
|
||||
// _HalfLambert (0..1) : wraps the DIRECT diffuse term, saturate(N.L) -> N.L*0.5+0.5, lifting
|
||||
// the shadow side (classic half-Lambert). Specular stays clamped.
|
||||
// _SpecularDampen (0..1) : scales down the DIRECT specular highlight (a strong shape cue).
|
||||
// _HalfLambert (0..1): form-flattening diffuse over two segments -
|
||||
// 0 -> saturate(N.L) plain Lambert (no-op)
|
||||
// 0.5 -> N.L * 0.5 + 0.5 classic half-Lambert (soft terminator,
|
||||
// no hard, form-revealing shadow line)
|
||||
// 1 -> 1 fully FLAT: the direct diffuse no longer
|
||||
// reveals the 3D form. View-INDEPENDENT
|
||||
// (operates on the shading term, not a view-/light-bent normal, so
|
||||
// there is no bright/dark flip when orbiting). The DIRECT specular
|
||||
// highlight (a strong shape cue) is faded out with it, gone at 1.
|
||||
// _HalfLambertBrightness (-1..1): additive shift of the (flattened) diffuse term, default 0. Tunes
|
||||
// the body brighter/darker to match the scene tone (e.g. flatten to
|
||||
// 1, then dial this down to sink into a dark scene). Clamped at 0.
|
||||
// _SpecularDampen (0..1): scales down the DIRECT specular highlight (a strong shape cue) on
|
||||
// top of the _HalfLambert fade.
|
||||
//
|
||||
// Implementation note: URP's LightingPhysicallyBased() hard-codes saturate(N.L) inline with no
|
||||
// override hook, so a faithful half-Lambert needs the forward light loop reimplemented. This file
|
||||
// is a 1:1 copy of URP 17.3 UniversalFragmentPBR that delegates ALL the fragile parts (GI / APV /
|
||||
// shadow mask / AO / clustered light loop / final color) to URP's own helpers and only swaps the
|
||||
// per-light shading call for the camo variant below.
|
||||
// override hook, so the dark-converging half-Lambert needs the forward light loop reimplemented.
|
||||
// This file is a 1:1 copy of URP 17.3 UniversalFragmentPBR that delegates ALL the fragile parts
|
||||
// (GI / APV / shadow mask / AO / clustered light loop / final color) to URP's own helpers and only
|
||||
// swaps the per-light shading call for the camo variant below.
|
||||
//
|
||||
// Declared OUTSIDE UnityPerMaterial (LitInput.hlsl owns that CBUFFER). The player is a handful of
|
||||
// instances, so the resulting SRP-batcher break is irrelevant.
|
||||
half _FormSoftness;
|
||||
half _HalfLambert;
|
||||
half _HalfLambertBrightness;
|
||||
half _SpecularDampen;
|
||||
|
||||
// Bend the shading normal toward the camera. n and viewDirWS are unit; for a visible (front-facing)
|
||||
// fragment N.V > 0, so the lerp never collapses to zero - SafeNormalize guards the rare grazing case.
|
||||
half3 SoftenNormalCamo(half3 normalWS, half3 viewDirWS)
|
||||
{
|
||||
half3 n = lerp(normalWS, viewDirWS, _FormSoftness);
|
||||
return half3(SafeNormalize(n));
|
||||
}
|
||||
|
||||
// Per-light shading: copy of URP's LightingPhysicallyBased with the diffuse term wrapped
|
||||
// (half-Lambert) and the specular term dampened. Diffuse and specular use SEPARATE radiance so the
|
||||
// wrap lifts only the diffuse shadow side and never smears specular onto back faces.
|
||||
@@ -42,10 +42,20 @@ half3 LightingPhysicallyBasedCamo(BRDFData brdfData, BRDFData brdfDataClearCoat,
|
||||
{
|
||||
half NdotL = dot(normalWS, lightDirectionWS);
|
||||
half ndlSat = saturate(NdotL);
|
||||
half diffuseTerm = lerp(ndlSat, NdotL * 0.5h + 0.5h, _HalfLambert);
|
||||
// Form-flattening diffuse driven by _HalfLambert over two segments:
|
||||
// 0 -> saturate(N.L) plain Lambert (no-op, byte-identical URP/Lit)
|
||||
// 0.5 -> N.L * 0.5 + 0.5 classic half-Lambert (soft terminator, no hard shadow line)
|
||||
// 1 -> 1 fully flat: the direct diffuse no longer reveals the 3D form.
|
||||
// _HalfLambertBrightness then shifts the whole (flattened) term up/down to tune the body tone;
|
||||
// clamped at 0 so it never produces negative light.
|
||||
half classicHL = NdotL * 0.5h + 0.5h; // already in [0,1] for N.L in [-1,1]
|
||||
half t1 = saturate(_HalfLambert * 2.0h); // 0..1 across _HalfLambert in [0, 0.5]
|
||||
half t2 = saturate(_HalfLambert * 2.0h - 1.0h); // 0..1 across _HalfLambert in [0.5, 1]
|
||||
half diffuseTerm = lerp(lerp(ndlSat, classicHL, t1), 1.0h, t2);
|
||||
diffuseTerm = max(0.0h, diffuseTerm + _HalfLambertBrightness);
|
||||
|
||||
half3 atten = lightColor * lightAttenuation;
|
||||
half3 diffuseRadiance = atten * diffuseTerm; // wrapped (half-Lambert)
|
||||
half3 diffuseRadiance = atten * diffuseTerm; // flattened + brightness-shifted
|
||||
half3 specularRadiance = atten * ndlSat; // clamped (no back-face specular)
|
||||
|
||||
half3 color = brdfData.diffuse * diffuseRadiance;
|
||||
@@ -63,7 +73,9 @@ half3 LightingPhysicallyBasedCamo(BRDFData brdfData, BRDFData brdfDataClearCoat,
|
||||
spec = spec * (1.0 - clearCoatMask * coatFresnel) + (brdfCoat * clearCoatMask).xxx;
|
||||
#endif
|
||||
|
||||
spec *= (1.0h - _SpecularDampen);
|
||||
// Fade the bright, form-revealing highlight out with _HalfLambert (gone at 1, so no highlight
|
||||
// survives at full camo), on top of the explicit _SpecularDampen.
|
||||
spec *= (1.0h - _SpecularDampen) * (1.0h - _HalfLambert);
|
||||
color += spec * specularRadiance;
|
||||
}
|
||||
#endif
|
||||
@@ -170,8 +182,8 @@ half4 UniversalFragmentCamo(InputData inputData, SurfaceData surfaceData)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Replicates URP's LitPassFragment body and inserts the normal-soften step before GI + lighting,
|
||||
// so GI (probe) direction, direct lighting and reflections all use the flattened normal.
|
||||
// Replicates URP's LitPassFragment body. The camo flattening lives entirely in the light loop
|
||||
// (UniversalFragmentCamo), so the true normal is kept and GI/reflections stay physically correct.
|
||||
void CamoForwardFragment(
|
||||
Varyings input
|
||||
, out half4 outColor : SV_Target0
|
||||
@@ -203,8 +215,9 @@ void CamoForwardFragment(
|
||||
InputData inputData;
|
||||
InitializeInputData(input, surfaceData.normalTS, inputData);
|
||||
|
||||
// Camo: flatten the shading normal toward the camera (no-op when _FormSoftness == 0).
|
||||
inputData.normalWS = SoftenNormalCamo(inputData.normalWS, inputData.viewDirectionWS);
|
||||
// Camo: the form is de-emphasised purely inside the light loop (direct diffuse driven toward the
|
||||
// dark/shadow value + specular faded), so the true normal is kept here and GI/reflections stay
|
||||
// physically correct. No normal bending -> view-independent, no bright/dark flip when orbiting.
|
||||
|
||||
SETUP_DEBUG_TEXTURE_DATA(inputData, UNDO_TRANSFORM_TEX(input.uv, _BaseMap));
|
||||
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
// transfer 1:1, the Lit inspector keeps working, and the runtime MeshPainter (which only sets
|
||||
// _BaseColor / PBR keywords on the material instance) is unaffected. The ONLY change is the
|
||||
// ForwardLit pass: its fragment is CamoForwardFragment, which reimplements URP's forward shading
|
||||
// while delegating GI / shadows / clustered lights to URP and applying the three camo knobs. All
|
||||
// while delegating GI / shadows / clustered lights to URP and applying the camo knobs. All
|
||||
// other passes (ShadowCaster, GBuffer, DepthOnly, DepthNormals, Meta, ...) are stock URP/Lit.
|
||||
Shader "Custom/PlayerCamoLit"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
// --- Camouflage knobs (all 0 => identical to URP/Lit) ---
|
||||
// --- Camouflage knobs (both 0 => identical to URP/Lit) ---
|
||||
[Header(Camouflage)]
|
||||
_FormSoftness("Form Softness (normal -> view)", Range(0.0, 1.0)) = 0.4
|
||||
_HalfLambert("Half Lambert (lift shadow side)", Range(0.0, 1.0)) = 0.5
|
||||
_HalfLambert("Half Lambert (0 none, 0.5 classic, 1 flat)", Range(0.0, 1.0)) = 0.5
|
||||
_HalfLambertBrightness("Half Lambert Brightness Shift", Range(-1.0, 1.0)) = 0.0
|
||||
_SpecularDampen("Specular Dampen", Range(0.0, 1.0)) = 0.5
|
||||
|
||||
// Specular vs Metallic workflow
|
||||
|
||||
Reference in new issue
Block a user