Project_WL/AgentScripts/WL816q_Verify.cs

237 lines
14 KiB
C#
Raw Permalink Normal View History

// WL-816q 검증 — 전/후 · 예산 · 0/2/4 s 좌표 불변(순간이동 0). 에셋 무변경(런타임 복제본).
public static class WL816q_Verify
{
public static void Start()
{
var go = UnityEngine.GameObject.Find("~WL816qVerify");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816qVerify");
go.AddComponent<WL816q_VerifyRunner>();
}
}
public class WL816q_VerifyRunner : UnityEngine.MonoBehaviour
{
static System.Text.StringBuilder sb;
const int W = 1080, H = 1920, TS = 768;
static string Dir = "Screenshots_WL/WL816q";
// 816n 이 쓰던 값 = 「전」
static readonly UnityEngine.Color BeforeDif = new UnityEngine.Color(0.60541177f, 0.82788235f, 0.5798824f, 1f);
static readonly UnityEngine.Color BeforeShd = new UnityEngine.Color(0.5434118f, 0.744f, 0.5215294f, 1f);
void Start() { StartCoroutine(Co()); }
System.Collections.IEnumerator Co()
{
sb = new System.Text.StringBuilder();
System.IO.Directory.CreateDirectory(Dir);
if (UnityEngine.SceneManagement.SceneManager.sceneCount < 2)
{
var op = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Level01", UnityEngine.SceneManagement.LoadSceneMode.Additive);
while (op != null && !op.isDone) yield return null;
}
for (int i = 0; i < 6; i++) yield return new UnityEngine.WaitForSeconds(1f);
var g = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLIslandGrass>(UnityEngine.FindObjectsInactive.Include);
if (g == null) { L("grass 없음"); Flush(); yield break; }
var rt = UnityEngine.Object.Instantiate(WL.Look.Farm.WLIslandLookSettings.Instance);
var arr = new WL.Look.Farm.WLScatterDef[rt.scatter.Length];
for (int i = 0; i < arr.Length; i++)
{
var s = rt.scatter[i];
arr[i] = new WL.Look.Farm.WLScatterDef { enabled_ = s.enabled_, label = s.label, mesh = s.mesh, material = s.material,
probability = s.probability, scale = s.scale, normalOffset = s.normalOffset, density = s.density };
}
rt.scatter = arr; g.cfg = rt;
var baseMat = arr[0].material;
var clone = new UnityEngine.Material(baseMat); clone.name = baseMat.name + "_verify";
for (int i = 0; i < arr.Length; i++) if (arr[i].material == baseMat) arr[i].material = clone;
L("풀 머티리얼 채택값 Dif=" + baseMat.GetColor("_DiffuseColor") + " Shd=" + baseMat.GetColor("_ShadowDiffuseColor")
+ " Change=" + baseMat.GetFloat("_Cloud_Change") + " Move=" + baseMat.GetVector("_Cloud_Movement"));
var b = g.CalculateInstancesBounds();
var camTop = MakeCam("~816qTop", true, b.center.x, b.center.z, 6f);
var camPd = MakeCam("~816qPd", false, 0, 0, 0);
L("섬 " + b.center + " " + b.size + " · 탑다운 ortho6 · PD 12m/45°/fov60");
UnityEngine.Time.timeScale = 0f; yield return null;
// ── 전 ────────────────────────────────────────────────────────
SetGrass(clone, BeforeDif, BeforeShd);
g.viewCameraOverride = camTop; rt.viewCullEnabled = 0; g.Rebuild(); yield return null; yield return null;
L("[전 top] " + Shot(camTop, TS, TS, "iter1_before_top"));
g.viewCameraOverride = camPd; g.Rebuild(); yield return null; yield return null;
L("[전 PD ] " + Shot(camPd, W, H, "iter1_before_pd") + " | " + Stat() + " | " + Bench(camPd, 60).ToString("F3") + " ms/frame");
// ── 후 ────────────────────────────────────────────────────────
SetGrass(clone, baseMat.GetColor("_DiffuseColor"), baseMat.GetColor("_ShadowDiffuseColor"));
g.viewCameraOverride = camTop; rt.viewCullEnabled = 0; g.Rebuild(); yield return null; yield return null;
L("[후 top 범위off] " + Shot(camTop, TS, TS, "iter1_after_top"));
g.viewCameraOverride = camPd; g.Rebuild(); yield return null; yield return null;
L("[후 PD 범위off] " + Shot(camPd, W, H, "iter1_after_pd_nocull") + " | " + Stat() + " | " + Bench(camPd, 60).ToString("F3") + " ms/frame");
rt.viewCullEnabled = 1; g.Rebuild(); yield return null; yield return null;
L("[후 PD 범위on ] " + Shot(camPd, W, H, "iter1_after_pd") + " | " + Stat() + " | " + Bench(camPd, 60).ToString("F3") + " ms/frame");
L(" " + WL.Look.Farm.WLIslandGrass.LastLog);
// ── 띠 안쪽 vs 가장자리 ───────────────────────────────────────
rt.grassEnabled = 0; g.Rebuild(); yield return null; yield return null;
g.viewCameraOverride = camTop;
var A = Grab(camTop, TS, TS); var apx = A.GetPixels32();
rt.grassEnabled = 1; rt.viewCullEnabled = 0; g.Rebuild(); yield return null; yield return null;
var B = Grab(camTop, TS, TS);
L("[후] 보이는 풀 = " + Cover(apx, B.GetPixels32()).ToString("F2") + "% of 지면 (띠 안쪽에서 바닥과 같은 픽셀이면 낮다)");
SetGrass(clone, BeforeDif, BeforeShd); g.Rebuild(); yield return null; yield return null;
var B0 = Grab(camTop, TS, TS);
L("[전] 보이는 풀 = " + Cover(apx, B0.GetPixels32()).ToString("F2") + "%");
SetGrass(clone, baseMat.GetColor("_DiffuseColor"), baseMat.GetColor("_ShadowDiffuseColor"));
UnityEngine.Object.DestroyImmediate(A); UnityEngine.Object.DestroyImmediate(B); UnityEngine.Object.DestroyImmediate(B0);
// ── 0 / 2 / 4 s 연속 — 좌표 집합이 그대로인가 ─────────────────
rt.viewCullEnabled = 1; g.viewCameraOverride = camPd; g.Rebuild();
UnityEngine.Time.timeScale = 1f;
ulong k0 = 0; int n0 = 0;
for (int f = 0; f < 3; f++)
{
if (f > 0) { float t0 = UnityEngine.Time.realtimeSinceStartup; while (UnityEngine.Time.realtimeSinceStartup - t0 < 2f) yield return null; }
g.RefreshView(); // 카메라가 멈춰 있어도 강제로 다시 골라 본다
yield return null; yield return null;
var tex = Grab(camPd, W, H);
System.IO.File.WriteAllBytes(Dir + "/iter1_t" + (f * 2) + "s.png", UnityEngine.ImageConversion.EncodeToPNG(tex));
UnityEngine.Object.DestroyImmediate(tex);
ulong k = WL.Look.Farm.WLIslandGrass.InstanceKey; int n = WL.Look.Farm.WLIslandGrass.Instances;
if (f == 0) { k0 = k; n0 = n; }
L("t=" + (f * 2) + "s 인스턴스=" + n + " 좌표지문=" + k + (f == 0 ? "" : (k == k0 && n == n0 ? " → 0 s 와 동일(생성·소멸·이동 0)" : " 🔴 달라졌다")));
}
// ── 카메라를 움직였을 때만 바뀌는가 ───────────────────────────
var p0 = camPd.transform.position;
L("카메라 이동 전 인스턴스=" + WL.Look.Farm.WLIslandGrass.Instances + " 지문=" + WL.Look.Farm.WLIslandGrass.InstanceKey);
camPd.transform.position = p0 + new UnityEngine.Vector3(6f, 0f, 6f);
L(" 움직임 판정(2.5 m 보폭) = " + g.ViewMovedEnough());
g.RefreshView(); yield return null; yield return null;
L(" 이동 후 인스턴스=" + WL.Look.Farm.WLIslandGrass.Instances + " 지문=" + WL.Look.Farm.WLIslandGrass.InstanceKey + " · " + WL.Look.Farm.WLIslandGrass.LastLog);
camPd.transform.position = p0; g.RefreshView(); yield return null; yield return null;
L(" 되돌린 뒤 인스턴스=" + WL.Look.Farm.WLIslandGrass.Instances + " 지문=" + WL.Look.Farm.WLIslandGrass.InstanceKey + (WL.Look.Farm.WLIslandGrass.InstanceKey == k0 ? " → 처음과 동일(격자 고정 확인)" : ""));
g.viewCameraOverride = null;
Flush();
}
static void SetGrass(UnityEngine.Material m, UnityEngine.Color dif, UnityEngine.Color shd)
{
m.SetColor("_DiffuseColor", dif); m.SetColor("_ShadowDiffuseColor", shd);
}
static float Cover(UnityEngine.Color32[] a, UnityEngine.Color32[] b)
{
int n = 0, tot = 0;
for (int i = 0; i < a.Length; i++)
{
var p = a[i]; if (!(p.g > p.b + 8 && p.g > 45)) continue; tot++;
var q = b[i]; if (q.g <= q.b + 4) continue;
if (System.Math.Abs(p.r - q.r) + System.Math.Abs(p.g - q.g) + System.Math.Abs(p.b - q.b) > 6) n++;
}
return tot == 0 ? 0f : 100f * n / tot;
}
static string Metric(UnityEngine.Color32[] px, int w, int h)
{
int bs = 8, bw = w / bs, bh = h / bs;
var bm = new float[bw * bh]; var bok = new bool[bw * bh];
double hf = 0; int hfn = 0;
for (int by = 0; by < bh; by++)
for (int bx = 0; bx < bw; bx++)
{
double s = 0; int n = 0;
for (int y = 0; y < bs; y++) for (int x = 0; x < bs; x++)
{ var p = px[(by * bs + y) * w + (bx * bs + x)]; if (!Gr(p)) continue; s += Lum(p); n++; }
if (n < bs * bs / 2) continue;
float mean = (float)(s / n); bm[by * bw + bx] = mean; bok[by * bw + bx] = true;
for (int y = 0; y < bs; y++) for (int x = 0; x < bs; x++)
{ var p = px[(by * bs + y) * w + (bx * bs + x)]; if (!Gr(p)) continue; hf += System.Math.Abs(Lum(p) - mean); hfn++; }
}
double s1 = 0, s2 = 0; int bn = 0; var lv = new int[40];
for (int i = 0; i < bm.Length; i++) { if (!bok[i]) continue; s1 += bm[i]; s2 += (double)bm[i] * bm[i]; bn++; int q = (int)(bm[i] / 8f); if (q >= 0 && q < 40) lv[q]++; }
if (bn == 0 || hfn == 0) return "지면 0";
double mu = s1 / bn, lf = System.Math.Sqrt(System.Math.Max(0, s2 / bn - mu * mu));
int levels = 0; for (int i = 0; i < 40; i++) if (lv[i] >= bn * 0.005) levels++;
int ed = 0, edn = 0;
for (int y = 0; y < h - 1; y++) for (int x = 0; x < w - 1; x++)
{
var p = px[y * w + x]; if (!Gr(p)) continue; var r = px[y * w + x + 1]; var d = px[(y + 1) * w + x]; edn++;
if ((Gr(r) && System.Math.Abs(Lum(p) - Lum(r)) > 8) || (Gr(d) && System.Math.Abs(Lum(p) - Lum(d)) > 8)) ed++;
}
return string.Format("띠세기LF={0:F2} 포기잡음HF={1:F2} LF/HF={2:F2} 띠단계={3} 에지%={4:F2} 평균밝기={5:F1}",
lf, hf / hfn, lf / System.Math.Max(0.01, hf / hfn), levels, 100.0 * ed / System.Math.Max(1, edn), mu);
}
static bool Gr(UnityEngine.Color32 p) { return p.g > p.b + 8 && p.g > 45; }
static float Lum(UnityEngine.Color32 p) { return 0.299f * p.r + 0.587f * p.g + 0.114f * p.b; }
static string Stat()
{
return "인스턴스=" + WL.Look.Farm.WLIslandGrass.Instances + " 삼각형=" + WL.Look.Farm.WLIslandGrass.Triangles
+ " 드로우콜+" + WL.Look.Farm.WLIslandGrass.DrawnConfigs;
}
static UnityEngine.Camera MakeCam(string name, bool top, float cx, float cz, float size)
{
var go = UnityEngine.GameObject.Find(name);
if (go == null) go = new UnityEngine.GameObject(name);
var c = go.GetComponent<UnityEngine.Camera>();
if (c == null) c = go.AddComponent<UnityEngine.Camera>();
c.nearClipPlane = 0.1f; c.farClipPlane = top ? 200f : 300f;
if (top)
{
c.orthographic = true; c.orthographicSize = size;
c.transform.rotation = UnityEngine.Quaternion.Euler(90f, 0f, 0f);
c.transform.position = new UnityEngine.Vector3(cx, 60f, cz);
}
else
{
UnityEngine.Vector3 tgt = UnityEngine.Vector3.zero;
var pc = UnityEngine.GameObject.FindGameObjectWithTag("Player");
if (pc != null) tgt = pc.transform.position;
c.orthographic = false; c.fieldOfView = 60f;
c.transform.rotation = UnityEngine.Quaternion.Euler(45f, 45f, 0f);
c.transform.position = tgt - c.transform.forward * 12f;
}
c.enabled = false; return c;
}
static UnityEngine.Texture2D Grab(UnityEngine.Camera cam, int w, int h)
{
var rt = new UnityEngine.RenderTexture(w, h, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.Create(); cam.targetTexture = rt; cam.Render();
var prev = UnityEngine.RenderTexture.active; UnityEngine.RenderTexture.active = rt;
var tex = new UnityEngine.Texture2D(w, h, UnityEngine.TextureFormat.RGB24, false);
tex.ReadPixels(new UnityEngine.Rect(0, 0, w, h), 0, 0); tex.Apply();
UnityEngine.RenderTexture.active = prev; cam.targetTexture = null;
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
return tex;
}
static string Shot(UnityEngine.Camera cam, int w, int h, string name)
{
var tex = Grab(cam, w, h);
System.IO.File.WriteAllBytes(Dir + "/" + name + ".png", UnityEngine.ImageConversion.EncodeToPNG(tex));
string s = Metric(tex.GetPixels32(), w, h);
UnityEngine.Object.DestroyImmediate(tex);
return s;
}
static float Bench(UnityEngine.Camera cam, int frames)
{
var rt = new UnityEngine.RenderTexture(W, H, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.Create(); cam.targetTexture = rt;
for (int i = 0; i < 10; i++) cam.Render();
var sw = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < frames; i++) cam.Render();
sw.Stop(); cam.targetTexture = null; rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
return (float)sw.Elapsed.TotalMilliseconds / frames;
}
static void L(string s) { sb.AppendLine(s); Flush(); }
static void Flush()
{
System.IO.File.WriteAllText("AgentScripts/WL816q_VERIFY.txt", sb.ToString());
UnityEngine.Debug.Log("[816q verify]\n" + sb.ToString());
}
}