FurShell: per-pixel z-order between fur objects (ZWrite On + coverage clip)

Transparent queue sorted fur draws per object, so overlapping fur
renderers swapped in front of each other frame to frame. FurForward
now writes depth; new Depth Sort Clip tunable discards low-coverage
fragments before they write depth (avoids halo punch-through).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Fable 5 committed 2026-07-11 14:30:47 +02:00
1 parent 38b9369051
commit 874ddccb4f
2 files changed
+30 -6

No files matched your search

@@ -65,6 +65,11 @@ namespace Chameleon.Components
[SerializeField, Range(0.01f, 1f)] float _furSoftness = 0.5f;
// Per-strand edge anti-aliasing (soft strand silhouette + tip instead of hard clipped discs).
[SerializeField, Range(0f, 1f)] float _strandSoftness = 0.3f;
// Fragments at/below this coverage are discarded so they don't write depth (the shader has
// ZWrite ON to get a correct per-pixel z-order between overlapping fur objects - transparents
// otherwise sort per object and pop). Higher = harder strand edges, less halo where two fur
// objects overlap.
[SerializeField, Range(0f, 1f)] float _depthSortClip = 0.3f;
// Opaque base shell. On for the standalone prototype (full coverage); turn OFF on the painted
// avatar so the camo body shows between strands instead of being sealed under a solid root.
[SerializeField] bool _solidRoot = true;
@@ -108,6 +113,7 @@ namespace Chameleon.Components
static readonly int FurThresholdId = Shader.PropertyToID("_FurThreshold");
static readonly int FurSoftnessId = Shader.PropertyToID("_FurSoftness");
static readonly int StrandSoftnessId = Shader.PropertyToID("_StrandSoftness");
static readonly int DepthSortClipId = Shader.PropertyToID("_DepthSortClip");
static readonly int SolidRootId = Shader.PropertyToID("_SolidRoot");
static readonly int WindStrengthId = Shader.PropertyToID("_WindStrength");
static readonly int WindSpeedId = Shader.PropertyToID("_WindSpeed");
@@ -246,6 +252,7 @@ namespace Chameleon.Components
_mpb.SetFloat(FurThresholdId, _furThreshold);
_mpb.SetFloat(FurSoftnessId, _furSoftness);
_mpb.SetFloat(StrandSoftnessId, _strandSoftness);
_mpb.SetFloat(DepthSortClipId, _depthSortClip);
_mpb.SetFloat(SolidRootId, _solidRoot ? 1f : 0f);
_mpb.SetFloat(WindStrengthId, _windStrength);
@@ -306,6 +313,7 @@ namespace Chameleon.Components
if (mat.HasProperty(FurThresholdId)) _furThreshold = mat.GetFloat(FurThresholdId);
if (mat.HasProperty(FurSoftnessId)) _furSoftness = mat.GetFloat(FurSoftnessId);
if (mat.HasProperty(StrandSoftnessId)) _strandSoftness = mat.GetFloat(StrandSoftnessId);
if (mat.HasProperty(DepthSortClipId)) _depthSortClip = mat.GetFloat(DepthSortClipId);
if (mat.HasProperty(SolidRootId)) _solidRoot = mat.GetFloat(SolidRootId) > 0.5f;
if (mat.HasProperty(WindStrengthId)) _windStrength = mat.GetFloat(WindStrengthId);
if (mat.HasProperty(WindSpeedId)) _windSpeed = mat.GetFloat(WindSpeedId);