Fur als 4. Paint-Map + Ziel-Opacity-Blend
Fur wird die vierte kanonische Paint-Map (RGB=Farbe, A=Länge). Integration über Seed (kahl), GPU/CPU-Paint, Undo, Netzwerk-Sync, Persistence (fur.png, v6) und Live-RT-Hook im FurShellRenderer. HUD-Binding (Toggle/Picker/Alpha) aktiviert. Fur-Alpha lerpt jetzt zu einer Ziel-Opacity (Slider_FurOpacity) statt additiv Richtung 1 zu akkumulieren (zwei Brush-Passes: Fade + Deposit). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
43791abc79
commit
7536e8ec8b
11 files changed
+319
-20
No files matched your search
@@ -36,7 +36,9 @@ public partial class MeshPainter
|
||||
{
|
||||
var src = ResolveSeed(mi, canonical);
|
||||
var px = (Color32[])src.Clone();
|
||||
if (mi != MAP_MSO) // albedo / emission: opaque. The mask map keeps alpha = smoothness.
|
||||
// Albedo / emission are opaque. The mask map keeps alpha = smoothness; fur keeps alpha = painted
|
||||
// length (0 = bald), which is data, not coverage.
|
||||
if (mi != MAP_MSO && mi != MAP_FUR)
|
||||
for (int i = 0; i < px.Length; i++) px[i].a = 255;
|
||||
return px;
|
||||
}
|
||||
@@ -66,7 +68,9 @@ public partial class MeshPainter
|
||||
{
|
||||
if (!IsChannelActive(mi)) continue;
|
||||
var cm = _cpu[mi];
|
||||
if (paddingTexels > 0) DilatePadding(cm, paddingTexels);
|
||||
// Fur skips edge padding: its alpha is length, not a coverage guard, so there is no valid
|
||||
// dilation source mask (matches the GPU path, which also skips fur in EndGpuStroke).
|
||||
if (paddingTexels > 0 && mi != MAP_FUR) DilatePadding(cm, paddingTexels);
|
||||
FlushDirty(cm);
|
||||
}
|
||||
}
|
||||
@@ -98,8 +102,8 @@ public partial class MeshPainter
|
||||
/// </summary>
|
||||
private void PaintStamp3D(Vector3 pLocal, float radiusLocal, Vector3 u, Vector3 v, Vector3 n)
|
||||
{
|
||||
bool aAlb = paintAlbedo, aMS = paintMetallicSmoothness, aEM = paintEmission, aAO = paintOcclusion;
|
||||
if (!aAlb && !aMS && !aEM && !aAO) return;
|
||||
bool aAlb = paintAlbedo, aMS = paintMetallicSmoothness, aEM = paintEmission, aAO = paintOcclusion, aFur = fur;
|
||||
if (!aAlb && !aMS && !aEM && !aAO && !aFur) return;
|
||||
|
||||
// Albedo target (brush color in linear space; blended linear to match the GPU).
|
||||
float brLin = Mathf.GammaToLinearSpace(brushColor.r);
|
||||
@@ -125,6 +129,15 @@ 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;
|
||||
CpuMap furm = _cpu[MAP_FUR];
|
||||
|
||||
float invR = 1f / radiusLocal;
|
||||
int triCount = _tris.Length / 3;
|
||||
float ts = textureSize;
|
||||
@@ -243,6 +256,27 @@ public partial class MeshPainter
|
||||
}
|
||||
}
|
||||
|
||||
if (aFur)
|
||||
{
|
||||
float op = maskOp * furOpMul;
|
||||
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);
|
||||
furm.painted[idx] = true;
|
||||
ExpandDirty(furm, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user