From 53bba8b5bd0eae8f85addcad0e9dbd64b29729ce Mon Sep 17 00:00:00 2001 From: swrring Date: Wed, 16 Sep 2026 09:11:22 +0900 Subject: [PATCH] =?UTF-8?q?[WL-816zg/zh]=20=ED=92=80=20=EA=B5=AC=EC=97=AD?= =?UTF-8?q?=20=EB=AA=A8=EC=84=9C=EB=A6=AC=20=EB=91=A5=EA=B8=80=EB=A6=BC(?= =?UTF-8?q?=EC=8A=A4=EC=BB=A4=ED=8A=B8=20=EC=97=B4=EA=B8=B0=20=EC=97=B0?= =?UTF-8?q?=EC=82=B0=20R3=20=C2=B7=20=ED=83=80=EC=9D=BC=20=EC=9C=84=20+1?= =?UTF-8?q?=20cm=20=EB=AA=A8=EB=9E=98)=20=C2=B7=20=ED=92=80=EC=9E=8E=20?= =?UTF-8?q?=EB=9D=A0=20=3D=20=EB=B3=B4=EC=9D=B4=EB=8A=94=20=EC=83=89=20?= =?UTF-8?q?=EA=B3=84=EB=8B=A8=EC=84=A0(ContourWeight=20=C2=B7=20cloudBandM?= =?UTF-8?q?ode=201)=20=C2=B7=20grassEdgeBand=200=20(#816)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 --- .../Resources/WL/WLIslandLookSettings.asset | 1 + Assets/WL/Look/Farm/WLCloudShadow.cs | 26 ++++++++++++++ Assets/WL/Look/Farm/WLIslandGrass.cs | 4 ++- Assets/WL/Look/Farm/WLIslandLookSettings.cs | 8 +++++ Assets/WL/Look/Farm/WLShoreSkirt.cs | 34 +++++++++++++++++-- 5 files changed, 69 insertions(+), 4 deletions(-) diff --git a/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset b/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset index 70b603569..07c19ebaf 100644 --- a/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset +++ b/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset @@ -107,6 +107,7 @@ MonoBehaviour: density: 0.05 positionVariance: 0.5 scaleVariance: 0.2 + grassEdgeBand: 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} diff --git a/Assets/WL/Look/Farm/WLCloudShadow.cs b/Assets/WL/Look/Farm/WLCloudShadow.cs index 2ddc12a95..916fcf5fc 100644 --- a/Assets/WL/Look/Farm/WLCloudShadow.cs +++ b/Assets/WL/Look/Farm/WLCloudShadow.cs @@ -393,6 +393,32 @@ namespace WL.Look.Farm return t * t * (3f - 2f * t); } + /// + /// 2026-09-16 PD 「데모는 그림자 그라데이션 **경계 지점**에 풀이 보인다 — 지금은 그림자와의 거리만으로 보인다」. + /// 화면에 실제로 보이는 경계는 ToonRamp 색 띠가 바뀌는 **계단선**(`Band` 가 바뀌는 곳)이다. 노이즈 0선(`BandWeight`)은 + /// 계단 사이 그라데이션의 중심이라 그림자 가장자리에서 떨어진 곳에 풀이 생겼다. + /// 계단선의 높이 문턱 T_m = (1 − (m − brightness)/shades)/strength (m = 1..top−1 · top 과 0 으로 뭉치는 칸은 경계가 없다) + /// 까지의 거리로 가중치를 준다: 가장 가까운 계단선 위 1 → width 만큼 떨어지면 0(smoothstep). + /// + public float ContourWeight(float wx, float wz, float width) + { + if (_strength <= 1e-6f) return 0f; + float lvl = LevelAt(wx, wz); + int top = Mathf.CeilToInt(_shades); + float best = float.MaxValue; + for (int m = 1; m < top; m++) + { + float T = (1f - (m - _brightness) / _shades) / _strength; + float d = Mathf.Abs(lvl - T); + if (d < best) best = d; + } + if (best == float.MaxValue) return 0f; + float t = 1f - best / (width > 1e-4f ? width : 1e-4f); + if (t <= 0f) return 0f; + if (t >= 1f) return 1f; + return t * t * (3f - 2f * t); + } + // ───────────────────────────────────────────────────────────── // ④ SimplexNoise3D.hlsl `snoise` 이식 (Ashima Arts · MIT · 원본 파일 0줄 수정) // ───────────────────────────────────────────────────────────── diff --git a/Assets/WL/Look/Farm/WLIslandGrass.cs b/Assets/WL/Look/Farm/WLIslandGrass.cs index f74f9748e..0fe50a975 100644 --- a/Assets/WL/Look/Farm/WLIslandGrass.cs +++ b/Assets/WL/Look/Farm/WLIslandGrass.cs @@ -534,7 +534,9 @@ namespace WL.Look.Farm for (int i = 0; i < _candCount; i++) { if (_cand[i].band != 0) continue; - float target = _field.BandWeight(_cand[i].x, _cand[i].z, width); + // 2026-09-16 PD — 기본은 화면에 보이는 색 계단선(ContourWeight) · 0 이면 노이즈 0선(BandWeight) + float target = cfg.cloudBandMode != 0 ? _field.ContourWeight(_cand[i].x, _cand[i].z, width) + : _field.BandWeight(_cand[i].x, _cand[i].z, width); _cand[i].w += (target - _cand[i].w) * k; } sw.Stop(); diff --git a/Assets/WL/Look/Farm/WLIslandLookSettings.cs b/Assets/WL/Look/Farm/WLIslandLookSettings.cs index db9085af6..db29e9a15 100644 --- a/Assets/WL/Look/Farm/WLIslandLookSettings.cs +++ b/Assets/WL/Look/Farm/WLIslandLookSettings.cs @@ -367,6 +367,10 @@ namespace WL.Look.Farm [Tooltip("Toon 모드에서 풀/모래 경계선을 칸마다 흔드는 폭(m) — 직선이 아니라 들쭉날쭉한 물가. 0 = 직선.")] public float skirtEdgeJitter = 0.6f; + [Tooltip("풀 구역 모서리 둥글림 반지름(m). 모래가 타일 모서리를 이 반지름으로 파고들어(열기 연산) 각진 타일 윤곽을 감춘다 — " + + "타일 윗면 위(+1 cm)에 모래를 얹는다. 풀 인스턴서 `outlineRoundRadius`(3)와 맞추면 풀잎도 같은 윤곽 안에만 깔린다. 0 = 각진 그대로(2026-09-16 PD 「각진 지형이 남는다」).")] + public float skirtGrassRound = 3f; + // ───────────────────────────────── §1 풀밭 [Header("§1 — 섬 타일 위 풀밭 (데모와 같은 인스턴싱)")] [Tooltip("`edgeOnly_ 1` 정의를 경계에서 이만큼(m) 안쪽까지만 깐다. 0 = 띠 제한 없음. " + @@ -386,6 +390,10 @@ namespace WL.Look.Farm "🔴 현 바닥 재질(_Shades 7 · _Brightness 0.25 · 강도 0.5)의 보이는 색 계단은 cover ± 0.143 에 있다 — 0.12 는 그 사이 그라데이션 중심 띠. 계단선까지 덮으려면 0.15~0.18.")] public float cloudBandWidth = 0.12f; + [Tooltip("띠를 어디에 둘 것인가. 1 = **화면에 보이는 색 계단선**(ToonRamp 띠가 바뀌는 문턱 · 데모처럼 그림자 가장자리에 풀 · 2026-09-16 PD) · " + + "0 = 노이즈 0선(cover · 계단 사이 그라데이션 중심 — 그림자에서 떨어져 보인다).")] + public int cloudBandMode = 1; + [Tooltip("띠 목표 배율을 다시 계산하고 인스턴스 버퍼를 다시 채우는 주기(초). 구름은 초당 약 1.4 m 흐른다(_Cloud_Movement 1,1). " + "비용 = 노이즈 표본(cloudEdgeSampleCell 간격 · 6 옥타브) + 후보 전수 겹선형 읽기 + 버퍼 재생성, 이 주기마다 1회.")] public float cloudBandRefreshSeconds = 0.2f; diff --git a/Assets/WL/Look/Farm/WLShoreSkirt.cs b/Assets/WL/Look/Farm/WLShoreSkirt.cs index 47562eebe..7b9c4754c 100644 --- a/Assets/WL/Look/Farm/WLShoreSkirt.cs +++ b/Assets/WL/Look/Farm/WLShoreSkirt.cs @@ -127,7 +127,7 @@ 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.skirtToonMode * 53f + cfg.skirtEdgeJitter * 59f + cfg.skirtSandShadowMul * 61f + cfg.skirtGrassRound * 67f; bool same = !_dirty && (_inst != null || _sandInst != null) && tileSig == _lastTileSig && Mathf.Abs(_lastValSig - valSig) < 0.0001f; if (same) { _mr.enabled = true; return; } @@ -359,6 +359,33 @@ namespace WL.Look.Farm for (int i = 0; i < nx; i++) dist[j * nx + i] = SignedDist(x0 + i * cell, z, half); } + // 2026-09-16 PD 「각진 지형이 남는다」 — 풀 구역 모서리를 R 로 둥글린다(열기 연산 = 침식 E{d ≤ −R} 뒤 팽창): + // distR = (E 까지의 거리) − R. 직선 변에서는 d 와 같고, 볼록 모서리에서는 반지름 R 원호가 된다. + // E 의 테두리 띠(d ∈ [−R − 1.5칸, −R]) 점만 모아 최소 거리를 재므로 비용은 (노드 수 × 띠 점 수). + float round = Mathf.Max(0f, cfg.skirtGrassRound); + var distR = dist; + if (round > 0.01f) + { + distR = new float[nx * nz]; + var ringX = new List(512); var ringZ = new List(512); + float lo = -round - cell * 1.5f, hi = -round; + for (int j = 0; j < nz; j++) for (int i = 0; i < nx; i++) { float dv = dist[j * nx + i]; if (dv <= hi && dv >= lo) { ringX.Add(x0 + i * cell); ringZ.Add(z0 + j * cell); } } + for (int j = 0; j < nz; j++) + { + float z = z0 + j * cell; + for (int i = 0; i < nx; i++) + { + int n = j * nx + i; float dv = dist[n]; + if (ringX.Count == 0) { distR[n] = dv; continue; } // 섬이 2R 보다 작으면 둥글림 없음 + if (dv <= hi) { distR[n] = dv + round; continue; } // E 안쪽 + float x = x0 + i * cell; float best = float.MaxValue; + for (int k = 0; k < ringX.Count; k++) { float dx = x - ringX[k], dz = z - ringZ[k]; float d2 = dx * dx + dz * dz; if (d2 < best) best = d2; } + distR[n] = Mathf.Sqrt(best) - round; + } + } + keep = Mathf.Max(keep, round * 0.3f + cell); // 모서리가 파고드는 깊이 R(1−1/√2) ≈ 0.29R 까지 안쪽 노드 필요 + } + _verts.Clear(); _cols.Clear(); _tris.Clear(); _trisSand.Clear(); float jitter = Mathf.Max(0f, cfg.skirtEdgeJitter); float outerKeep = width + cell * 2f; // 사각형 네 귀가 전부 있어야 하므로 바깥으로 두 칸 여유 @@ -372,7 +399,8 @@ namespace WL.Look.Farm float t = Mathf.Clamp01(d / width); t = t * t * (3f - 2f * t); // smoothstep — 윗면에서도 바닥에서도 접선이 수평(완만) - float y = top + (bottom - top) * t; + // 타일 안(d<0)은 윗면 **위** 1 cm — 풀 서브메시는 타일 재질 그대로라 보이지 않고, 모래 서브메시가 모서리 위에 얹힌다(홈·립도 덮는다) + float y = (_toon && d < 0f) ? topY + 0.01f : top + (bottom - top) * t; index[n] = _verts.Count; _verts.Add(new Vector3(x0 + i * cell, y, z0 + j * cell)); @@ -402,7 +430,7 @@ namespace WL.Look.Farm // 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; + float dq = (distR[n] + distR[n + 1] + distR[n + nx] + distR[n + nx + 1]) * 0.25f; // 둥글린 거리로 가른다 if (dq >= band + jit) list = _trisSand; } list.Add(ia); list.Add(ic); list.Add(id);