From c896b684b0508250580dee198cf3ceac81658f5c Mon Sep 17 00:00:00 2001 From: swrring Date: Wed, 16 Sep 2026 09:49:01 +0900 Subject: [PATCH] =?UTF-8?q?[WL-816ze/zg]=20=EB=8D=B0=EB=AA=A8=20=EB=8F=99?= =?UTF-8?q?=EC=9D=BC=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20=EB=8C=80=EC=A1=B0?= =?UTF-8?q?=EB=A1=9C=20=ED=99=95=EC=A0=95=20=E2=80=94=20=ED=92=80=20=3D=20?= =?UTF-8?q?=EB=8D=B0=EB=AA=A8=20=EA=B7=9C=EC=B9=99(0.35=20=C2=B7=206.25/?= =?UTF-8?q?=E3=8E=A1=20=C2=B7=20=EA=B7=A0=EC=9D=BC=20=C2=B7=20=EA=B5=AC?= =?UTF-8?q?=EB=A6=84=EB=9D=A0/=EA=B2=BD=EA=B3=84=EB=9D=A0=20off=20=C2=B7?= =?UTF-8?q?=20=EB=B0=91=EB=8F=99=20=EC=96=B4=EB=91=A1=EA=B2=8C=20=C2=B7=20?= =?UTF-8?q?=EC=9E=8E=20=EC=9C=97=EB=A9=B4=20diffuse=20=C3=971.167=20?= =?UTF-8?q?=EB=A1=9C=20=ED=83=80=EC=9D=BC=EA=B3=BC=20=EB=8F=99=EC=9D=BC=20?= =?UTF-8?q?=E2=86=92=20=EC=9E=8E=20=EC=9C=A4=EA=B3=BD=20=EC=86=8C=EB=A9=B8?= =?UTF-8?q?=20sd=2011=E2=86=922.8)=20=C2=B7=20=ED=95=B4=EB=B3=80=20=3D=20?= =?UTF-8?q?=ED=92=80/=EB=AA=A8=EB=9E=98=20=EA=B2=BD=EA=B3=84=20=EB=93=B1?= =?UTF-8?q?=EA=B3=A0=EC=84=A0=20=EC=A0=88=EB=8B=A8(=EB=A7=88=EC=B9=AD=20?= =?UTF-8?q?=ED=8A=B8=EB=9D=BC=EC=9D=B4=EC=95=B5=EA=B8=80)=20+=20=EA=B5=BD?= =?UTF-8?q?=EC=9D=B4=20=EB=85=B8=EC=9D=B4=EC=A6=88=200.8=20m=20+=20?= =?UTF-8?q?=EB=AA=A8=EC=84=9C=EB=A6=AC=20=ED=95=84=EB=A0=9B=20R3(keep=20?= =?UTF-8?q?=E2=89=A5=20R)=20(#816)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 --- .../Look/Farm/Materials/Farm_Grass_Demo3.mat | 4 +- .../Resources/WL/WLIslandLookSettings.asset | 17 ++-- Assets/WL/Look/Farm/WLIslandLookSettings.cs | 6 ++ Assets/WL/Look/Farm/WLShoreSkirt.cs | 93 ++++++++++++++++--- 4 files changed, 100 insertions(+), 20 deletions(-) diff --git a/Assets/WL/Look/Farm/Materials/Farm_Grass_Demo3.mat b/Assets/WL/Look/Farm/Materials/Farm_Grass_Demo3.mat index e68cbbf57..d35745f06 100644 --- a/Assets/WL/Look/Farm/Materials/Farm_Grass_Demo3.mat +++ b/Assets/WL/Look/Farm/Materials/Farm_Grass_Demo3.mat @@ -136,9 +136,9 @@ Material: - _Cloud_Movement: {r: 1, g: 1, b: 0, a: 0} - _Cloud_Step: {r: 13, g: 17, b: 0, a: 0} - _Color: {r: 1, g: 1, b: 1, a: 1} - - _DiffuseColor: {r: 0.62, g: 0.82, b: 0.485, a: 0} + - _DiffuseColor: {r: 0.72, g: 0.957, b: 0.566, a: 0} - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - - _ShadowDiffuseColor: {r: 0.55, g: 0.75, b: 0.47, a: 0} + - _ShadowDiffuseColor: {r: 0.2509804, g: 0.3254902, b: 0.23529413, a: 0} - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} - _WindMovement: {r: 6, g: 0, b: 0, a: 0} m_BuildTextureStacks: [] diff --git a/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset b/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset index 07c19ebaf..1bc3fd36a 100644 --- a/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset +++ b/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset @@ -85,35 +85,38 @@ MonoBehaviour: mesh: {fileID: -4413095505993930501, guid: 1f298817fdd2a184480e8af5b21278bc, type: 3} material: {fileID: 2100000, guid: 3b82f6a1cd074e2f9a15d8e73c40b592, type: 2} probability: 100 - scale: 0.1 + scale: 0.35 normalOffset: 0.04 - density: 5 - edgeOnly_: 1 + density: 2.5 + edgeOnly_: 0 - enabled_: 1 label: "\uAF43" mesh: {fileID: -6327915695457451627, guid: dab2c84bf0b5ced4aaf0cffc4c491d55, type: 3} material: {fileID: 2100000, guid: d50a40cb47ec86444b898d9121bbe0f7, type: 2} probability: 1 - scale: 0.1 + scale: 0.35 normalOffset: 0.04 - density: 5 + density: 2.5 - enabled_: 1 label: "\uC790\uAC08" mesh: {fileID: 2928966540353291585, guid: 7f1f11ac2c783374b876f8dc2bb7c7db, type: 3} material: {fileID: 2100000, guid: 77d84437c100f274aad603219110357d, type: 2} probability: 1 - scale: 0.35 + scale: 0.7 normalOffset: 0.1 density: 0.05 positionVariance: 0.5 scaleVariance: 0.2 grassEdgeBand: 0 + cloudBandEnabled: 0 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.4 skirtInnerKeep: 0.5 skirtTopDrop: 0.01 + skirtCell: 0.5 + skirtEdgeJitter: 0.15 randomYaw: 0 edgeInset: 0.15 roadMargin: 0.15 @@ -123,7 +126,7 @@ MonoBehaviour: farmEdgeNoise: 0.4 farmEdgeNoiseCell: 0.5 outlineRoundEnabled: 1 - outlineRoundRadius: 3 + outlineRoundRadius: 1.5 outlineOverhang: 0 outlineRoundCell: 0.25 propMargin: 0.05 diff --git a/Assets/WL/Look/Farm/WLIslandLookSettings.cs b/Assets/WL/Look/Farm/WLIslandLookSettings.cs index db29e9a15..a8c45315f 100644 --- a/Assets/WL/Look/Farm/WLIslandLookSettings.cs +++ b/Assets/WL/Look/Farm/WLIslandLookSettings.cs @@ -371,6 +371,12 @@ namespace WL.Look.Farm "타일 윗면 위(+1 cm)에 모래를 얹는다. 풀 인스턴서 `outlineRoundRadius`(3)와 맞추면 풀잎도 같은 윤곽 안에만 깔린다. 0 = 각진 그대로(2026-09-16 PD 「각진 지형이 남는다」).")] public float skirtGrassRound = 3f; + [Tooltip("풀/모래 경계선을 굽이치게 하는 진폭(m). 직선 변을 파도처럼 들쭉날쭉하게(값 노이즈 · 결정적). 0 = 직선.")] + public float skirtWaveAmp = 0.8f; + + [Tooltip("굽이의 크기(m) — 노이즈 격자 한 칸. 클수록 완만한 큰 굽이.")] + public float skirtWaveCell = 2.5f; + // ───────────────────────────────── §1 풀밭 [Header("§1 — 섬 타일 위 풀밭 (데모와 같은 인스턴싱)")] [Tooltip("`edgeOnly_ 1` 정의를 경계에서 이만큼(m) 안쪽까지만 깐다. 0 = 띠 제한 없음. " + diff --git a/Assets/WL/Look/Farm/WLShoreSkirt.cs b/Assets/WL/Look/Farm/WLShoreSkirt.cs index 7b9c4754c..19d54b818 100644 --- a/Assets/WL/Look/Farm/WLShoreSkirt.cs +++ b/Assets/WL/Look/Farm/WLShoreSkirt.cs @@ -127,7 +127,8 @@ namespace WL.Look.Farm + 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 - + cfg.skirtToonMode * 53f + cfg.skirtEdgeJitter * 59f + cfg.skirtSandShadowMul * 61f + cfg.skirtGrassRound * 67f; + + cfg.skirtToonMode * 53f + cfg.skirtEdgeJitter * 59f + cfg.skirtSandShadowMul * 61f + cfg.skirtGrassRound * 67f + + cfg.skirtWaveAmp * 71f + cfg.skirtWaveCell * 73f; bool same = !_dirty && (_inst != null || _sandInst != null) && tileSig == _lastTileSig && Mathf.Abs(_lastValSig - valSig) < 0.0001f; if (same) { _mr.enabled = true; return; } @@ -204,6 +205,62 @@ namespace WL.Look.Farm return cfg.shoreFoamFallbackY; } + /// 격자 정수 좌표 → 0~1 결정적 해시(굽이 노이즈용). + static float Hash01(int x, int z) + { + uint h = (uint)(x * 73856093) ^ (uint)(z * 19349663) ^ 0x9E3779B9u; h ^= h >> 13; h *= 0x5bd1e995u; h ^= h >> 15; + return (h & 0xffffu) / 65535f; + } + + /// 경계 굽이 — 값 노이즈(격자 skirtWaveCell · 겹선형 · 결정적) × 진폭. 직선 변을 파도처럼 굽이치게 한다. + float Wave(float x, float z) + { + if (cfg.skirtWaveAmp <= 0f || cfg.skirtWaveCell <= 0.1f) return 0f; + float qx = x / cfg.skirtWaveCell, qz = z / cfg.skirtWaveCell; + int gx = Mathf.FloorToInt(qx), gz = Mathf.FloorToInt(qz); float fx = qx - gx, fz = qz - gz; + fx = fx * fx * (3f - 2f * fx); fz = fz * fz * (3f - 2f * fz); + float v00 = Hash01(gx, gz), v10 = Hash01(gx + 1, gz), v01 = Hash01(gx, gz + 1), v11 = Hash01(gx + 1, gz + 1); + return (Mathf.Lerp(Mathf.Lerp(v00, v10, fx), Mathf.Lerp(v01, v11, fx), fz) - 0.5f) * 2f * cfg.skirtWaveAmp; + } + + // ── 마칭 트라이앵글 — s 의 부호가 갈리는 삼각형을 등고선(s=0)으로 잘라 풀/모래 서브메시에 나눠 담는다 ── + float[] _s; + readonly Dictionary _cut = new Dictionary(); + + /// 노드 na/nb 사이 s=0 지점의 정점(위치·색 보간 · 변마다 한 번만 생성). + int CrossVertex(int va, int na, int vb, int nb) + { + long key = na < nb ? ((long)na << 32) | (uint)nb : ((long)nb << 32) | (uint)na; + int idx; + if (_cut.TryGetValue(key, out idx)) return idx; + float sa = _s[na], sb = _s[nb]; + float t = Mathf.Clamp01(sa / (sa - sb)); + idx = _verts.Count; + _verts.Add(Vector3.LerpUnclamped(_verts[va], _verts[vb], t)); + _cols.Add(Color32.Lerp(_cols[va], _cols[vb], t)); + _cut[key] = idx; + return idx; + } + + /// 삼각형(정점 va,vb,vc · 노드 na,nb,nc · 감김 유지)을 s 부호로 가른다. + void CutTri(int va, int na, int vb, int nb, int vc, int nc) + { + bool ga = _s[na] < 0f, gb = _s[nb] < 0f, gc = _s[nc] < 0f; + if (ga == gb && gb == gc) { var l = ga ? _tris : _trisSand; l.Add(va); l.Add(vb); l.Add(vc); return; } + // 홀로 다른 꼭짓점 o 와 나머지 p,q (a→b→c 순환 순서 유지) + int vo, no, vp, np, vq, nq; bool go; + if (ga != gb && ga != gc) { vo = va; no = na; vp = vb; np = nb; vq = vc; nq = nc; go = ga; } + else if (gb != ga && gb != gc) { vo = vb; no = nb; vp = vc; np = nc; vq = va; nq = na; go = gb; } + else { vo = vc; no = nc; vp = va; np = na; vq = vb; nq = nb; go = gc; } + int m1 = CrossVertex(vo, no, vp, np); + int m2 = CrossVertex(vo, no, vq, nq); + var lo = go ? _tris : _trisSand; // o 쪽 조각(삼각형 1) + var lr = go ? _trisSand : _tris; // 나머지 조각(사각형 = 삼각형 2) + lo.Add(vo); lo.Add(m1); lo.Add(m2); + lr.Add(m1); lr.Add(vp); lr.Add(vq); + lr.Add(m1); lr.Add(vq); lr.Add(m2); + } + /// 풀색 — 타일 윗면 머티리얼(Farm_IslandTop_Demo3)의 `_DiffuseColor`. 없으면 `_BaseColor`·`_Color` → 설정 대체값. Color GrassColor() { @@ -383,11 +440,12 @@ namespace WL.Look.Farm distR[n] = Mathf.Sqrt(best) - round; } } - keep = Mathf.Max(keep, round * 0.3f + cell); // 모서리가 파고드는 깊이 R(1−1/√2) ≈ 0.29R 까지 안쪽 노드 필요 + // 🔴 실측(2026-09-16 09:5x): 모서리 필렛은 변을 따라 R 까지 이어지는 곡선 삼각형(깊이 R) — keep 이 R 보다 작으면 직사각형으로 잘린다. + keep = Mathf.Max(keep, round + cell); } _verts.Clear(); _cols.Clear(); _tris.Clear(); _trisSand.Clear(); - float jitter = Mathf.Max(0f, cfg.skirtEdgeJitter); + float jitter = Mathf.Max(0f, cfg.skirtEdgeJitter); // 노드 단위 미세 흔들림(m) — 등고선을 살짝 거칠게 float outerKeep = width + cell * 2f; // 사각형 네 귀가 전부 있어야 하므로 바깥으로 두 칸 여유 for (int j = 0; j < nz; j++) { @@ -413,6 +471,20 @@ namespace WL.Look.Farm } } + // 2026-09-16 PD 「각진 경계 · 완만한 해변」 — 풀/모래 경계를 **격자 칸이 아니라 등고선으로 자른다**(칸 단위 계단 제거). + // 노드 값 s = 둥글린 거리 − 굽이(값 노이즈) − 풀 띠 (+ 미세 흔들림). s<0 = 풀 · s≥0 = 모래. 삼각형이 두 쪽에 걸치면 + // s=0 을 지나는 변 위에 정점을 보간해 만들고 세 조각으로 나눈다(마칭 트라이앵글). + if (_s == null || _s.Length < nx * nz) _s = new float[nx * nz]; + _cut.Clear(); + for (int j = 0; j < nz; j++) + for (int i = 0; i < nx; i++) + { + int n = j * nx + i; + if (index[n] < 0) { _s[n] = 0f; continue; } + float x = x0 + i * cell, z = z0 + j * cell; + _s[n] = distR[n] - Wave(x, z) - band + (Hash01(i * 3 + 7, j * 5 + 11) - 0.5f) * jitter; + } + for (int j = 0; j < nz - 1; j++) { for (int i = 0; i < nx - 1; i++) @@ -424,17 +496,16 @@ 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) 이 위를 향한다 - 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 = (distR[n] + distR[n + 1] + distR[n + nx] + distR[n + nx + 1]) * 0.25f; // 둥글린 거리로 가른다 - if (dq >= band + jit) list = _trisSand; + CutTri(ia, n, ic, n + nx, id, n + nx + 1); + CutTri(ia, n, id, n + nx + 1, ib, n + 1); + } + else + { + _tris.Add(ia); _tris.Add(ic); _tris.Add(id); + _tris.Add(ia); _tris.Add(id); _tris.Add(ib); } - list.Add(ia); list.Add(ic); list.Add(id); - list.Add(ia); list.Add(id); list.Add(ib); } }