Paint-Partikel beim Malen: Emission pro Stamp, Scale = Brush-Radius, Farbe = Brush-Albedo mit Helligkeits-Streuung

- MeshPainter: Paint Particles Prefab, lazy Instanz, Play/Stop am Stroke, MinMaxGradient (Albedo +-25%/15% dunkel/hell)
- PaintSplatter.prefab + Paint Particle Material/Textur, Wiring am Player.prefab
- FurShellRenderer: Kill-Switch-Destroy nur im Play-Mode (Edit-Mode-Error beim Prefab-Load)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Fable 5 committed 2026-07-11 22:13:16 +02:00
1 parent a5438cf5a0
commit 094cf3a493
9 files changed
+5169 -3

No files matched your search

+139
View File
@@ -0,0 +1,139 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-4934386220503690563
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Paint Particle
m_Shader: {fileID: 4800000, guid: 0406db5a14f94604a8c57ccfbc9f3b46, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _SURFACE_TYPE_TRANSPARENT
m_InvalidKeywords:
- _FLIPBOOKBLENDING_OFF
- _RECEIVE_SHADOWS_OFF
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 3000
stringTagMap:
RenderType: Transparent
disabledShaderPasses:
- ALWAYS
- DepthOnly
- SHADOWCASTER
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 2800000, guid: 16290b7fdde7a604f852a7d50da25795, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 10300, guid: 0000000000000000f000000000000000, type: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 0
- _AlphaToMask: 0
- _BaseColorMode: 0
- _Blend: 0
- _BlendOp: 0
- _BumpScale: 1
- _CameraFadingEnabled: 0
- _CameraFarFadeDistance: 2
- _CameraNearFadeDistance: 1
- _ColorMode: 0
- _Cull: 2
- _Cutoff: 0.7
- _DetailNormalMapScale: 1
- _DistortionBlend: 0.5
- _DistortionEnabled: 0
- _DistortionStrength: 1
- _DistortionStrengthScaled: 0
- _DstBlend: 10
- _DstBlendAlpha: 10
- _EmissionEnabled: 0
- _EnvironmentReflections: 1
- _FlipbookBlending: 0
- _FlipbookMode: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _InvFade: 1
- _LightingEnabled: 0
- _Metallic: 0
- _Mode: 2
- _OcclusionStrength: 1
- _Parallax: 0.02
- _QueueOffset: 0
- _SmoothnessTextureChannel: 0
- _SoftParticlesEnabled: 0
- _SoftParticlesFarFadeDistance: 1
- _SoftParticlesNearFadeDistance: 0
- _SpecularHighlights: 1
- _SrcBlend: 5
- _SrcBlendAlpha: 1
- _Surface: 1
- _UVSec: 0
- _ZWrite: 0
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _BaseColorAddSubDiff: {r: 1, g: 0, b: 0, a: 0}
- _CameraFadeParams: {r: 0, g: Infinity, b: 0, a: 0}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _ColorAddSubDiff: {r: 0, g: 0, b: 0, a: 0}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SoftParticleFadeParams: {r: 0, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 884e834faeecb6140b911ba7046e69bf
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large. Load diff
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 129e63016225faa4cb3c3ec86a7f66e5
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+10 -2
View File
@@ -227,7 +227,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
GlobalObjectIdHash: 1963377418
GlobalObjectIdHash: 459555553
InScenePlacedSourceGlobalObjectIdHash: 0
DeferredDespawnTick: 0
Ownership: 1
@@ -525,6 +525,7 @@ MonoBehaviour:
cornerGuardMaxFraction: 0.7
previewMode: 2
previewOpacity: 1
paintParticlesPrefab: {fileID: 723043959019929999, guid: 129e63016225faa4cb3c3ec86a7f66e5, type: 3}
textureSize: 512
baseColor: {r: 0.8, g: 0.8, b: 0.8, a: 1}
paddingTexels: 8
@@ -551,13 +552,16 @@ MonoBehaviour:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8192868123569919240}
m_Enabled: 1
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a3c62508e4154584680226a26d48f0af, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Chameleon.Components.FurShellRenderer
_furMaterial: {fileID: 2100000, guid: ea8336dff8223b241a672d9fab39e1fa, type: 2}
_shellCount: 32
_lodStartDistance: 8
_lodEndDistance: 30
_lodMinShells: 8
_shellLength: 8
_density: 400
_thickness: 3
@@ -576,6 +580,7 @@ MonoBehaviour:
_furThreshold: 0
_furSoftness: 0.5
_strandSoftness: 0.3
_depthSortClip: 0.3
_solidRoot: 0
_windStrength: 0.002
_windSpeed: 2
@@ -584,4 +589,7 @@ MonoBehaviour:
_xrayFurStrength: 0.00001
_shadowMode: 0
_receiveShadows: 1
_renderingLayerMask:
serializedVersion: 0
m_Bits: 1
_hideSourceRenderer: 1
@@ -153,7 +153,9 @@ namespace Chameleon.Components
return;
}
Destroy(this);
// Play mode only: in the editor Awake also runs on edit-mode loads (LoadPrefabContents,
// [ExecuteAlways]) where Destroy is illegal - there OnEnable's disable path handles it.
if (Application.isPlaying) Destroy(this);
}
void OnEnable()
@@ -328,6 +328,14 @@ public partial class MeshPainter : MonoBehaviour
[Range(0f, 4f)]
public float previewOpacity = 0.5f;
[Header("Paint particles")]
[Tooltip("Optional particle effect played at the brush while a stroke deposits paint. Instantiated " +
"once on first use. Author the system for brush world-radius 1 with scaling mode Hierarchy: " +
"the instance is scaled to the brush's world radius and tinted with the brush albedo color " +
"(Start Color is overwritten). +Z of the instance points along the surface normal.")]
public ParticleSystem paintParticlesPrefab;
private ParticleSystem _paintParticles;
[Header("Canvas")]
[Tooltip("Resolution of the paint textures (square).")]
public int textureSize = 1024;
@@ -829,6 +837,7 @@ public partial class MeshPainter : MonoBehaviour
if (!PaintingEnabled)
{
EndStrokeIfActive(); // finalize an in-progress stroke if the mode was left mid-paint
StopPaintParticles();
_hasLastScreen = false;
return;
}
@@ -859,6 +868,7 @@ public partial class MeshPainter : MonoBehaviour
if (!TryGetMouse(out bool pressed, out Vector2 screenPos))
{
StrokeInProgress = false;
StopPaintParticles();
return;
}
@@ -869,6 +879,7 @@ public partial class MeshPainter : MonoBehaviour
// While the button is held on the paint path the hardware cursor is hidden entirely
// (CursorController); the preview decal itself stays visible during the stroke.
StrokeInProgress = pressed && !pipette;
if (!StrokeInProgress) StopPaintParticles();
// Brush preview at the hover point.
if (previewMode != BrushPreviewMode.Off && _previewMat && paintCamera)
@@ -975,11 +986,46 @@ public partial class MeshPainter : MonoBehaviour
else UseResolvedStampMask();
DrawBackendStamp(pLocal, radiusLocal, u, v, nLocal);
UpdatePaintParticles(pLocal, radiusLocal, nLocal);
EmitStamp(pLocal, radiusLocal, u, v, nLocal);
MarkStrokePainted(); // a stamp landed -> this stroke is undoable
}
/// <summary>Moves/scales/tints the stroke particle effect at a landed stamp and keeps it playing.
/// Scale = brush world radius (author the prefab for radius 1, scaling mode Hierarchy), tint =
/// brush albedo color with a small per-particle random offset (alpha forced opaque - the brush
/// alpha is paint opacity, not particle fade).</summary>
private void UpdatePaintParticles(Vector3 pLocal, float radiusLocal, Vector3 nLocal)
{
if (!paintParticlesPrefab) return;
if (!_paintParticles)
_paintParticles = Instantiate(paintParticlesPrefab);
Transform t = _paintParticles.transform;
t.SetPositionAndRotation(transform.TransformPoint(pLocal),
Quaternion.LookRotation(transform.TransformDirection(nLocal)));
t.localScale = Vector3.one * transform.TransformVector(nLocal * radiusLocal).magnitude;
var main = _paintParticles.main;
Color c = brushColor;
c.a = 1f;
// Per-particle random tint offset around the albedo (darker <-> lighter).
Color dark = Color.Lerp(c, Color.black, 0.13f);
Color light = Color.Lerp(c, Color.white, 0.09f);
dark.a = light.a = 1f;
main.startColor = new ParticleSystem.MinMaxGradient(dark, light);
if (!_paintParticles.isPlaying) _paintParticles.Play();
}
/// <summary>Stops emitting stroke particles (already-spawned ones live out their lifetime).</summary>
private void StopPaintParticles()
{
if (_paintParticles && _paintParticles.isPlaying)
_paintParticles.Stop();
}
/// <summary>
/// True if a brush dab at this placement would wrap excessively around a corner: more than
/// <see cref="cornerGuardMaxFraction"/> of its footprint sits on surface whose face normal
@@ -1235,6 +1281,7 @@ public partial class MeshPainter : MonoBehaviour
if (_proceduralMask) Destroy(_proceduralMask);
if (_previewMat) Destroy(_previewMat);
if (_previewQuad) Destroy(_previewQuad);
if (_paintParticles) Destroy(_paintParticles.gameObject);
TeardownSpray();
TeardownCpu();
TeardownGpu();
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+117
View File
@@ -0,0 +1,117 @@
fileFormatVersion: 2
guid: 16290b7fdde7a604f852a7d50da25795
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant: