[WL-816ze/zg] 데모 동일 카메라 대조로 확정 — 풀 = 데모 규칙(0.35 · 6.25/㎡ · 균일 · 구름띠/경계띠 off · 밑동 어둡게 · 잎 윗면 diffuse ×1.167 로 타일과 동일 → 잎 윤곽 소멸 sd 11→2.8) · 해변 = 풀/모래 경계 등고선 절단(마칭 트라이앵글) + 굽이 노이즈 0.8 m + 모서리 필렛 R3(keep ≥ R) (#816)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
53bba8b5bd
commit
c896b684b0
|
|
@ -136,9 +136,9 @@ Material:
|
||||||
- _Cloud_Movement: {r: 1, g: 1, b: 0, a: 0}
|
- _Cloud_Movement: {r: 1, g: 1, b: 0, a: 0}
|
||||||
- _Cloud_Step: {r: 13, g: 17, b: 0, a: 0}
|
- _Cloud_Step: {r: 13, g: 17, b: 0, a: 0}
|
||||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
- _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}
|
- _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}
|
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||||
- _WindMovement: {r: 6, g: 0, b: 0, a: 0}
|
- _WindMovement: {r: 6, g: 0, b: 0, a: 0}
|
||||||
m_BuildTextureStacks: []
|
m_BuildTextureStacks: []
|
||||||
|
|
|
||||||
|
|
@ -85,35 +85,38 @@ MonoBehaviour:
|
||||||
mesh: {fileID: -4413095505993930501, guid: 1f298817fdd2a184480e8af5b21278bc, type: 3}
|
mesh: {fileID: -4413095505993930501, guid: 1f298817fdd2a184480e8af5b21278bc, type: 3}
|
||||||
material: {fileID: 2100000, guid: 3b82f6a1cd074e2f9a15d8e73c40b592, type: 2}
|
material: {fileID: 2100000, guid: 3b82f6a1cd074e2f9a15d8e73c40b592, type: 2}
|
||||||
probability: 100
|
probability: 100
|
||||||
scale: 0.1
|
scale: 0.35
|
||||||
normalOffset: 0.04
|
normalOffset: 0.04
|
||||||
density: 5
|
density: 2.5
|
||||||
edgeOnly_: 1
|
edgeOnly_: 0
|
||||||
- enabled_: 1
|
- enabled_: 1
|
||||||
label: "\uAF43"
|
label: "\uAF43"
|
||||||
mesh: {fileID: -6327915695457451627, guid: dab2c84bf0b5ced4aaf0cffc4c491d55, type: 3}
|
mesh: {fileID: -6327915695457451627, guid: dab2c84bf0b5ced4aaf0cffc4c491d55, type: 3}
|
||||||
material: {fileID: 2100000, guid: d50a40cb47ec86444b898d9121bbe0f7, type: 2}
|
material: {fileID: 2100000, guid: d50a40cb47ec86444b898d9121bbe0f7, type: 2}
|
||||||
probability: 1
|
probability: 1
|
||||||
scale: 0.1
|
scale: 0.35
|
||||||
normalOffset: 0.04
|
normalOffset: 0.04
|
||||||
density: 5
|
density: 2.5
|
||||||
- enabled_: 1
|
- enabled_: 1
|
||||||
label: "\uC790\uAC08"
|
label: "\uC790\uAC08"
|
||||||
mesh: {fileID: 2928966540353291585, guid: 7f1f11ac2c783374b876f8dc2bb7c7db, type: 3}
|
mesh: {fileID: 2928966540353291585, guid: 7f1f11ac2c783374b876f8dc2bb7c7db, type: 3}
|
||||||
material: {fileID: 2100000, guid: 77d84437c100f274aad603219110357d, type: 2}
|
material: {fileID: 2100000, guid: 77d84437c100f274aad603219110357d, type: 2}
|
||||||
probability: 1
|
probability: 1
|
||||||
scale: 0.35
|
scale: 0.7
|
||||||
normalOffset: 0.1
|
normalOffset: 0.1
|
||||||
density: 0.05
|
density: 0.05
|
||||||
positionVariance: 0.5
|
positionVariance: 0.5
|
||||||
scaleVariance: 0.2
|
scaleVariance: 0.2
|
||||||
grassEdgeBand: 0
|
grassEdgeBand: 0
|
||||||
|
cloudBandEnabled: 0
|
||||||
softDirtColor: {r: 0.71, g: 0.706, b: 0.549, a: 1}
|
softDirtColor: {r: 0.71, g: 0.706, b: 0.549, a: 1}
|
||||||
seabedDepth: 0.6
|
seabedDepth: 0.6
|
||||||
seabedColor: {r: 0.86, g: 0.86, b: 0.72, a: 1}
|
seabedColor: {r: 0.86, g: 0.86, b: 0.72, a: 1}
|
||||||
skirtGrassBand: 0.4
|
skirtGrassBand: 0.4
|
||||||
skirtInnerKeep: 0.5
|
skirtInnerKeep: 0.5
|
||||||
skirtTopDrop: 0.01
|
skirtTopDrop: 0.01
|
||||||
|
skirtCell: 0.5
|
||||||
|
skirtEdgeJitter: 0.15
|
||||||
randomYaw: 0
|
randomYaw: 0
|
||||||
edgeInset: 0.15
|
edgeInset: 0.15
|
||||||
roadMargin: 0.15
|
roadMargin: 0.15
|
||||||
|
|
@ -123,7 +126,7 @@ MonoBehaviour:
|
||||||
farmEdgeNoise: 0.4
|
farmEdgeNoise: 0.4
|
||||||
farmEdgeNoiseCell: 0.5
|
farmEdgeNoiseCell: 0.5
|
||||||
outlineRoundEnabled: 1
|
outlineRoundEnabled: 1
|
||||||
outlineRoundRadius: 3
|
outlineRoundRadius: 1.5
|
||||||
outlineOverhang: 0
|
outlineOverhang: 0
|
||||||
outlineRoundCell: 0.25
|
outlineRoundCell: 0.25
|
||||||
propMargin: 0.05
|
propMargin: 0.05
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,12 @@ namespace WL.Look.Farm
|
||||||
"타일 윗면 위(+1 cm)에 모래를 얹는다. 풀 인스턴서 `outlineRoundRadius`(3)와 맞추면 풀잎도 같은 윤곽 안에만 깔린다. 0 = 각진 그대로(2026-09-16 PD 「각진 지형이 남는다」).")]
|
"타일 윗면 위(+1 cm)에 모래를 얹는다. 풀 인스턴서 `outlineRoundRadius`(3)와 맞추면 풀잎도 같은 윤곽 안에만 깔린다. 0 = 각진 그대로(2026-09-16 PD 「각진 지형이 남는다」).")]
|
||||||
public float skirtGrassRound = 3f;
|
public float skirtGrassRound = 3f;
|
||||||
|
|
||||||
|
[Tooltip("풀/모래 경계선을 굽이치게 하는 진폭(m). 직선 변을 파도처럼 들쭉날쭉하게(값 노이즈 · 결정적). 0 = 직선.")]
|
||||||
|
public float skirtWaveAmp = 0.8f;
|
||||||
|
|
||||||
|
[Tooltip("굽이의 크기(m) — 노이즈 격자 한 칸. 클수록 완만한 큰 굽이.")]
|
||||||
|
public float skirtWaveCell = 2.5f;
|
||||||
|
|
||||||
// ───────────────────────────────── §1 풀밭
|
// ───────────────────────────────── §1 풀밭
|
||||||
[Header("§1 — 섬 타일 위 풀밭 (데모와 같은 인스턴싱)")]
|
[Header("§1 — 섬 타일 위 풀밭 (데모와 같은 인스턴싱)")]
|
||||||
[Tooltip("`edgeOnly_ 1` 정의를 경계에서 이만큼(m) 안쪽까지만 깐다. 0 = 띠 제한 없음. " +
|
[Tooltip("`edgeOnly_ 1` 정의를 경계에서 이만큼(m) 안쪽까지만 깐다. 0 = 띠 제한 없음. " +
|
||||||
|
|
|
||||||
|
|
@ -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.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
|
+ 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 + 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;
|
bool same = !_dirty && (_inst != null || _sandInst != null) && tileSig == _lastTileSig && Mathf.Abs(_lastValSig - valSig) < 0.0001f;
|
||||||
if (same) { _mr.enabled = true; return; }
|
if (same) { _mr.enabled = true; return; }
|
||||||
|
|
@ -204,6 +205,62 @@ namespace WL.Look.Farm
|
||||||
return cfg.shoreFoamFallbackY;
|
return cfg.shoreFoamFallbackY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>격자 정수 좌표 → 0~1 결정적 해시(굽이 노이즈용).</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>경계 굽이 — 값 노이즈(격자 skirtWaveCell · 겹선형 · 결정적) × 진폭. 직선 변을 파도처럼 굽이치게 한다.</summary>
|
||||||
|
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<long, int> _cut = new Dictionary<long, int>();
|
||||||
|
|
||||||
|
/// <summary>노드 na/nb 사이 s=0 지점의 정점(위치·색 보간 · 변마다 한 번만 생성).</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>삼각형(정점 va,vb,vc · 노드 na,nb,nc · 감김 유지)을 s 부호로 가른다.</summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>풀색 — 타일 윗면 머티리얼(Farm_IslandTop_Demo3)의 `_DiffuseColor`. 없으면 `_BaseColor`·`_Color` → 설정 대체값.</summary>
|
/// <summary>풀색 — 타일 윗면 머티리얼(Farm_IslandTop_Demo3)의 `_DiffuseColor`. 없으면 `_BaseColor`·`_Color` → 설정 대체값.</summary>
|
||||||
Color GrassColor()
|
Color GrassColor()
|
||||||
{
|
{
|
||||||
|
|
@ -383,11 +440,12 @@ namespace WL.Look.Farm
|
||||||
distR[n] = Mathf.Sqrt(best) - round;
|
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();
|
_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; // 사각형 네 귀가 전부 있어야 하므로 바깥으로 두 칸 여유
|
float outerKeep = width + cell * 2f; // 사각형 네 귀가 전부 있어야 하므로 바깥으로 두 칸 여유
|
||||||
for (int j = 0; j < nz; j++)
|
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 j = 0; j < nz - 1; j++)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < nx - 1; i++)
|
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]));
|
float dm = Mathf.Min(Mathf.Min(dist[n], dist[n + 1]), Mathf.Min(dist[n + nx], dist[n + nx + 1]));
|
||||||
if (dm >= width) continue;
|
if (dm >= width) continue;
|
||||||
// 바닥판(SpawnSeabed)과 같은 감김 — (x0,z0)→(x0,z1)→(x1,z1) 이 위를 향한다
|
// 바닥판(SpawnSeabed)과 같은 감김 — (x0,z0)→(x0,z1)→(x1,z1) 이 위를 향한다
|
||||||
var list = _tris;
|
|
||||||
if (_toon)
|
if (_toon)
|
||||||
{
|
{
|
||||||
// Toon 모드 — 풀/모래는 서브메시로 가른다. 문턱 = 풀 띠 + 칸별 흔들림(들쭉날쭉한 물가 · 결정적)
|
CutTri(ia, n, ic, n + nx, id, n + nx + 1);
|
||||||
uint h = (uint)(i * 73856093) ^ (uint)(j * 19349663); h ^= h >> 13; h *= 0x5bd1e995u; h ^= h >> 15;
|
CutTri(ia, n, id, n + nx + 1, ib, n + 1);
|
||||||
float jit = ((h & 0xffffu) / 65535f - 0.5f) * jitter;
|
}
|
||||||
float dq = (distR[n] + distR[n + 1] + distR[n + nx] + distR[n + nx + 1]) * 0.25f; // 둥글린 거리로 가른다
|
else
|
||||||
if (dq >= band + jit) list = _trisSand;
|
{
|
||||||
|
_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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue