// WL-816n — PD 구도(12 m · 45° · fov 60)에서 섬 전/후 캡처 + 화면색·성능 실측. // 에셋은 건드리지 않는다 (cfg 는 Instantiate 복제본 · 816f 방식). public static class WL816n_Play { public static void Start() { var go = UnityEngine.GameObject.Find("~WL816nPlay"); if (go != null) UnityEngine.Object.DestroyImmediate(go); go = new UnityEngine.GameObject("~WL816nPlay"); go.AddComponent(); } } public class WL816n_PlayRunner : UnityEngine.MonoBehaviour { static System.Text.StringBuilder sb; const int W = 1080, H = 1920; const string TOP_A = "Assets/WL/Look/Farm/Materials/Farm_IslandTop_Arena.mat"; const string GR_A = "Assets/WL/Look/Farm/Materials/Farm_Grass_Arena.mat"; void Start() { StartCoroutine(Co()); } System.Collections.IEnumerator Co() { sb = new System.Text.StringBuilder(); 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 < 7; i++) yield return new UnityEngine.WaitForSeconds(1f); var g = UnityEngine.Object.FindFirstObjectByType(UnityEngine.FindObjectsInactive.Include); if (g == null) { L("🔴 WLIslandGrass 없음 — 중단"); Flush(); yield break; } var src = WL.Look.Farm.WLIslandLookSettings.Instance; var rt = UnityEngine.Object.Instantiate(src); // 에셋 무변경 g.cfg = rt; var cam = MakeCam(); L("카메라 " + cam.transform.position + " euler=" + cam.transform.eulerAngles + " fov=" + cam.fieldOfView + " (타깃까지 12 m · 45°)"); // ── BEFORE (현재 값: 밀도 1.8 · Demo 머티리얼) ───────────── g.Rebuild(); yield return null; yield return null; L(Shot(cam, "a_before", "BEFORE 밀도=" + Dens(rt) + " 풀mat=" + MatName(rt))); L(" 인스턴스=" + WL.Look.Farm.WLIslandGrass.Instances + " 삼각형=" + WL.Look.Farm.WLIslandGrass.Triangles + " 드로우콜+" + WL.Look.Farm.WLIslandGrass.DrawnConfigs + " " + Bench(cam, 60).ToString("F3") + " ms/frame"); // ── AFTER (밀도 2.5 = 데모 FirstLayer 값 · *_Arena 머티리얼) ─ var topA = UnityEditor.AssetDatabase.LoadAssetAtPath(TOP_A); var graA = UnityEditor.AssetDatabase.LoadAssetAtPath(GR_A); if (topA == null || graA == null) { L("🔴 *_Arena 머티리얼 없음 — Apply 먼저"); Flush(); yield break; } for (int i = 0; i < rt.scatter.Length; i++) if (rt.scatter[i] != null && (rt.scatter[i].label.Contains("풀") || rt.scatter[i].label.Contains("꽃"))) { rt.scatter[i].density = 2.5f; if (rt.scatter[i].label.Contains("풀")) rt.scatter[i].material = graA; } var topD = rt.islandTopMaterial; rt.islandTopMaterial = topA; for (int i = 0; i < rt.materialRemap.Length; i++) if (rt.materialRemap[i] != null && rt.materialRemap[i].to == topD) rt.materialRemap[i].to = topA; WL.Look.Farm.WLIslandLook.SwapMaterials(rt, UnityEngine.SceneManagement.SceneManager.GetSceneByName("Level01")); g.Rebuild(); yield return new UnityEngine.WaitForSeconds(1.5f); L(Shot(cam, "b_after", "AFTER 밀도=" + Dens(rt) + " 풀mat=" + MatName(rt))); L(" 인스턴스=" + WL.Look.Farm.WLIslandGrass.Instances + " 삼각형=" + WL.Look.Farm.WLIslandGrass.Triangles + " 드로우콜+" + WL.Look.Farm.WLIslandGrass.DrawnConfigs + " " + Bench(cam, 60).ToString("F3") + " ms/frame"); // 구름 띠가 시간에 따라 움직이는가 (같은 카메라 · 3초 간격 두 장의 차이) var t0 = Grab(cam); yield return new UnityEngine.WaitForSeconds(3f); var t1 = Grab(cam); L("[구름 이동] 3초 간격 두 프레임의 평균 채널차 = " + Diff(t0, t1).ToString("F2") + " (0 이면 정지)"); Flush(); } // ─────────────────────────────────────────── helpers static string Dens(WL.Look.Farm.WLIslandLookSettings c) { for (int i = 0; i < c.scatter.Length; i++) if (c.scatter[i] != null && c.scatter[i].label.Contains("풀")) return c.scatter[i].density.ToString("F2"); return "?"; } static string MatName(WL.Look.Farm.WLIslandLookSettings c) { for (int i = 0; i < c.scatter.Length; i++) if (c.scatter[i] != null && c.scatter[i].label.Contains("풀")) return c.scatter[i].material ? c.scatter[i].material.name : "-"; return "?"; } static UnityEngine.Camera MakeCam() { UnityEngine.Vector3 tgt = UnityEngine.Vector3.zero; var pc = UnityEngine.GameObject.FindGameObjectWithTag("Player"); if (pc != null) tgt = pc.transform.position; var go = new UnityEngine.GameObject("~816nCam"); var c = go.AddComponent(); c.fieldOfView = 60f; c.orthographic = false; c.nearClipPlane = 0.1f; c.farClipPlane = 300f; 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) { 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 float Diff(UnityEngine.Texture2D a, UnityEngine.Texture2D b) { var pa = a.GetPixels32(); var pb = b.GetPixels32(); long s = 0; int n = 0; for (int i = 0; i < pa.Length; i += 7) { s += System.Math.Abs(pa[i].r - pb[i].r) + System.Math.Abs(pa[i].g - pb[i].g) + System.Math.Abs(pa[i].b - pb[i].b); n += 3; } UnityEngine.Object.DestroyImmediate(a); UnityEngine.Object.DestroyImmediate(b); return n == 0 ? 0f : (float)s / n; } static string Shot(UnityEngine.Camera cam, string name, string tag) { var tex = Grab(cam); System.IO.Directory.CreateDirectory("Screenshots_WL/WL816n"); System.IO.File.WriteAllBytes("Screenshots_WL/WL816n/" + name + ".png", UnityEngine.ImageConversion.EncodeToPNG(tex)); // 초록(풀밭) 픽셀 통계 + 풀 실루엣 커버% var px = tex.GetPixels32(); long r = 0, gg = 0, b = 0; int n = 0; for (int i = 0; i < px.Length; i++) { var p = px[i]; if (p.g > p.b + 12 && p.g > p.r + 8 && p.g > 60) { r += p.r; gg += p.g; b += p.b; n++; } } UnityEngine.Object.DestroyImmediate(tex); if (n == 0) return tag + " : 초록 픽셀 0"; return string.Format("{0} : 풀밭 초록 픽셀 {1} ({2:F1}%) 평균 #{3:X2}{4:X2}{5:X2}", tag, n, 100f * n / px.Length, r / n, gg / n, b / n); } 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); } static void Flush() { System.IO.File.WriteAllText("AgentScripts/WL816n_PLAY.txt", sb.ToString()); UnityEngine.Debug.Log("[816n play]\n" + sb.ToString()); } }