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
@@ -36,8 +36,8 @@ public partial class MeshPainter
|
||||
{
|
||||
var src = ResolveSeed(mi, canonical);
|
||||
var px = (Color32[])src.Clone();
|
||||
// Albedo / emission are opaque. The mask map keeps alpha = smoothness; fur keeps alpha = painted
|
||||
// length (0 = bald), which is data, not coverage.
|
||||
// Albedo / emission are opaque. The mask map keeps alpha = smoothness; fur is a data map (R =
|
||||
// length), so leave its channels as seeded rather than forcing alpha opaque.
|
||||
if (mi != MAP_MSO && mi != MAP_FUR)
|
||||
for (int i = 0; i < px.Length; i++) px[i].a = 255;
|
||||
return px;
|
||||
@@ -102,6 +102,8 @@ public partial class MeshPainter
|
||||
/// </summary>
|
||||
private void PaintStamp3D(Vector3 pLocal, float radiusLocal, Vector3 u, Vector3 v, Vector3 n)
|
||||
{
|
||||
// Fur is gated by its own toggle (see IsChannelActive), reusing the brush's alpha as the length
|
||||
// deposit weight; the strand colour still comes from the albedo map at render time.
|
||||
bool aAlb = paintAlbedo, aMS = paintMetallicSmoothness, aEM = paintEmission, aAO = paintOcclusion, aFur = fur;
|
||||
if (!aAlb && !aMS && !aEM && !aAO && !aFur) return;
|
||||
|
||||
@@ -129,13 +131,9 @@ public partial class MeshPainter
|
||||
float emOpMul = Mathf.Clamp01(emissionColor.a);
|
||||
CpuMap em = _cpu[MAP_EM];
|
||||
|
||||
// Fur target (linear RGB like albedo; alpha = painted length that EASES toward furTargetA
|
||||
// at the brush strength furColor.a, not accumulating to full - mirrors the GPU fade+deposit).
|
||||
float frLin = Mathf.GammaToLinearSpace(furColor.r);
|
||||
float fgLin = Mathf.GammaToLinearSpace(furColor.g);
|
||||
float fbLin = Mathf.GammaToLinearSpace(furColor.b);
|
||||
float furOpMul = Mathf.Clamp01(furColor.a);
|
||||
float furTargetA = Mathf.Clamp01(furOpacity) * 255f;
|
||||
// Fur target: only R = length, eased toward furLength*255 at the brush's alpha (albOpMul) as the
|
||||
// deposit weight - a linear data lerp (no sRGB), mirroring the GPU source-over on R.
|
||||
float furTargetR = Mathf.Clamp01(furLength) * 255f;
|
||||
CpuMap furm = _cpu[MAP_FUR];
|
||||
|
||||
float invR = 1f / radiusLocal;
|
||||
@@ -258,20 +256,14 @@ public partial class MeshPainter
|
||||
|
||||
if (aFur)
|
||||
{
|
||||
float op = maskOp * furOpMul;
|
||||
// Length (R) eases toward furLength at the brush weight: lerp(dst.R, target, op) -
|
||||
// over-writable, not accumulating. Linear data, so no sRGB blend. G/B/A stay 0.
|
||||
float op = maskOp * albOpMul;
|
||||
if (op > 0f)
|
||||
{
|
||||
Color32 dst = furm.pixels[idx];
|
||||
float it = 1f - op;
|
||||
// RGB source-over toward the fur colour (linear); alpha EASES toward the target
|
||||
// length at the deposit rate: lerp(dst.a, target, op) - over-writable, not
|
||||
// accumulating. Mirrors the GPU fur fade (alpha *= 1-op) + deposit (+= op*target).
|
||||
byte na = (byte)(dst.a * it + furTargetA * op + 0.5f);
|
||||
furm.pixels[idx] = new Color32(
|
||||
BlendSrgb(dst.r, frLin, it, op),
|
||||
BlendSrgb(dst.g, fgLin, it, op),
|
||||
BlendSrgb(dst.b, fbLin, it, op),
|
||||
na);
|
||||
byte nr = (byte)(dst.r * (1f - op) + furTargetR * op + 0.5f);
|
||||
furm.pixels[idx] = new Color32(nr, 0, 0, 0);
|
||||
furm.painted[idx] = true;
|
||||
ExpandDirty(furm, x, y);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user