Camo: generalize brightness + add persistent saturation
Move brightness out of the half-lambert diffuse term to a final exposure multiply on the whole shaded body color (diffuse + GI + specular), and add a saturation grade (0 = neutral, -1 = grayscale, >0 = boosted) on the same final stage. Saturation rides as a 4th component on CamoKnobs (Vector3 -> Vector4) so it inherits all three persistence paths: HUD JSON, network replication (_camo NetworkVariable), and painting-slot metadata. Wires the renamed Slider_Brightness and new Slider_Saturation. Bumps HudFileVersion and PaintMetaVersion with load-time remaps so existing paintings keep their look (old 1=neutral saturation remaps to 0=neutral). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
45b7de05e4
commit
50781e32d2
7 files changed
+87
-36
No files matched your search
@@ -30,7 +30,8 @@ public partial class MeshPainter
|
|||||||
// is), so saves without a fur.png load fine - the fur map stays seeded (bald). NOTE: the fur.png layout
|
// is), so saves without a fur.png load fine - the fur map stays seeded (bald). NOTE: the fur.png layout
|
||||||
// changed to R = length (was RGB colour + A length); pre-change fur.png files load stale length data and
|
// changed to R = length (was RGB colour + A length); pre-change fur.png files load stale length data and
|
||||||
// should be repainted.
|
// should be repainted.
|
||||||
private const int PaintMetaVersion = 6;
|
// v8: camoSaturation semantics changed to an offset (0 = neutral, -1 = greyscale; was 1 = neutral in v7).
|
||||||
|
private const int PaintMetaVersion = 8;
|
||||||
|
|
||||||
private const string MetaFileName = "painting.json";
|
private const string MetaFileName = "painting.json";
|
||||||
private const string AlbedoFileName = "albedo.png";
|
private const string AlbedoFileName = "albedo.png";
|
||||||
@@ -57,6 +58,7 @@ public partial class MeshPainter
|
|||||||
public float camoHalfLambert;
|
public float camoHalfLambert;
|
||||||
public float camoHalfLambertBrightness;
|
public float camoHalfLambertBrightness;
|
||||||
public float camoSpecularDampen;
|
public float camoSpecularDampen;
|
||||||
|
public float camoSaturation; // v7+; remapped on load per meta.version (semantics changed in v8)
|
||||||
public float occlusionThreshold;
|
public float occlusionThreshold;
|
||||||
public float heightNormalIntensity;
|
public float heightNormalIntensity;
|
||||||
public float parallaxIntensity;
|
public float parallaxIntensity;
|
||||||
@@ -149,6 +151,7 @@ public partial class MeshPainter
|
|||||||
camoHalfLambert = CamoHalfLambert,
|
camoHalfLambert = CamoHalfLambert,
|
||||||
camoHalfLambertBrightness = CamoHalfLambertBrightness,
|
camoHalfLambertBrightness = CamoHalfLambertBrightness,
|
||||||
camoSpecularDampen = CamoSpecularDampen,
|
camoSpecularDampen = CamoSpecularDampen,
|
||||||
|
camoSaturation = CamoSaturation,
|
||||||
occlusionThreshold = OcclusionThreshold,
|
occlusionThreshold = OcclusionThreshold,
|
||||||
heightNormalIntensity = HeightNormalIntensity,
|
heightNormalIntensity = HeightNormalIntensity,
|
||||||
parallaxIntensity = ParallaxIntensity,
|
parallaxIntensity = ParallaxIntensity,
|
||||||
@@ -200,6 +203,12 @@ public partial class MeshPainter
|
|||||||
CamoHalfLambert = meta.camoHalfLambert;
|
CamoHalfLambert = meta.camoHalfLambert;
|
||||||
CamoHalfLambertBrightness = meta.camoHalfLambertBrightness;
|
CamoHalfLambertBrightness = meta.camoHalfLambertBrightness;
|
||||||
CamoSpecularDampen = meta.camoSpecularDampen;
|
CamoSpecularDampen = meta.camoSpecularDampen;
|
||||||
|
// Saturation semantics changed in v8 (0 = neutral, -1 = greyscale; was 1 = neutral in v7).
|
||||||
|
// v7 stored the old scheme -> shift by -1 to preserve its look. Pre-v7 has no field (deserializes
|
||||||
|
// to 0), which is already neutral under the new scheme.
|
||||||
|
CamoSaturation = meta.version >= 8 ? meta.camoSaturation
|
||||||
|
: meta.version == 7 ? meta.camoSaturation - 1f
|
||||||
|
: 0f;
|
||||||
OcclusionThreshold = meta.occlusionThreshold;
|
OcclusionThreshold = meta.occlusionThreshold;
|
||||||
HeightNormalIntensity = meta.heightNormalIntensity;
|
HeightNormalIntensity = meta.heightNormalIntensity;
|
||||||
ParallaxIntensity = meta.parallaxIntensity;
|
ParallaxIntensity = meta.parallaxIntensity;
|
||||||
|
|||||||
@@ -78,6 +78,16 @@ public partial class MeshPainter : MonoBehaviour
|
|||||||
set => SetMatFloat(SpecularDampenId, value);
|
set => SetMatFloat(SpecularDampenId, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>General colour saturation of the shaded body (0 = neutral, -1 = greyscale, >0 = punchier).
|
||||||
|
/// A camo body-tone knob alongside <see cref="CamoHalfLambertBrightness"/>; drives the material's
|
||||||
|
/// <c>_Saturation</c> (offset: shader lerps to grey by 1 + value). Persisted + replicated with the
|
||||||
|
/// other camo knobs (see <see cref="CamoKnobs"/>).</summary>
|
||||||
|
public float CamoSaturation
|
||||||
|
{
|
||||||
|
get => GetMatFloat(SaturationId, 0f);
|
||||||
|
set => SetMatFloat(SaturationId, value);
|
||||||
|
}
|
||||||
|
|
||||||
// Painted-height look knobs on the same material instance, surfaced/guarded/persisted/replicated
|
// Painted-height look knobs on the same material instance, surfaced/guarded/persisted/replicated
|
||||||
// like the camo knobs above. Defaults match the shader so the unpainted look is unchanged.
|
// like the camo knobs above. Defaults match the shader so the unpainted look is unchanged.
|
||||||
|
|
||||||
@@ -104,11 +114,12 @@ public partial class MeshPainter : MonoBehaviour
|
|||||||
set => SetMatFloat(ParallaxId, Mathf.Max(0f, value) * ParallaxBase);
|
set => SetMatFloat(ParallaxId, Mathf.Max(0f, value) * ParallaxBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>The three camo knobs as one vector (x = half-Lambert, y = its brightness, z = specular dampen).</summary>
|
/// <summary>The four camo knobs as one vector (x = half-Lambert, y = brightness, z = specular dampen,
|
||||||
public Vector3 CamoKnobs
|
/// w = saturation) - one payload for persistence + network replication.</summary>
|
||||||
|
public Vector4 CamoKnobs
|
||||||
{
|
{
|
||||||
get => new(CamoHalfLambert, CamoHalfLambertBrightness, CamoSpecularDampen);
|
get => new(CamoHalfLambert, CamoHalfLambertBrightness, CamoSpecularDampen, CamoSaturation);
|
||||||
set { CamoHalfLambert = value.x; CamoHalfLambertBrightness = value.y; CamoSpecularDampen = value.z; }
|
set { CamoHalfLambert = value.x; CamoHalfLambertBrightness = value.y; CamoSpecularDampen = value.z; CamoSaturation = value.w; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>The three painted-height knobs as one vector (x = occlusion threshold, y = relief normal intensity, z = parallax intensity).</summary>
|
/// <summary>The three painted-height knobs as one vector (x = occlusion threshold, y = relief normal intensity, z = parallax intensity).</summary>
|
||||||
@@ -454,6 +465,7 @@ public partial class MeshPainter : MonoBehaviour
|
|||||||
private const float ParallaxBase = 0.02f;
|
private const float ParallaxBase = 0.02f;
|
||||||
private static readonly int HalfLambertId = Shader.PropertyToID("_HalfLambert");
|
private static readonly int HalfLambertId = Shader.PropertyToID("_HalfLambert");
|
||||||
private static readonly int HalfLambertBrightnessId = Shader.PropertyToID("_HalfLambertBrightness");
|
private static readonly int HalfLambertBrightnessId = Shader.PropertyToID("_HalfLambertBrightness");
|
||||||
|
private static readonly int SaturationId = Shader.PropertyToID("_Saturation");
|
||||||
private static readonly int SpecularDampenId = Shader.PropertyToID("_SpecularDampen");
|
private static readonly int SpecularDampenId = Shader.PropertyToID("_SpecularDampen");
|
||||||
private static readonly int XRayColorId = Shader.PropertyToID("_XRayColor");
|
private static readonly int XRayColorId = Shader.PropertyToID("_XRayColor");
|
||||||
private static readonly int XRayPulseSpeedId = Shader.PropertyToID("_XRayPulseSpeed");
|
private static readonly int XRayPulseSpeedId = Shader.PropertyToID("_XRayPulseSpeed");
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ public partial class PaintHud
|
|||||||
"defaults every session.")]
|
"defaults every session.")]
|
||||||
public bool persistHud = true;
|
public bool persistHud = true;
|
||||||
|
|
||||||
// v2: HudValues gained fur (toggle) + furLength. Bumping discards pre-v2 saves so the new fur fields
|
// v2: HudValues gained fur (toggle) + furLength. v3: gained camoSaturation. v4: camoSaturation
|
||||||
// fall back to the painter's prefab defaults instead of deserializing as 0/false.
|
// semantics changed to an offset (0 = neutral, was 1). Bumping discards older saves so the fields
|
||||||
private const int HudFileVersion = 2;
|
// fall back to the painter's prefab/material defaults instead of stale/old-scheme values (a v3 save
|
||||||
|
// stored saturation 1 = old-neutral, which would read as punchier now).
|
||||||
|
private const int HudFileVersion = 4;
|
||||||
|
|
||||||
// Auto-save bookkeeping. Brush edits are debounced (sliders fire every frame while dragged): the due
|
// Auto-save bookkeeping. Brush edits are debounced (sliders fire every frame while dragged): the due
|
||||||
// time is pushed forward and written once they settle.
|
// time is pushed forward and written once they settle.
|
||||||
@@ -41,7 +43,7 @@ public partial class PaintHud
|
|||||||
// Camo knobs live on the material instance, which only exists after the painter's Start: a restored
|
// Camo knobs live on the material instance, which only exists after the painter's Start: a restored
|
||||||
// (disk) or carried (respawn) value waits here and is applied in the LateUpdate ready-gate.
|
// (disk) or carried (respawn) value waits here and is applied in the LateUpdate ready-gate.
|
||||||
// _camoEverReady gates carry/save so the not-yet-ready 0 never overwrites the authored value.
|
// _camoEverReady gates carry/save so the not-yet-ready 0 never overwrites the authored value.
|
||||||
private Vector3 _pendingCamo;
|
private Vector4 _pendingCamo; // (x,y,z,w) = CamoKnobs (halfLambert, brightness, specularDampen, saturation)
|
||||||
private bool _hasPendingCamo;
|
private bool _hasPendingCamo;
|
||||||
private bool _camoEverReady;
|
private bool _camoEverReady;
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ public partial class PaintHud
|
|||||||
public int activeBrushIndex;
|
public int activeBrushIndex;
|
||||||
public int sprayDots;
|
public int sprayDots;
|
||||||
public float sprayDotSizeMin, sprayDotSizeMax, sprayDotAlphaMin, sprayDotAlphaMax;
|
public float sprayDotSizeMin, sprayDotSizeMax, sprayDotAlphaMin, sprayDotAlphaMax;
|
||||||
public float camoHalfLambert, camoHalfLambertBrightness, camoSpecularDampen; // PlayerCamoLit material knobs
|
public float camoHalfLambert, camoHalfLambertBrightness, camoSpecularDampen, camoSaturation; // PlayerCamoLit material knobs
|
||||||
public float occlusionThreshold, heightNormalIntensity, parallaxIntensity; // painted-height knobs (material-backed)
|
public float occlusionThreshold, heightNormalIntensity, parallaxIntensity; // painted-height knobs (material-backed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,9 +172,10 @@ public partial class PaintHud
|
|||||||
// save files (applying their 0 would clobber the authored camo).
|
// save files (applying their 0 would clobber the authored camo).
|
||||||
if (state.hasCamoValues && state.values != null)
|
if (state.hasCamoValues && state.values != null)
|
||||||
{
|
{
|
||||||
_pendingCamo = new Vector3(state.values.camoHalfLambert,
|
_pendingCamo = new Vector4(state.values.camoHalfLambert,
|
||||||
state.values.camoHalfLambertBrightness,
|
state.values.camoHalfLambertBrightness,
|
||||||
state.values.camoSpecularDampen);
|
state.values.camoSpecularDampen,
|
||||||
|
state.values.camoSaturation);
|
||||||
_hasPendingCamo = true;
|
_hasPendingCamo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +231,7 @@ public partial class PaintHud
|
|||||||
_savedValues.camoHalfLambert = _lastSeenValues.camoHalfLambert = painter.CamoHalfLambert;
|
_savedValues.camoHalfLambert = _lastSeenValues.camoHalfLambert = painter.CamoHalfLambert;
|
||||||
_savedValues.camoHalfLambertBrightness = _lastSeenValues.camoHalfLambertBrightness = painter.CamoHalfLambertBrightness;
|
_savedValues.camoHalfLambertBrightness = _lastSeenValues.camoHalfLambertBrightness = painter.CamoHalfLambertBrightness;
|
||||||
_savedValues.camoSpecularDampen = _lastSeenValues.camoSpecularDampen = painter.CamoSpecularDampen;
|
_savedValues.camoSpecularDampen = _lastSeenValues.camoSpecularDampen = painter.CamoSpecularDampen;
|
||||||
|
_savedValues.camoSaturation = _lastSeenValues.camoSaturation = painter.CamoSaturation;
|
||||||
_savedValues.occlusionThreshold = _lastSeenValues.occlusionThreshold = painter.OcclusionThreshold;
|
_savedValues.occlusionThreshold = _lastSeenValues.occlusionThreshold = painter.OcclusionThreshold;
|
||||||
_savedValues.heightNormalIntensity = _lastSeenValues.heightNormalIntensity = painter.HeightNormalIntensity;
|
_savedValues.heightNormalIntensity = _lastSeenValues.heightNormalIntensity = painter.HeightNormalIntensity;
|
||||||
_savedValues.parallaxIntensity = _lastSeenValues.parallaxIntensity = painter.ParallaxIntensity;
|
_savedValues.parallaxIntensity = _lastSeenValues.parallaxIntensity = painter.ParallaxIntensity;
|
||||||
@@ -298,6 +302,7 @@ public partial class PaintHud
|
|||||||
&& p.CamoHalfLambert == v.camoHalfLambert
|
&& p.CamoHalfLambert == v.camoHalfLambert
|
||||||
&& p.CamoHalfLambertBrightness == v.camoHalfLambertBrightness
|
&& p.CamoHalfLambertBrightness == v.camoHalfLambertBrightness
|
||||||
&& p.CamoSpecularDampen == v.camoSpecularDampen
|
&& p.CamoSpecularDampen == v.camoSpecularDampen
|
||||||
|
&& p.CamoSaturation == v.camoSaturation
|
||||||
&& p.OcclusionThreshold == v.occlusionThreshold
|
&& p.OcclusionThreshold == v.occlusionThreshold
|
||||||
&& p.HeightNormalIntensity == v.heightNormalIntensity
|
&& p.HeightNormalIntensity == v.heightNormalIntensity
|
||||||
&& p.ParallaxIntensity == v.parallaxIntensity;
|
&& p.ParallaxIntensity == v.parallaxIntensity;
|
||||||
@@ -322,9 +327,10 @@ public partial class PaintHud
|
|||||||
// by _camoEverReady so a carry taken before any material was ready can't apply a stale 0.
|
// by _camoEverReady so a carry taken before any material was ready can't apply a stale 0.
|
||||||
if (_camoEverReady)
|
if (_camoEverReady)
|
||||||
{
|
{
|
||||||
_pendingCamo = new Vector3(_carry.camoHalfLambert,
|
_pendingCamo = new Vector4(_carry.camoHalfLambert,
|
||||||
_carry.camoHalfLambertBrightness,
|
_carry.camoHalfLambertBrightness,
|
||||||
_carry.camoSpecularDampen);
|
_carry.camoSpecularDampen,
|
||||||
|
_carry.camoSaturation);
|
||||||
_hasPendingCamo = true;
|
_hasPendingCamo = true;
|
||||||
_pendingHeight = new Vector3(_carry.occlusionThreshold,
|
_pendingHeight = new Vector3(_carry.occlusionThreshold,
|
||||||
_carry.heightNormalIntensity,
|
_carry.heightNormalIntensity,
|
||||||
@@ -365,6 +371,7 @@ public partial class PaintHud
|
|||||||
v.camoHalfLambert = from.CamoHalfLambert;
|
v.camoHalfLambert = from.CamoHalfLambert;
|
||||||
v.camoHalfLambertBrightness = from.CamoHalfLambertBrightness;
|
v.camoHalfLambertBrightness = from.CamoHalfLambertBrightness;
|
||||||
v.camoSpecularDampen = from.CamoSpecularDampen;
|
v.camoSpecularDampen = from.CamoSpecularDampen;
|
||||||
|
v.camoSaturation = from.CamoSaturation;
|
||||||
v.occlusionThreshold = from.OcclusionThreshold;
|
v.occlusionThreshold = from.OcclusionThreshold;
|
||||||
v.heightNormalIntensity = from.HeightNormalIntensity;
|
v.heightNormalIntensity = from.HeightNormalIntensity;
|
||||||
v.parallaxIntensity = from.ParallaxIntensity;
|
v.parallaxIntensity = from.ParallaxIntensity;
|
||||||
@@ -401,6 +408,7 @@ public partial class PaintHud
|
|||||||
to.CamoHalfLambert = v.camoHalfLambert;
|
to.CamoHalfLambert = v.camoHalfLambert;
|
||||||
to.CamoHalfLambertBrightness = v.camoHalfLambertBrightness;
|
to.CamoHalfLambertBrightness = v.camoHalfLambertBrightness;
|
||||||
to.CamoSpecularDampen = v.camoSpecularDampen;
|
to.CamoSpecularDampen = v.camoSpecularDampen;
|
||||||
|
to.CamoSaturation = v.camoSaturation;
|
||||||
to.OcclusionThreshold = v.occlusionThreshold;
|
to.OcclusionThreshold = v.occlusionThreshold;
|
||||||
to.HeightNormalIntensity = v.heightNormalIntensity;
|
to.HeightNormalIntensity = v.heightNormalIntensity;
|
||||||
to.ParallaxIntensity = v.parallaxIntensity;
|
to.ParallaxIntensity = v.parallaxIntensity;
|
||||||
@@ -459,6 +467,7 @@ public partial class PaintHud
|
|||||||
_defaults.camoHalfLambert = painter.CamoHalfLambert;
|
_defaults.camoHalfLambert = painter.CamoHalfLambert;
|
||||||
_defaults.camoHalfLambertBrightness = painter.CamoHalfLambertBrightness;
|
_defaults.camoHalfLambertBrightness = painter.CamoHalfLambertBrightness;
|
||||||
_defaults.camoSpecularDampen = painter.CamoSpecularDampen;
|
_defaults.camoSpecularDampen = painter.CamoSpecularDampen;
|
||||||
|
_defaults.camoSaturation = painter.CamoSaturation;
|
||||||
_defaults.occlusionThreshold = painter.OcclusionThreshold;
|
_defaults.occlusionThreshold = painter.OcclusionThreshold;
|
||||||
_defaults.heightNormalIntensity = painter.HeightNormalIntensity;
|
_defaults.heightNormalIntensity = painter.HeightNormalIntensity;
|
||||||
_defaults.parallaxIntensity = painter.ParallaxIntensity;
|
_defaults.parallaxIntensity = painter.ParallaxIntensity;
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ public partial class PaintHud : MonoBehaviour
|
|||||||
private const string SliderSprayDotAlphaMin = "Slider_SprayDotAlphaMin";
|
private const string SliderSprayDotAlphaMin = "Slider_SprayDotAlphaMin";
|
||||||
private const string SliderSprayDotAlphaMax = "Slider_SprayDotAlphaMax";
|
private const string SliderSprayDotAlphaMax = "Slider_SprayDotAlphaMax";
|
||||||
private const string SliderHalfLambert = "Slider_HalfLambert";
|
private const string SliderHalfLambert = "Slider_HalfLambert";
|
||||||
private const string SliderHalfLambertBrightness = "Slider_HalfLambertBrightness";
|
private const string SliderBrightness = "Slider_Brightness";
|
||||||
|
private const string SliderSaturation = "Slider_Saturation";
|
||||||
private const string SliderSpecularDamp = "Slider_SpecularDamp";
|
private const string SliderSpecularDamp = "Slider_SpecularDamp";
|
||||||
private const string SliderOcclusionThreshold = "Slider_OcclusionThreshold";
|
private const string SliderOcclusionThreshold = "Slider_OcclusionThreshold";
|
||||||
private const string SliderNormalIntensity = "Slider_NormalIntensity";
|
private const string SliderNormalIntensity = "Slider_NormalIntensity";
|
||||||
@@ -253,7 +254,8 @@ public partial class PaintHud : MonoBehaviour
|
|||||||
// Camo knobs are material-backed and not readable yet (the HUD binds before the painter's Start);
|
// Camo knobs are material-backed and not readable yet (the HUD binds before the painter's Start);
|
||||||
// SyncCamoSliders re-seeds them once IsReady. Setting a slider before then is a safe no-op.
|
// SyncCamoSliders re-seeds them once IsReady. Setting a slider before then is a safe no-op.
|
||||||
WireSlider(panel, SliderHalfLambert, painter ? painter.CamoHalfLambert : 0f, v => { if (painter) painter.CamoHalfLambert = v; });
|
WireSlider(panel, SliderHalfLambert, painter ? painter.CamoHalfLambert : 0f, v => { if (painter) painter.CamoHalfLambert = v; });
|
||||||
WireSlider(panel, SliderHalfLambertBrightness, painter ? painter.CamoHalfLambertBrightness : 0f, v => { if (painter) painter.CamoHalfLambertBrightness = v; });
|
WireSlider(panel, SliderBrightness, painter ? painter.CamoHalfLambertBrightness : 0f, v => { if (painter) painter.CamoHalfLambertBrightness = v; });
|
||||||
|
WireSlider(panel, SliderSaturation, painter ? painter.CamoSaturation : 0f, v => { if (painter) painter.CamoSaturation = v; });
|
||||||
WireSlider(panel, SliderSpecularDamp, painter ? painter.CamoSpecularDampen : 0f, v => { if (painter) painter.CamoSpecularDampen = v; });
|
WireSlider(panel, SliderSpecularDamp, painter ? painter.CamoSpecularDampen : 0f, v => { if (painter) painter.CamoSpecularDampen = v; });
|
||||||
|
|
||||||
// Painted-height knobs: material-backed like the camo sliders above.
|
// Painted-height knobs: material-backed like the camo sliders above.
|
||||||
@@ -333,7 +335,8 @@ public partial class PaintHud : MonoBehaviour
|
|||||||
if (!_panel || !painter) return;
|
if (!_panel || !painter) return;
|
||||||
var panel = _panel.transform;
|
var panel = _panel.transform;
|
||||||
SetSlider(panel, SliderHalfLambert, painter.CamoHalfLambert);
|
SetSlider(panel, SliderHalfLambert, painter.CamoHalfLambert);
|
||||||
SetSlider(panel, SliderHalfLambertBrightness, painter.CamoHalfLambertBrightness);
|
SetSlider(panel, SliderBrightness, painter.CamoHalfLambertBrightness);
|
||||||
|
SetSlider(panel, SliderSaturation, painter.CamoSaturation);
|
||||||
SetSlider(panel, SliderSpecularDamp, painter.CamoSpecularDampen);
|
SetSlider(panel, SliderSpecularDamp, painter.CamoSpecularDampen);
|
||||||
// Painted-height knobs share the same material-ready timing as the camo sliders.
|
// Painted-height knobs share the same material-ready timing as the camo sliders.
|
||||||
SetSlider(panel, SliderOcclusionThreshold, painter.OcclusionThreshold);
|
SetSlider(panel, SliderOcclusionThreshold, painter.OcclusionThreshold);
|
||||||
|
|||||||
@@ -69,12 +69,12 @@ public class PlayerPaintSync : NetworkBehaviour
|
|||||||
private readonly NetworkVariable<float> _emissionIntensity =
|
private readonly NetworkVariable<float> _emissionIntensity =
|
||||||
new(1f, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
new(1f, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||||
|
|
||||||
// Per-player camo knobs on the PlayerCamoLit material (x = HalfLambert, y = its brightness,
|
// Per-player camo knobs on the PlayerCamoLit material (x = HalfLambert, y = brightness,
|
||||||
// z = specular dampen) - material values like emission intensity, applied once the remote painter
|
// z = specular dampen, w = saturation) - material values like emission intensity, applied once the
|
||||||
// is ready and on every change. The default mirrors Player.mat's authored camo so the brief
|
// remote painter is ready and on every change. The default mirrors Player.mat's authored camo (with
|
||||||
// pre-publish spawn frame shows no flash.
|
// neutral saturation 0) so the brief pre-publish spawn frame shows no flash.
|
||||||
private readonly NetworkVariable<Vector3> _camo =
|
private readonly NetworkVariable<Vector4> _camo =
|
||||||
new(new Vector3(0.5f, 0f, 0.1f), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
new(new Vector4(0.5f, 0f, 0.1f, 0f), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||||
private bool _camoApplyPending; // remote: apply _camo.Value to the material once IsReady / on change
|
private bool _camoApplyPending; // remote: apply _camo.Value to the material once IsReady / on change
|
||||||
|
|
||||||
// Painted-height knobs (x = occlusion AO threshold, y = relief normal intensity, z = parallax
|
// Painted-height knobs (x = occlusion AO threshold, y = relief normal intensity, z = parallax
|
||||||
@@ -246,7 +246,7 @@ public class PlayerPaintSync : NetworkBehaviour
|
|||||||
|
|
||||||
// Apply on the next LateUpdate once the material is ready (the camo setters are a no-op before that,
|
// Apply on the next LateUpdate once the material is ready (the camo setters are a no-op before that,
|
||||||
// and on a fresh spawn the OnValueChanged may arrive before the painter's Start has built the instance).
|
// and on a fresh spawn the OnValueChanged may arrive before the painter's Start has built the instance).
|
||||||
private void OnCamoChanged(Vector3 previous, Vector3 current) => _camoApplyPending = true;
|
private void OnCamoChanged(Vector4 previous, Vector4 current) => _camoApplyPending = true;
|
||||||
private void OnHeightChanged(Vector3 previous, Vector3 current) => _heightApplyPending = true;
|
private void OnHeightChanged(Vector3 previous, Vector3 current) => _heightApplyPending = true;
|
||||||
|
|
||||||
// ---- Live stamp stream (owner -> server -> remotes) ----
|
// ---- Live stamp stream (owner -> server -> remotes) ----
|
||||||
@@ -315,7 +315,7 @@ public class PlayerPaintSync : NetworkBehaviour
|
|||||||
// published (it would briefly flatten remotes' lighting until the real value lands).
|
// published (it would briefly flatten remotes' lighting until the real value lands).
|
||||||
if (_painter && _painter.IsReady)
|
if (_painter && _painter.IsReady)
|
||||||
{
|
{
|
||||||
Vector3 camo = _painter.CamoKnobs;
|
Vector4 camo = _painter.CamoKnobs;
|
||||||
if (_camo.Value != camo)
|
if (_camo.Value != camo)
|
||||||
_camo.Value = camo;
|
_camo.Value = camo;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
// Camouflage lighting for the player avatar. Goal: keep the player painted to match its
|
// 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.
|
// 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:
|
// Knobs - _HalfLambert 0, _HalfLambertBrightness 0 and _Saturation 0 => byte-identical URP/Lit:
|
||||||
//
|
//
|
||||||
// _HalfLambert (0..1): form-flattening diffuse over two segments -
|
// _HalfLambert (0..1): form-flattening diffuse over two segments -
|
||||||
// 0 -> saturate(N.L) plain Lambert (no-op)
|
// 0 -> saturate(N.L) plain Lambert (no-op)
|
||||||
@@ -14,9 +14,16 @@
|
|||||||
// (operates on the shading term, not a view-/light-bent normal, so
|
// (operates on the shading term, not a view-/light-bent normal, so
|
||||||
// there is no bright/dark flip when orbiting). The DIRECT specular
|
// there is no bright/dark flip when orbiting). The DIRECT specular
|
||||||
// highlight (a strong shape cue) is faded out with it, gone at 1.
|
// 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
|
// _HalfLambertBrightness (-1..1): GENERAL brightness (exposure) on the WHOLE shaded body - diffuse,
|
||||||
// the body brighter/darker to match the scene tone (e.g. flatten to
|
// ambient/GI AND specular - as a final multiply (1 + value), default
|
||||||
// 1, then dial this down to sink into a dark scene). Clamped at 0.
|
// 0 = neutral. NO LONGER tied to the half-Lambert diffuse term (the
|
||||||
|
// "HalfLambert" in the name is legacy): dial down to sink the body
|
||||||
|
// into a dark scene, up to lift it into a bright one. Applied in
|
||||||
|
// CamoForwardFragment, before fog and the X-ray glow.
|
||||||
|
// _Saturation (-1..1): colour saturation of the shaded body, default 0 = neutral. -1 =
|
||||||
|
// greyscale, <0 = muted (blends into muted scenes), >0 = punchier.
|
||||||
|
// Luma-preserving lerp(grey, colour, 1 + _Saturation) toward Rec.709
|
||||||
|
// grey, same final stage as brightness.
|
||||||
// _SpecularDampen (0..1): scales down the DIRECT specular highlight (a strong shape cue) on
|
// _SpecularDampen (0..1): scales down the DIRECT specular highlight (a strong shape cue) on
|
||||||
// top of the _HalfLambert fade.
|
// top of the _HalfLambert fade.
|
||||||
//
|
//
|
||||||
@@ -29,7 +36,8 @@
|
|||||||
// Declared OUTSIDE UnityPerMaterial (LitInput.hlsl owns that CBUFFER). The player is a handful of
|
// Declared OUTSIDE UnityPerMaterial (LitInput.hlsl owns that CBUFFER). The player is a handful of
|
||||||
// instances, so the resulting SRP-batcher break is irrelevant.
|
// instances, so the resulting SRP-batcher break is irrelevant.
|
||||||
half _HalfLambert;
|
half _HalfLambert;
|
||||||
half _HalfLambertBrightness;
|
half _HalfLambertBrightness; // legacy name: now a GENERAL brightness/exposure (CamoForwardFragment), not a half-Lambert-only shift
|
||||||
|
half _Saturation;
|
||||||
half _SpecularDampen;
|
half _SpecularDampen;
|
||||||
|
|
||||||
// --- Painted height (from the occlusion channel) ---
|
// --- Painted height (from the occlusion channel) ---
|
||||||
@@ -103,13 +111,11 @@ half3 LightingPhysicallyBasedCamo(BRDFData brdfData, BRDFData brdfDataClearCoat,
|
|||||||
// 0 -> saturate(N.L) plain Lambert (no-op, byte-identical URP/Lit)
|
// 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)
|
// 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.
|
// 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;
|
// (Brightness is NOT applied here anymore - it is a general final-color exposure in CamoForwardFragment.)
|
||||||
// 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 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 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 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);
|
half diffuseTerm = lerp(lerp(ndlSat, classicHL, t1), 1.0h, t2); // in [0,1], no negative light
|
||||||
diffuseTerm = max(0.0h, diffuseTerm + _HalfLambertBrightness);
|
|
||||||
|
|
||||||
half3 atten = lightColor * lightAttenuation;
|
half3 atten = lightColor * lightAttenuation;
|
||||||
half3 diffuseRadiance = atten * diffuseTerm; // flattened + brightness-shifted
|
half3 diffuseRadiance = atten * diffuseTerm; // flattened + brightness-shifted
|
||||||
@@ -291,6 +297,16 @@ void CamoForwardFragment(
|
|||||||
InitializeBakedGIData(input, inputData);
|
InitializeBakedGIData(input, inputData);
|
||||||
|
|
||||||
half4 color = UniversalFragmentCamo(inputData, surfaceData);
|
half4 color = UniversalFragmentCamo(inputData, surfaceData);
|
||||||
|
|
||||||
|
// Camo body-tone grade on the WHOLE shaded body (direct + ambient/GI + specular): a GENERAL
|
||||||
|
// brightness/exposure and a saturation control - camo tools to sink/lift the body tone and to
|
||||||
|
// mute/punch its colour to match the scene. Applied before fog (part of the body look) and before
|
||||||
|
// the X-ray reveal glow below, so the reveal keeps its authored intensity. Neutral at brightness 0
|
||||||
|
// / saturation 0 (x1 multiply, no-op lerp) => byte-identical to stock when the camo knobs are off.
|
||||||
|
color.rgb *= 1.0h + _HalfLambertBrightness;
|
||||||
|
half camoLuma = dot(color.rgb, half3(0.2126h, 0.7152h, 0.0722h)); // Rec.709 luma
|
||||||
|
color.rgb = lerp(camoLuma.xxx, color.rgb, 1.0h + _Saturation);
|
||||||
|
|
||||||
color.rgb = MixFog(color.rgb, inputData.fogCoord);
|
color.rgb = MixFog(color.rgb, inputData.fogCoord);
|
||||||
|
|
||||||
// X-Ray reveal glow on the VISIBLE parts. URP draws the SRPDefaultUnlit XRayOverlay pass BEFORE
|
// X-Ray reveal glow on the VISIBLE parts. URP draws the SRPDefaultUnlit XRayOverlay pass BEFORE
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ Shader "Custom/PlayerCamoLit"
|
|||||||
{
|
{
|
||||||
Properties
|
Properties
|
||||||
{
|
{
|
||||||
// --- Camouflage knobs (both 0 => identical to URP/Lit) ---
|
// --- Camouflage knobs (HalfLambert 0, Brightness 0, Saturation 0 => identical to URP/Lit) ---
|
||||||
[Header(Camouflage)]
|
[Header(Camouflage)]
|
||||||
_HalfLambert("Half Lambert (0 none, 0.5 classic, 1 flat)", 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
|
// Legacy property name; now a GENERAL brightness/exposure on the whole shaded body (see PlayerCamo.hlsl).
|
||||||
|
_HalfLambertBrightness("Brightness (general exposure, 0 = neutral)", Range(-1.0, 1.0)) = 0.0
|
||||||
|
_Saturation("Saturation (0 = neutral, -1 = greyscale, >0 = punchier)", Range(-1.0, 1.0)) = 0.0
|
||||||
_SpecularDampen("Specular Dampen", Range(0.0, 1.0)) = 0.5
|
_SpecularDampen("Specular Dampen", Range(0.0, 1.0)) = 0.5
|
||||||
|
|
||||||
// Specular vs Metallic workflow
|
// Specular vs Metallic workflow
|
||||||
|
|||||||
Reference in new issue
Block a user