MeshPainter: Rückseiten-Abweisung über Flächen-Normale bei großem Brush
Zusätzlich zum Kugel-Test wird die Oberflächen-Normale jedes Texels gegen die Brush-Normale geprüft (CPU/GPU/Preview). Neues Inspector-Feld "Brush Max Surface Angle" (90-180°, Default 120°). Bei fehlenden Mesh-Normalen Degradation zur reinen Kugel. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
9d17baee85
commit
cdd9fa9532
7 files changed
+68
-8
No files matched your search
@@ -51,18 +51,21 @@ Shader "Hidden/MeshPaintBrush"
|
||||
float3 _BrushV; // stamp up axis (unit, local space)
|
||||
float3 _BrushNormal; // surface normal at the hit (unit, local space)
|
||||
float _BrushRadius; // stamp half-extent in local mesh space
|
||||
float _BrushNormalCutoff; // cos(maxAngle): paint only if dot(texelN, brushN) >= this (-1 = off)
|
||||
sampler2D _MaskTex;
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION; // local 3D position
|
||||
float3 normal : NORMAL; // local surface normal
|
||||
float2 uv : TEXCOORD0; // UV (target in the texture layout)
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
float3 local : TEXCOORD0; // interpolated local 3D position
|
||||
float4 pos : SV_POSITION;
|
||||
float3 local : TEXCOORD0; // interpolated local 3D position
|
||||
float3 normal : TEXCOORD1; // interpolated local surface normal
|
||||
};
|
||||
|
||||
v2f vert (appdata v)
|
||||
@@ -76,6 +79,7 @@ Shader "Hidden/MeshPaintBrush"
|
||||
#endif
|
||||
o.pos = float4(p, 0.0, 1.0);
|
||||
o.local = v.vertex.xyz;
|
||||
o.normal = v.normal;
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -91,6 +95,11 @@ Shader "Hidden/MeshPaintBrush"
|
||||
// (both sides of a 3D edge) yet never bleeds onto far/adjacent surfaces past r.
|
||||
clip(_BrushRadius * _BrushRadius - (du * du + dv * dv + dn * dn));
|
||||
|
||||
// Back-face rejection: the opposite side of thin geometry faces ~180deg away,
|
||||
// so drop texels whose normal turns away from the clicked surface past the cutoff.
|
||||
float nl = dot(normalize(i.normal), _BrushNormal);
|
||||
clip(nl - _BrushNormalCutoff);
|
||||
|
||||
// Mask -> opacity. The sphere guarantees |du|,|dv| <= r -> uv in [0,1].
|
||||
float2 muv = float2(du, dv) / _BrushRadius * 0.5 + 0.5;
|
||||
fixed4 m = tex2D(_MaskTex, muv);
|
||||
|
||||
@@ -43,6 +43,7 @@ Shader "Hidden/MeshPaintPreview"
|
||||
float3 _BrushV; // stamp up axis (unit, local space)
|
||||
float3 _BrushNormal; // surface normal at the hit (unit, local space)
|
||||
float _BrushRadius; // stamp half-extent in local mesh space
|
||||
float _BrushNormalCutoff; // cos(maxAngle): show only if dot(texelN, brushN) >= this (-1 = off)
|
||||
float _PreviewOpacity; // overall preview alpha multiplier (>1 saturates to fully opaque)
|
||||
float _PreviewMode; // 0 = on-surface (mesh), 1 = flat decal quad
|
||||
sampler2D _MaskTex;
|
||||
@@ -50,14 +51,16 @@ Shader "Hidden/MeshPaintPreview"
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float3 normal : NORMAL;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
float3 local : TEXCOORD0; // object-space position (on-surface box test)
|
||||
float2 uv : TEXCOORD1; // quad uv (decal sampling)
|
||||
float4 pos : SV_POSITION;
|
||||
float3 local : TEXCOORD0; // object-space position (on-surface box test)
|
||||
float2 uv : TEXCOORD1; // quad uv (decal sampling)
|
||||
float3 normal : TEXCOORD2; // object-space normal (on-surface back-face test)
|
||||
};
|
||||
|
||||
v2f vert (appdata v)
|
||||
@@ -66,6 +69,7 @@ Shader "Hidden/MeshPaintPreview"
|
||||
o.pos = UnityObjectToClipPos(v.vertex);
|
||||
o.local = v.vertex.xyz;
|
||||
o.uv = v.uv;
|
||||
o.normal = v.normal;
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -80,6 +84,8 @@ Shader "Hidden/MeshPaintPreview"
|
||||
float dv = dot(d, _BrushV);
|
||||
float dn = dot(d, _BrushNormal);
|
||||
clip(_BrushRadius * _BrushRadius - (du * du + dv * dv + dn * dn));
|
||||
// Back-face rejection: hide the preview on the opposite side of thin geometry.
|
||||
clip(dot(normalize(i.normal), _BrushNormal) - _BrushNormalCutoff);
|
||||
muv = float2(du, dv) / _BrushRadius * 0.5 + 0.5;
|
||||
}
|
||||
else
|
||||
|
||||
Reference in new issue
Block a user