[WL-816zg] 스커트 Toon 2재질(풀=타일 재질 · 모래=복사본 · 구름·조명 동일 → 이음새 0) · 풀/모래 경계 칸별 흔들림 0.6 · 풀 띠 0.4 (#816)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
b8d839fbb1
commit
efa7a066ee
|
|
@ -110,7 +110,7 @@ MonoBehaviour:
|
|||
softDirtColor: {r: 0.71, g: 0.706, b: 0.549, a: 1}
|
||||
seabedDepth: 0.6
|
||||
seabedColor: {r: 0.86, g: 0.86, b: 0.72, a: 1}
|
||||
skirtGrassBand: 0
|
||||
skirtGrassBand: 0.4
|
||||
skirtInnerKeep: 0.5
|
||||
skirtTopDrop: 0.01
|
||||
randomYaw: 0
|
||||
|
|
|
|||
|
|
@ -356,6 +356,17 @@ namespace WL.Look.Farm
|
|||
[Tooltip("거리장·메시 격자 정점 수 상한. 넘으면 칸을 두 배씩 키운다(섬이 아무리 커져도 비용이 묶인다).")]
|
||||
public int skirtMaxGridNodes = 400000;
|
||||
|
||||
[Tooltip("1 = 스커트를 타일과 **같은 Toon 재질**로 그린다(풀 = islandTopMaterial 그대로 · 모래 = 그 복사본에 모래색) → " +
|
||||
"구름 그림자·조명이 타일과 똑같이 떨어져 이음새가 뜨지 않는다(2026-09-16 PD 「가장자리 경계가 붕 뜬다」 실측: 언릿 스커트가 구름 아래 타일보다 최대 15 % 밝았다). " +
|
||||
"0 = 전용 언릿 정점색 셰이더(그라데이션은 부드럽지만 구름을 못 받는다).")]
|
||||
public int skirtToonMode = 1;
|
||||
|
||||
[Tooltip("Toon 모드 모래 재질의 그늘색 배율(모래색 × 이 값) — 경사의 그늘진 면.")]
|
||||
public float skirtSandShadowMul = 0.72f;
|
||||
|
||||
[Tooltip("Toon 모드에서 풀/모래 경계선을 칸마다 흔드는 폭(m) — 직선이 아니라 들쭉날쭉한 물가. 0 = 직선.")]
|
||||
public float skirtEdgeJitter = 0.6f;
|
||||
|
||||
// ───────────────────────────────── §1 풀밭
|
||||
[Header("§1 — 섬 타일 위 풀밭 (데모와 같은 인스턴싱)")]
|
||||
[Tooltip("`edgeOnly_ 1` 정의를 경계에서 이만큼(m) 안쪽까지만 깐다. 0 = 띠 제한 없음. " +
|
||||
|
|
|
|||
|
|
@ -59,6 +59,10 @@ namespace WL.Look.Farm
|
|||
readonly List<Vector3> _verts = new List<Vector3>();
|
||||
readonly List<Color32> _cols = new List<Color32>();
|
||||
readonly List<int> _tris = new List<int>();
|
||||
readonly List<int> _trisSand = new List<int>(); // Toon 모드 — 모래 서브메시
|
||||
static readonly int IdShadowDiffuseColor = Shader.PropertyToID("_ShadowDiffuseColor");
|
||||
Material _sandInst; // Toon 모드 — 타일 재질 복사본(모래색)
|
||||
bool _toon;
|
||||
|
||||
// 마지막으로 만든 조건 — 같으면 다시 만들지 않는다(1초 폴링·재훑기에서 공짜로 돌게).
|
||||
bool _dirty = true;
|
||||
|
|
@ -71,6 +75,7 @@ namespace WL.Look.Farm
|
|||
void OnDestroy()
|
||||
{
|
||||
if (_inst != null) Destroy(_inst);
|
||||
if (_sandInst != null) Destroy(_sandInst);
|
||||
if (_mesh != null) Destroy(_mesh);
|
||||
}
|
||||
|
||||
|
|
@ -121,9 +126,10 @@ namespace WL.Look.Farm
|
|||
float valSig = cfg.skirtWidth * 7.1f + cfg.skirtBottomY * 13.3f + cfg.skirtGrassBand * 3.7f + cfg.skirtSandBlend * 29.3f
|
||||
+ cfg.skirtCell * 5.9f + cfg.skirtInnerKeep * 11.9f + cfg.skirtTopDrop * 17.7f + cfg.skirtUnderwaterMul * 31.7f
|
||||
+ cfg.skirtMaxGridNodes * 0.001f + topY * 37.3f + waterY * 41.1f
|
||||
+ grass.r * 101f + grass.g * 103f + grass.b * 107f + sand.r * 109f + sand.g * 113f + sand.b * 127f;
|
||||
+ grass.r * 101f + grass.g * 103f + grass.b * 107f + sand.r * 109f + sand.g * 113f + sand.b * 127f
|
||||
+ cfg.skirtToonMode * 53f + cfg.skirtEdgeJitter * 59f + cfg.skirtSandShadowMul * 61f;
|
||||
|
||||
bool same = !_dirty && _inst != null && tileSig == _lastTileSig && Mathf.Abs(_lastValSig - valSig) < 0.0001f;
|
||||
bool same = !_dirty && (_inst != null || _sandInst != null) && tileSig == _lastTileSig && Mathf.Abs(_lastValSig - valSig) < 0.0001f;
|
||||
if (same) { _mr.enabled = true; return; }
|
||||
|
||||
if (!EnsureMaterial()) { _mr.enabled = false; LastLog = "🔴 셰이더를 못 찾음(WL/Shore Skirt · URP Unlit 둘 다)"; return; }
|
||||
|
|
@ -131,7 +137,8 @@ namespace WL.Look.Farm
|
|||
BuildEdges(size, half);
|
||||
float cell = BuildMesh(half, topY, waterY, grass, sand);
|
||||
|
||||
_mr.sharedMaterial = _inst;
|
||||
if (_toon) _mr.sharedMaterials = new[] { cfg.islandTopMaterial, _sandInst }; // 풀 = 타일 재질 그대로 · 모래 = 복사본
|
||||
else _mr.sharedMaterial = _inst;
|
||||
_mr.enabled = true;
|
||||
_lastTileSig = tileSig; _lastValSig = valSig; _dirty = false;
|
||||
Rebuilds++;
|
||||
|
|
@ -142,8 +149,8 @@ namespace WL.Look.Farm
|
|||
+ (fixedTop ? "(고정 · 실측 " + (float.IsNaN(topMeasured) ? "없음" : topMeasured.ToString("F2"))
|
||||
: "(실측 · 고정값 " + cfg.skirtTopY.ToString("F2")) + ")"
|
||||
+ " → 바닥 " + cfg.skirtBottomY.ToString("F2") + " · 물 " + waterY.ToString("F2") + (waterFound ? "" : "(미발견·대체값)")
|
||||
+ " · 격자 " + cell.ToString("F2") + " m · 셰이더 " + (_vertexColor ? ShaderName : "대체 URP Unlit(정점색 없음 · 모래 단색)")
|
||||
+ " · 드로우콜 +1";
|
||||
+ " · 격자 " + cell.ToString("F2") + " m · 셰이더 " + (_toon ? "Toon 2재질(풀=타일 · 모래=복사본 · 구름 동일)" : _vertexColor ? ShaderName : "대체 URP Unlit(정점색 없음 · 모래 단색)")
|
||||
+ " · 드로우콜 +" + (_toon ? "2" : "1");
|
||||
cfg.Log(LastLog);
|
||||
}
|
||||
|
||||
|
|
@ -222,6 +229,20 @@ namespace WL.Look.Farm
|
|||
/// <summary>전용 셰이더(정점색) → 없으면 URP Unlit 모래 단색 대체. 에셋은 만들지 않는다.</summary>
|
||||
bool EnsureMaterial()
|
||||
{
|
||||
// 2026-09-16 Lead — Toon 모드: 타일과 같은 재질 계열이라 구름 그림자·조명이 똑같이 떨어진다(이음새 0).
|
||||
if (cfg.skirtToonMode != 0 && cfg.islandTopMaterial != null)
|
||||
{
|
||||
_toon = true; _vertexColor = false;
|
||||
if (_sandInst == null)
|
||||
_sandInst = new Material(cfg.islandTopMaterial) { name = "WL_ShoreSkirt_Sand(runtime)", hideFlags = HideFlags.DontSave };
|
||||
var sc = cfg.softDirtColor; sc.a = 1f;
|
||||
float sm = Mathf.Clamp01(cfg.skirtSandShadowMul);
|
||||
if (_sandInst.HasProperty(IdDiffuseColor)) _sandInst.SetColor(IdDiffuseColor, sc);
|
||||
if (_sandInst.HasProperty(IdShadowDiffuseColor)) _sandInst.SetColor(IdShadowDiffuseColor, new Color(sc.r * sm, sc.g * sm, sc.b * sm, 1f));
|
||||
if (_sandInst.HasProperty(IdBaseColor)) _sandInst.SetColor(IdBaseColor, sc);
|
||||
return true;
|
||||
}
|
||||
_toon = false;
|
||||
if (_inst != null) return true;
|
||||
|
||||
Shader sh = cfg.shoreSkirtMaterial != null ? cfg.shoreSkirtMaterial.shader : null;
|
||||
|
|
@ -338,7 +359,8 @@ namespace WL.Look.Farm
|
|||
for (int i = 0; i < nx; i++) dist[j * nx + i] = SignedDist(x0 + i * cell, z, half);
|
||||
}
|
||||
|
||||
_verts.Clear(); _cols.Clear(); _tris.Clear();
|
||||
_verts.Clear(); _cols.Clear(); _tris.Clear(); _trisSand.Clear();
|
||||
float jitter = Mathf.Max(0f, cfg.skirtEdgeJitter);
|
||||
float outerKeep = width + cell * 2f; // 사각형 네 귀가 전부 있어야 하므로 바깥으로 두 칸 여유
|
||||
for (int j = 0; j < nz; j++)
|
||||
{
|
||||
|
|
@ -374,8 +396,17 @@ namespace WL.Look.Farm
|
|||
float dm = Mathf.Min(Mathf.Min(dist[n], dist[n + 1]), Mathf.Min(dist[n + nx], dist[n + nx + 1]));
|
||||
if (dm >= width) continue;
|
||||
// 바닥판(SpawnSeabed)과 같은 감김 — (x0,z0)→(x0,z1)→(x1,z1) 이 위를 향한다
|
||||
_tris.Add(ia); _tris.Add(ic); _tris.Add(id);
|
||||
_tris.Add(ia); _tris.Add(id); _tris.Add(ib);
|
||||
var list = _tris;
|
||||
if (_toon)
|
||||
{
|
||||
// Toon 모드 — 풀/모래는 서브메시로 가른다. 문턱 = 풀 띠 + 칸별 흔들림(들쭉날쭉한 물가 · 결정적)
|
||||
uint h = (uint)(i * 73856093) ^ (uint)(j * 19349663); h ^= h >> 13; h *= 0x5bd1e995u; h ^= h >> 15;
|
||||
float jit = ((h & 0xffffu) / 65535f - 0.5f) * jitter;
|
||||
float dq = (dist[n] + dist[n + 1] + dist[n + nx] + dist[n + nx + 1]) * 0.25f;
|
||||
if (dq >= band + jit) list = _trisSand;
|
||||
}
|
||||
list.Add(ia); list.Add(ic); list.Add(id);
|
||||
list.Add(ia); list.Add(id); list.Add(ib);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -383,12 +414,14 @@ namespace WL.Look.Farm
|
|||
_mesh.indexFormat = _verts.Count > 65000 ? IndexFormat.UInt32 : IndexFormat.UInt16;
|
||||
_mesh.SetVertices(_verts);
|
||||
_mesh.SetColors(_cols);
|
||||
_mesh.subMeshCount = _toon ? 2 : 1;
|
||||
_mesh.SetTriangles(_tris, 0);
|
||||
_mesh.RecalculateNormals(); // DepthNormals 패스용
|
||||
if (_toon) _mesh.SetTriangles(_trisSand, 1);
|
||||
_mesh.RecalculateNormals(); // DepthNormals 패스용 · Toon 조명용
|
||||
_mesh.RecalculateBounds();
|
||||
|
||||
LastVertices = _verts.Count;
|
||||
LastTriangles = _tris.Count / 3;
|
||||
LastTriangles = (_tris.Count + _trisSand.Count) / 3;
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue