Fur: Albedo-gekoppelte Länge, Toggle-Gate, Soft-Edge, World-Gravity, Pipette & FP-Hide
- Fur-Map trägt nur noch R = Länge (linearer Datenkanal); Farbe kommt per-texel aus der Live-Albedo-RT (an _BaseMap des Shell-Shaders gebunden) - Toggle_Fur als echtes Gate (IsChannelActive = fur), Default aus, furLength 0.5 - Weicher Alpha-Übergang (radial/tip/length coverage) statt Hard-Clip; Transparent- Pass mit Depth-Bias (Offset -1,-1) gegen Body-Durchflimmern - Root dunkler / Tip heller aus gemalter Farbe abgeleitet (kein Fix-Tint) - Gravity/Curl in World-Space nach Transform (unabhängig von Mesh-Rotation) - Pipette aktiviert Fur-Toggle immer; liest R-Kanal als Länge - First-Person blendet FurShellRenderer mit aus - Netzwerk (FlagFur), Undo, Persistence (fur.png R=Länge) mitgezogen Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
7536e8ec8b
commit
1b783a39be
15 files changed
+254
-306
No files matched your search
@@ -43,23 +43,31 @@ namespace Chameleon.Components
|
||||
[SerializeField, Range(0f, 5f)] float _silhouetteStretch = 2f;
|
||||
|
||||
[Header("Shading")]
|
||||
// Optional per-instance albedo override; leave null to keep the material's own texture.
|
||||
// Optional albedo (strand-colour) override for the standalone prototype; a sibling MeshPainter's
|
||||
// live albedo RT overrides this when present, so the fur wears the painted camo per texel.
|
||||
[SerializeField] Texture _baseMap;
|
||||
[SerializeField] Color _rootColor = new Color(0.42f, 0.32f, 0.20f, 1f);
|
||||
[SerializeField] Color _tipColor = new Color(0.62f, 0.52f, 0.36f, 1f);
|
||||
[SerializeField, Range(0f, 1f)] float _rootAO = 0.45f;
|
||||
// Overall tint multiplied onto the PAINTED fur colour; white leaves the painted colour untouched.
|
||||
[SerializeField] Color _furTint = Color.white;
|
||||
// Root/tip are two shades of the same painted colour: roots a touch darker, tips a touch lighter.
|
||||
[SerializeField, Range(0f, 1f)] float _rootAO = 0.3f;
|
||||
[SerializeField, Range(0f, 1f)] float _tipShade = 0.2f;
|
||||
[SerializeField, Range(0.5f, 8f)] float _rimPower = 3f;
|
||||
[SerializeField, Range(0f, 2f)] float _rimStrength = 0.4f;
|
||||
|
||||
[Header("Fur Map")]
|
||||
// RGBA fur map: RGB = per-texel strand colour, A = fur length (0 = bald, 1 = full length). Leave
|
||||
// null on the standalone prototype - the shader's white default is uniform full-length fur. A
|
||||
// sibling MeshPainter's live fur RT overrides this when present, so a painted avatar grows fur in
|
||||
// real time (see PushProperties).
|
||||
// Fur map: R = fur length (0 = bald, 1 = full length); G/B/A unused. The strand colour comes from
|
||||
// the albedo map, not here. Leave null on the standalone prototype - the shader's white default is
|
||||
// uniform full-length fur. A sibling MeshPainter's live fur RT overrides this when present, so a
|
||||
// painted avatar grows fur in real time (see PushProperties).
|
||||
[SerializeField] Texture _furMap;
|
||||
// Fur is fully cut where the map's alpha is at/below this - keeps stray dilation fuzz from growing
|
||||
// hair. 0 keeps the exact "alpha 0 = no fur" contract.
|
||||
[SerializeField, Range(0f, 1f)] float _furThreshold = 0f;
|
||||
// Low painted alpha => low fur opacity, so a lightly-painted region fades in as faint short fuzz
|
||||
// instead of hard-cutting to full-opacity fur. Larger = softer/longer fade.
|
||||
[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;
|
||||
// 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;
|
||||
@@ -89,12 +97,14 @@ namespace Chameleon.Components
|
||||
static readonly int SilhouetteStretchId = Shader.PropertyToID("_SilhouetteStretch");
|
||||
static readonly int BaseMapId = Shader.PropertyToID("_BaseMap");
|
||||
static readonly int BaseColorId = Shader.PropertyToID("_BaseColor");
|
||||
static readonly int TipColorId = Shader.PropertyToID("_TipColor");
|
||||
static readonly int RootAOId = Shader.PropertyToID("_RootAO");
|
||||
static readonly int TipShadeId = Shader.PropertyToID("_TipShade");
|
||||
static readonly int RimPowerId = Shader.PropertyToID("_RimPower");
|
||||
static readonly int RimStrengthId = Shader.PropertyToID("_RimStrength");
|
||||
static readonly int FurMapId = Shader.PropertyToID("_FurMap");
|
||||
static readonly int FurThresholdId = Shader.PropertyToID("_FurThreshold");
|
||||
static readonly int FurSoftnessId = Shader.PropertyToID("_FurSoftness");
|
||||
static readonly int StrandSoftnessId = Shader.PropertyToID("_StrandSoftness");
|
||||
static readonly int SolidRootId = Shader.PropertyToID("_SolidRoot");
|
||||
static readonly int WindStrengthId = Shader.PropertyToID("_WindStrength");
|
||||
static readonly int WindSpeedId = Shader.PropertyToID("_WindSpeed");
|
||||
@@ -211,16 +221,22 @@ namespace Chameleon.Components
|
||||
_mpb.SetFloat(DirRandomnessId, _directionRandomness);
|
||||
_mpb.SetFloat(SilhouetteStretchId, _silhouetteStretch);
|
||||
|
||||
if (_baseMap) _mpb.SetTexture(BaseMapId, _baseMap);
|
||||
// Prefer the sibling painter's live fur RT (painted colour + length) over the serialized map.
|
||||
// Strand colour = the painted albedo: prefer the sibling painter's live albedo RT over the
|
||||
// serialized override, so the fur wears the camo per texel. Re-fetched each frame - the RT
|
||||
// reference ping-pongs on the dilation swap.
|
||||
Texture albedoTex = _painter && _painter.AlbedoMapTexture ? _painter.AlbedoMapTexture : _baseMap;
|
||||
if (albedoTex) _mpb.SetTexture(BaseMapId, albedoTex);
|
||||
// Prefer the sibling painter's live fur RT (R = painted length) over the serialized map.
|
||||
Texture furTex = _painter && _painter.FurMapTexture ? _painter.FurMapTexture : _furMap;
|
||||
if (furTex) _mpb.SetTexture(FurMapId, furTex);
|
||||
_mpb.SetColor(BaseColorId, ToRenderColor(_rootColor));
|
||||
_mpb.SetColor(TipColorId, ToRenderColor(_tipColor));
|
||||
_mpb.SetColor(BaseColorId, ToRenderColor(_furTint));
|
||||
_mpb.SetFloat(RootAOId, _rootAO);
|
||||
_mpb.SetFloat(TipShadeId, _tipShade);
|
||||
_mpb.SetFloat(RimPowerId, _rimPower);
|
||||
_mpb.SetFloat(RimStrengthId, _rimStrength);
|
||||
_mpb.SetFloat(FurThresholdId, _furThreshold);
|
||||
_mpb.SetFloat(FurSoftnessId, _furSoftness);
|
||||
_mpb.SetFloat(StrandSoftnessId, _strandSoftness);
|
||||
_mpb.SetFloat(SolidRootId, _solidRoot ? 1f : 0f);
|
||||
|
||||
_mpb.SetFloat(WindStrengthId, _windStrength);
|
||||
@@ -253,12 +269,14 @@ namespace Chameleon.Components
|
||||
if (mat.HasProperty(CurvatureId)) _curvature = mat.GetFloat(CurvatureId);
|
||||
if (mat.HasProperty(DirRandomnessId)) _directionRandomness = mat.GetFloat(DirRandomnessId);
|
||||
if (mat.HasProperty(SilhouetteStretchId)) _silhouetteStretch = mat.GetFloat(SilhouetteStretchId);
|
||||
if (mat.HasProperty(BaseColorId)) _rootColor = FromRenderColor(mat.GetColor(BaseColorId));
|
||||
if (mat.HasProperty(TipColorId)) _tipColor = FromRenderColor(mat.GetColor(TipColorId));
|
||||
if (mat.HasProperty(BaseColorId)) _furTint = FromRenderColor(mat.GetColor(BaseColorId));
|
||||
if (mat.HasProperty(RootAOId)) _rootAO = mat.GetFloat(RootAOId);
|
||||
if (mat.HasProperty(TipShadeId)) _tipShade = mat.GetFloat(TipShadeId);
|
||||
if (mat.HasProperty(RimPowerId)) _rimPower = mat.GetFloat(RimPowerId);
|
||||
if (mat.HasProperty(RimStrengthId)) _rimStrength = mat.GetFloat(RimStrengthId);
|
||||
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(SolidRootId)) _solidRoot = mat.GetFloat(SolidRootId) > 0.5f;
|
||||
if (mat.HasProperty(WindStrengthId)) _windStrength = mat.GetFloat(WindStrengthId);
|
||||
if (mat.HasProperty(WindSpeedId)) _windSpeed = mat.GetFloat(WindSpeedId);
|
||||
|
||||
Reference in new issue
Block a user