Project_WL/AgentScripts/WL816n_Apply.cs

166 lines
9.0 KiB
C#
Raw Normal View History

// WL-816n — ① 기준 캡처(c_demo_vs_island_ground.png) 이미지 분석: 데모 풀 대비 · 경계 집중도
// ② 섬 바닥/풀 머티리얼 *_Arena 복사본 생성(구름 속도 감속) + SO 값 적용(밀도 = 데모 2.5)
// ③ 섬 전/후 렌더(PD 구도 12 m · 45° · fov 60)
public static class WL816n_Apply
{
const string TOP_D = "Assets/WL/Look/Farm/Materials/Farm_IslandTop_Demo.mat";
const string TOP_A = "Assets/WL/Look/Farm/Materials/Farm_IslandTop_Arena.mat";
const string GR_D = "Assets/WL/Look/Farm/Materials/Farm_Grass_Demo.mat";
const string GR_A = "Assets/WL/Look/Farm/Materials/Farm_Grass_Arena.mat";
const string SO_P = "Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset";
const int W = 540, H = 960;
static System.Text.StringBuilder sb = new System.Text.StringBuilder();
// ─────────────────────────────────────────── 1) 기준 이미지 분석
public static void Analyze()
{
sb.Length = 0;
var path = "Screenshots_WL/WL816f/c_demo_vs_island_ground.png";
var bytes = System.IO.File.ReadAllBytes(path);
var tex = new UnityEngine.Texture2D(2, 2, UnityEngine.TextureFormat.RGB24, false);
UnityEngine.ImageConversion.LoadImage(tex, bytes);
int w = tex.width, h = tex.height;
var px = tex.GetPixels32();
sb.AppendLine("기준 캡처 " + w + "x" + h + " (좌 = 데모 · 우 = 우리 섬)");
Half(px, w, h, 10, w / 2 - 12, 40, h - 60, "데모(좌)");
Half(px, w, h, w / 2 + 12, w - 10, 40, h - 60, "우리섬(우)");
Flush("AgentScripts/WL816n_IMG.txt");
UnityEngine.Object.DestroyImmediate(tex);
}
// 로컬 중앙값(=띠 색) 대비 어두운 이탈 픽셀 = 풀 실루엣. 띠 경계 근처 / 평면 내부로 갈라 센다.
static void Half(UnityEngine.Color32[] px, int w, int h, int x0, int x1, int y0, int y1, string tag)
{
int R = 9; // 로컬 창 반경(풀 포기보다 크게)
long tuft = 0, tot = 0, tuftNearEdge = 0, nearEdge = 0;
double dsum = 0;
long lr = 0, lg = 0, lb = 0; // 바닥(띠) 평균
long gr = 0, gg = 0, gb = 0; long gn = 0;
for (int y = y0 + R; y < y1 - R; y += 2)
for (int x = x0 + R; x < x1 - R; x += 2)
{
// 로컬 밝기 중앙값 근사 = 창 안 최대값 쪽 25% 평균(풀이 어두우므로 바닥이 밝은 쪽)
int mx = 0; long s = 0; int c = 0;
for (int dy = -R; dy <= R; dy += 3)
for (int dx = -R; dx <= R; dx += 3)
{
var p = px[(y + dy) * w + (x + dx)];
int L = p.r + p.g + p.b;
if (L > mx) mx = L;
s += L; c++;
}
int mean = (int)(s / c);
var q = px[y * w + x];
int lum = q.r + q.g + q.b;
tot++;
lr += q.r; lg += q.g; lb += q.b;
// 창 안 밝기 폭 = 띠 경계 지표(경계면 창 안 명암 차가 크다)
bool edgeZone = (mx - mean) > 14;
if (edgeZone) nearEdge++;
if (lum < mx - 18) // 바닥보다 뚜렷이 어두움 = 풀 실루엣
{
tuft++;
dsum += (double)lum / mx;
gr += q.r; gg += q.g; gb += q.b; gn++;
if (edgeZone) tuftNearEdge++;
}
}
double cov = 100.0 * tuft / System.Math.Max(1, tot);
double ne = 100.0 * nearEdge / System.Math.Max(1, tot);
double tne = 100.0 * tuftNearEdge / System.Math.Max(1, tuft);
sb.AppendLine(string.Format("{0}: 표본={1} 풀실루엣커버%={2:F2} 풀/바닥 밝기비={3:F3} 바닥평균=#{4:X2}{5:X2}{6:X2} 풀평균=#{7:X2}{8:X2}{9:X2}",
tag, tot, cov, gn == 0 ? 0 : dsum / gn, lr / System.Math.Max(1, tot), lg / System.Math.Max(1, tot), lb / System.Math.Max(1, tot),
gn == 0 ? 0 : gr / gn, gn == 0 ? 0 : gg / gn, gn == 0 ? 0 : gb / gn));
sb.AppendLine(string.Format(" 경계대 면적%={0:F2} · 풀 중 경계대 비율={1:F1}% (균일 배치면 면적%와 같아야 한다 → 초과분 = 경계 집중)",
ne, tne));
}
// ─────────────────────────────────────────── 2) 값 적용
public static void Apply() { MakeMats(); SetSO(); }
public static void MakeMats()
{
sb.Length = 0;
// 머티리얼 복사본
if (!System.IO.File.Exists(TOP_A)) UnityEditor.AssetDatabase.CopyAsset(TOP_D, TOP_A);
if (!System.IO.File.Exists(GR_A)) UnityEditor.AssetDatabase.CopyAsset(GR_D, GR_A);
UnityEditor.AssetDatabase.Refresh();
var top = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(TOP_A);
var gra = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(GR_A);
var topD = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(TOP_D);
var graD = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(GR_D);
// 구름 그림자 = 데모 띠의 원천(실측). 움직임만 5배 느리게 — 얼룩 모양·세기는 데모 그대로.
foreach (var m in new UnityEngine.Material[] { top, gra })
{
if (m == null) continue;
m.EnableKeyword("_CLOUDSENABLED");
m.SetFloat("_CLOUDSENABLED", 1f);
m.SetFloat("_Cloud_Change", 0.001f); // 0.005 → 0.001 (5배 감속)
m.SetVector("_Cloud_Movement", new UnityEngine.Vector4(0.2f, 0.2f, 0, 0)); // (1,1) → (0.2,0.2)
m.SetFloat("_Cloud_Strength", 1f);
m.SetFloat("_Cloud_Cover", 0.5f);
m.SetFloat("_Cloud_Density", 0.01f);
m.SetVector("_Cloud_Step", new UnityEngine.Vector4(13f, 17f, 0, 0));
}
// 풀은 바닥보다 살짝 진하게 (기준 이미지 실측 비율 적용 — Analyze 로그 참조)
if (gra != null && graD != null)
{
var d = graD.GetColor("_DiffuseColor");
var s = graD.GetColor("_ShadowDiffuseColor");
gra.SetColor("_DiffuseColor", Mul(d, GRASS_MUL));
gra.SetColor("_ShadowDiffuseColor", Mul(s, GRASS_MUL));
sb.AppendLine("풀색 " + Hex(d) + "/" + Hex(s) + " → " + Hex(Mul(d, GRASS_MUL)) + "/" + Hex(Mul(s, GRASS_MUL)) + " (x" + GRASS_MUL + ")");
}
UnityEditor.EditorUtility.SetDirty(top); UnityEditor.EditorUtility.SetDirty(gra);
UnityEditor.AssetDatabase.SaveAssets();
Flush("AgentScripts/WL816n_MATS.txt");
}
public static void SetSO()
{
sb.Length = 0;
var top = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(TOP_A);
var gra = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(GR_A);
var topD = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(TOP_D);
var graD = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(GR_D);
// SO 값
var so = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.ScriptableObject>(SO_P);
var sobj = new UnityEditor.SerializedObject(so);
var mr = sobj.FindProperty("materialRemap");
for (int i = 0; i < mr.arraySize; i++)
{
var to = mr.GetArrayElementAtIndex(i).FindPropertyRelative("to");
if (to.objectReferenceValue == topD) { to.objectReferenceValue = top; sb.AppendLine("materialRemap[" + i + "].to → Farm_IslandTop_Arena"); }
}
sobj.FindProperty("islandTopMaterial").objectReferenceValue = top;
var sc = sobj.FindProperty("scatter");
for (int i = 0; i < sc.arraySize; i++)
{
var e = sc.GetArrayElementAtIndex(i);
var mp = e.FindPropertyRelative("material");
if (mp.objectReferenceValue == graD) mp.objectReferenceValue = gra;
var dp = e.FindPropertyRelative("density");
var lb = e.FindPropertyRelative("label").stringValue;
if (dp.floatValue > 0.5f) { sb.AppendLine("scatter[" + i + "](" + lb + ") density " + dp.floatValue + " → 2.5 (데모 FirstLayer 값)"); dp.floatValue = 2.5f; }
}
sobj.ApplyModifiedPropertiesWithoutUndo();
UnityEditor.EditorUtility.SetDirty(so);
UnityEditor.AssetDatabase.SaveAssets();
sb.AppendLine("SO 저장 완료");
Flush("AgentScripts/WL816n_APPLY.txt");
}
public static float GRASS_MUL = 0.93f;
static UnityEngine.Color Mul(UnityEngine.Color c, float k) { return new UnityEngine.Color(c.r * k, c.g * k, c.b * k, c.a); }
static string Hex(UnityEngine.Color c) { return "#" + UnityEngine.ColorUtility.ToHtmlStringRGB(c); }
static void Flush(string p)
{
System.IO.File.WriteAllText(p, sb.ToString());
UnityEngine.Debug.Log("[816n]\n" + sb.ToString());
}
}