diff --git a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset
index ba30bc8a7..35a91f946 100644
--- a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset
+++ b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset
@@ -103,6 +103,7 @@ MonoBehaviour:
camDistance: 22
islandCamDistance: 12
islandVcamName: PlayerVirtualCamera
+ islandCamPitch: 50
dungeonCamDistance: 11
camFov: 60
camLookHeight: 0
diff --git a/Assets/WL/Island/WLIslandSceneSetup.cs b/Assets/WL/Island/WLIslandSceneSetup.cs
index bca0356d5..61018061a 100644
--- a/Assets/WL/Island/WLIslandSceneSetup.cs
+++ b/Assets/WL/Island/WLIslandSceneSetup.cs
@@ -138,8 +138,16 @@ namespace WL.Island
float was = ft.m_CameraDistance;
ft.m_CameraDistance = cfg.islandCamDistance;
IslandCamApplied++;
+ string pitchLog = "";
+ if (cfg.islandCamPitch > 0f)
+ {
+ // 2026-09-15 PD 「카메라 각도 60도로 비교」 — vcam 은 Aim 없이 고정 회전이라 트랜스폼 회전의 X 만 바꾼다(씬 값 45).
+ var e = v.transform.eulerAngles;
+ v.transform.rotation = Quaternion.Euler(cfg.islandCamPitch, e.y, 0f);
+ pitchLog = " · 각도 " + e.x.ToString("F0") + " → " + cfg.islandCamPitch.ToString("F0") + "°";
+ }
WLIslandBridge.Log(cfg, "섬 카메라 거리 — " + v.name + " " + was.ToString("F1") + " → " +
- cfg.islandCamDistance.ToString("F1") + " m");
+ cfg.islandCamDistance.ToString("F1") + " m" + pitchLog);
}
}
diff --git a/Assets/WL/Island/WLIslandSettings.cs b/Assets/WL/Island/WLIslandSettings.cs
index 2a628e5d9..bddfa6a2b 100644
--- a/Assets/WL/Island/WLIslandSettings.cs
+++ b/Assets/WL/Island/WLIslandSettings.cs
@@ -478,6 +478,10 @@ namespace WL.Island
[Tooltip("위 섬 거리를 적용할 FI 가상 카메라 이름. 비우면 섬 씬의 FramingTransposer 전부.")]
public string islandVcamName = "PlayerVirtualCamera";
+ [Tooltip("섬 카메라 내려다보는 각도(도). 씬 값 45 · 데모 캡처는 거의 수직(≈90). 0 이하면 무동작(= 씬 값 45 그대로). " +
+ "2026-09-15 PD 「60도로 올려서 비교」.")]
+ public float islandCamPitch = 0f;
+
[Tooltip("🔴 **던전 전용** 카메라 거리(m) — WL-816i2 · PD 「던전에서는 카메라를 조금만 더 당기자」. " +
"0 이하면 위 섬 값(22)을 그대로 쓴다. 각도 45°·yaw 0°·FOV 60 은 섬과 같게 유지하고 **거리만** 줄인다. " +
"섬은 이 값에 전혀 영향받지 않는다.")]
diff --git a/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset b/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset
index ba4ea111b..4963f3c31 100644
--- a/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset
+++ b/Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset
@@ -88,6 +88,7 @@ MonoBehaviour:
scale: 0.1
normalOffset: 0.04
density: 5
+ edgeOnly_: 1
- enabled_: 1
label: "\uAF43"
mesh: {fileID: -6327915695457451627, guid: dab2c84bf0b5ced4aaf0cffc4c491d55, type: 3}
diff --git a/Assets/WL/Look/Farm/WLIslandGrass.cs b/Assets/WL/Look/Farm/WLIslandGrass.cs
index 6ecbdd788..61d9969c3 100644
--- a/Assets/WL/Look/Farm/WLIslandGrass.cs
+++ b/Assets/WL/Look/Farm/WLIslandGrass.cs
@@ -314,6 +314,9 @@ namespace WL.Look.Farm
if (r <= acc) { pick = d; break; }
}
+ // 2026-09-15 PD 「그라데이션과 무관하게 풀이 얼룩덜룩」 — 경계 전용 정의는 경계 띠 밖(들판 한가운데)에는 깔지 않는다.
+ if (cfg.grassEdgeBand > 0f && defs[pick].edgeOnly_ != 0 && !NearBoundary(tiles, wx, wz, cfg.grassEdgeBand)) { Excluded++; continue; }
+
float sc = 1f + (Frac(h, 3) - 0.5f) * 2f * sv;
float yaw = cfg.randomYaw != 0 ? Frac(h, 4) * 360f : 0f;
@@ -799,6 +802,23 @@ namespace WL.Look.Farm
}
}
+ ///
+ /// 점 둘레 `band` m 안에 경계(섬 윤곽 밖·타일 밖·소품/흙 제외)가 있는가 — 8방향 표본.
+ /// 경계 전용 풀잎(`edgeOnly_`)은 여기서 true 인 곳에만 깔린다(들판 안쪽 = 깔지 않음).
+ ///
+ bool NearBoundary(List tiles, float x, float z, float band)
+ {
+ for (int k = 0; k < 8; k++)
+ {
+ float a = k * (Mathf.PI * 0.25f);
+ float sx = x + Mathf.Cos(a) * band, sz = z + Mathf.Sin(a) * band;
+ if (_outOn) { if (!InRoundOutline(sx, sz)) return true; }
+ else if (OwnerTile(tiles, sx, sz) < 0) return true;
+ if (Blocked(sx, sz)) return true;
+ }
+ return false;
+ }
+
bool Blocked(float x, float z)
{
for (int i = 0; i < _blockers.Count; i++)
diff --git a/Assets/WL/Look/Farm/WLIslandLookSettings.cs b/Assets/WL/Look/Farm/WLIslandLookSettings.cs
index a0bc3f118..01cb6fd39 100644
--- a/Assets/WL/Look/Farm/WLIslandLookSettings.cs
+++ b/Assets/WL/Look/Farm/WLIslandLookSettings.cs
@@ -86,6 +86,8 @@ namespace WL.Look.Farm
[Tooltip("법선 방향으로 띄우는 거리(m). 데모 = 0.04.")] public float normalOffset = 0.04f;
[Tooltip("이 층의 밀도(개/m 한 변). 실제 밀도 = 값². 데모 1층 = 2.5(6.25개/㎡).")]
public float density = 2.5f;
+ [Tooltip("1 = 이 정의는 경계(섬 윤곽·흙 경계·소품) 근처 `grassEdgeBand` 띠에만 깐다 — 들판 한가운데의 얼룩덜룩함 제거(2026-09-15 PD). 0 = 어디나.")]
+ public int edgeOnly_ = 0;
}
[CreateAssetMenu(fileName = "WLIslandLookSettings", menuName = "WL/Island Look Settings", order = 39)]
@@ -308,6 +310,10 @@ namespace WL.Look.Farm
// ───────────────────────────────── §1 풀밭
[Header("§1 — 섬 타일 위 풀밭 (데모와 같은 인스턴싱)")]
+ [Tooltip("`edgeOnly_ 1` 정의를 경계에서 이만큼(m) 안쪽까지만 깐다. 0 = 띠 제한 없음. " +
+ "2026-09-15 PD 「그라데이션과 무관하게 풀이 얼룩덜룩」 — 들판 안쪽 풀잎은 같은 색이라도 미세한 음영 차로 얼룩이 되므로 경계 실루엣만 남긴다.")]
+ public float grassEdgeBand = 1.2f;
+
[Tooltip("0 이면 풀을 깔지 않는다.")]
public int grassEnabled = 1;