FurShell: remove scale-influence, curvature scales with player scale

Drop the _FurScaleInfluence blend (fur stays a constant world length at
any avatar size, as tuned to influence 0) and scale the gravity curvature
droop by the avatar world scale (_FurObjScale) instead, so the bend tracks
the player: hider 0.5 => smaller droop, hunter 1.5 => bigger droop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-06 17:31:05 +02:00
1 parent 41d5b87e9e
commit 9d16524ded
2 files changed
+20 -35

No files matched your search

+4 -13
View File
@@ -41,11 +41,6 @@ namespace Chameleon.Components
// direction so the flat shell slices merge into continuous strands at the rim instead of squished
// dots. 0 = off (footprint stays circular everywhere).
[SerializeField, Range(0f, 5f)] float _silhouetteStretch = 2f;
// How much the avatar's world scale drives the fur SIZE. 1 = fully proportional (small avatar => small
// fur, like the body). 0 = constant world size (same absolute fur length at any avatar scale). Fully
// proportional fur reads as too short on the small hider and too long on the big hunter, so dial this
// down to keep the fur closer to a fixed world length; the curl keeps its shape at any setting.
[SerializeField, Range(0f, 1f)] float _furScaleInfluence = 0.5f;
[Header("Shading")]
// Optional albedo (strand-colour) override for the standalone prototype; a sibling MeshPainter's
@@ -124,7 +119,6 @@ namespace Chameleon.Components
static readonly int WindFrequencyId = Shader.PropertyToID("_WindFrequency");
static readonly int WindDirectionId = Shader.PropertyToID("_WindDirection");
static readonly int FurObjScaleId = Shader.PropertyToID("_FurObjScale");
static readonly int FurScaleInfluenceId = Shader.PropertyToID("_FurScaleInfluence");
static readonly int XRayColorId = Shader.PropertyToID("_XRayColor");
static readonly int XRayPulseSpeedId = Shader.PropertyToID("_XRayPulseSpeed");
static readonly int XRayPulseStrengthId = Shader.PropertyToID("_XRayPulseStrength");
@@ -264,14 +258,11 @@ namespace Chameleon.Components
_mpb.SetFloat(WindFrequencyId, _windFrequency);
_mpb.SetVector(WindDirectionId, new Vector4(_windDirection.x, _windDirection.y, 0f, 0f));
// Avatar world scale (uniform via PlayerScaler) = the scale baked into the instance matrix used
// for the shell extrusion, so the shader can scale the curvature droop in lockstep and keep the
// curl proportional at any player size (hider 0.5 / hunter 1.5). Read from lossyScale because
// the raw object matrix does NOT carry the per-instance scale in the RenderMeshInstanced path.
// Avatar world scale (uniform via PlayerScaler). The shader keeps the fur a CONSTANT world length
// but scales the gravity droop by this, so the bend tracks the player (hider 0.5 / hunter 1.5).
// Read from lossyScale because the raw object matrix does NOT carry the per-instance scale in the
// RenderMeshInstanced path.
_mpb.SetFloat(FurObjScaleId, transform.lossyScale.y);
// Blend the fur size between fully proportional to the avatar (1) and a constant world size (0):
// the shader pre-divides the object-space displacement so the net world length follows this.
_mpb.SetFloat(FurScaleInfluenceId, _furScaleInfluence);
// Mirror the sibling painter's live body X-ray reveal onto the fur so the strands glow WITH the
// reveal (see-through camo) instead of hiding it under opaque fur. Same colour/pulse/flash the
+16 -22
View File
@@ -60,13 +60,10 @@ Shader "Custom/FurShell"
_WindFrequency("Wind Spatial Frequency", Range(0.0, 10.0)) = 1.0
_WindDirection("Wind Direction (object XZ)", Vector) = (1, 0, 0, 0)
// Avatar world scale, pushed by FurShellRenderer (transform.lossyScale). Drives the curvature droop
// so the curl stays proportional at any player size; 1 = authored size (see the vertex shader).
// Avatar world scale, pushed by FurShellRenderer (transform.lossyScale). The fur keeps a CONSTANT
// world length at any player size, but the gravity droop scales with this so the bend tracks the
// avatar: small hider => small droop, big hunter => big droop. 1 = authored size (vertex shader).
[HideInInspector] _FurObjScale("Object Scale (driven by FurShellRenderer)", Float) = 1
// How strongly the avatar scale drives the fur SIZE: 1 = fully proportional (fur shrinks/grows with the
// avatar), 0 = constant world size (same absolute fur length at any avatar scale). Proportional fur
// reads as too short on the small hider and too long on the big hunter, so this blends between them.
[HideInInspector] _FurScaleInfluence("Scale Influence (driven by FurShellRenderer)", Range(0, 1)) = 0.5
[Header(XRay Reveal)]
// See-through reveal glow mirrored from the sibling MeshPainter's body X-ray so the fur strands glow
@@ -150,7 +147,6 @@ Shader "Custom/FurShell"
float _WindFrequency;
float4 _WindDirection;
float _FurObjScale;
float _FurScaleInfluence;
half4 _XRayColor;
half _XRayPulseSpeed;
half _XRayPulseStrength;
@@ -202,16 +198,14 @@ Shader "Custom/FurShell"
float count = max(_ShellCount, 1.0);
float h = (count <= 1.0) ? 0.0 : (float)shellIndex / (count - 1.0);
// Effective fur-size scale. The instance matrix already multiplies the object-space fur
// displacement by the avatar's world scale (_FurObjScale), so at full influence the fur is
// fully proportional (small avatar => small fur, like the body). _FurScaleInfluence blends
// that toward a CONSTANT world size (influence 0 => same absolute fur length at any avatar
// size), because fully proportional fur reads as too short on the small hider and too long on
// the big hunter. dispComp pre-divides the object-space offset so that after the matrix's
// *_FurObjScale the NET world displacement equals effScale, not _FurObjScale.
// The instance matrix multiplies the object-space fur displacement by the avatar's world scale
// (objScale), so a naive extrusion shrinks/grows 1:1 with the player - too short on the small
// hider, too long on the big hunter. To keep the fur a CONSTANT world length (same absolute
// length at any size) we pre-divide the object-space offset by objScale; after the matrix's
// *objScale the net world displacement is then 1x. Wind sway shares this so it stays a constant
// world size too. (The gravity droop below intentionally does NOT - it scales with objScale.)
float objScale = max(_FurObjScale, 1e-4);
float effScale = lerp(1.0, objScale, _FurScaleInfluence);
float dispComp = effScale / objScale;
float dispComp = 1.0 / objScale;
// extrude along the normal; bend the tips down with h^2 for a gravity/curl droop.
// The (h + 0.02) lifts the whole stack a hair off the surface: the root (h=0) would else
@@ -237,12 +231,12 @@ Shader "Custom/FurShell"
// RenderMeshInstanced path - when it falls back to identity the droop follows the mesh's own
// local down, so on the rotated player the fur hung sideways instead of toward true down.
float3 positionWS = TransformObjectToWorld(posOS);
// Curl droop in world down. It uses the SAME effScale as the extrusion above (not the raw
// _FurObjScale), so the curl keeps its SHAPE relative to the fur length at any avatar size and
// at any influence setting - dialing the length toward constant carries the droop with it
// instead of leaving it too strong on the shrunk hider / too weak on the grown hunter. Applied
// after moving to world space, so the droop follows true world down regardless of player rotation.
positionWS.y -= _Curvature * _ShellLength * h * h * effScale;
// Curl droop in world down, scaled by the avatar's world size (objScale) so the bend tracks the
// player: a bigger avatar gets a proportionally bigger droop, a smaller one a smaller one. The
// fur length above is a CONSTANT world size, so scaling only the droop keeps the bend reading
// right relative to the body at every player scale (hider 0.5 / hunter 1.5). Applied after
// moving to world space, so the droop follows true world down regardless of player rotation.
positionWS.y -= _Curvature * _ShellLength * h * h * objScale;
VertexNormalInputs vn = GetVertexNormalInputs(input.normalOS);