// WL-816r 2차 — PD 구도에서 「흙띠·모래띠」 후보 스윕 + 길/밭 머티리얼 프로브 역산. public static class WL816r_Play2 { public static void Start() { var go = UnityEngine.GameObject.Find("~WL816rPlay2"); if (go != null) UnityEngine.Object.DestroyImmediate(go); go = new UnityEngine.GameObject("~WL816rPlay2"); go.AddComponent(); } } public class WL816r_Runner2 : UnityEngine.MonoBehaviour { static System.Text.StringBuilder sb; const int W = 1080, H = 1920; static string Dir = "Screenshots_WL/WL816r"; // (모래 사분면, 흙 사분면) 후보 — 데모 흙 화면색 #A4A88B 목표 static readonly string[][] TopCand = { new[]{"CCCBB5","807057"}, // 0 현행 new[]{"B5B69E","8F8F79"}, new[]{"A8A992","82836E"}, new[]{"C0C1A8","9A9B84"}, new[]{"AEAF98","6E6F5C"}, }; void Start() { StartCoroutine(Co()); } System.Collections.IEnumerator Co() { sb = new System.Text.StringBuilder(); System.IO.Directory.CreateDirectory(Dir); var cfg = WL.Look.Farm.WLIslandLookSettings.Instance; var topMat = cfg.islandTopMaterial; // ── 실태 보고 (비활성 포함) ───────────────────────────────────────── var farms = UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None); int soilAll = 0, soilActive = 0; UnityEngine.Material soilMat = null; foreach (var f in farms) foreach (var s in f.GetComponentsInChildren(true)) { soilAll++; var r = s.GetComponent(); if (r != null && r.gameObject.activeInHierarchy && r.enabled) soilActive++; if (soilMat == null && r != null) soilMat = r.sharedMaterial; } var roadTfs = new System.Collections.Generic.List(); foreach (var t in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) if (t.name == "Road") roadTfs.Add(t); int roadRendAll = 0, roadRendActive = 0; UnityEngine.Material roadMat = null; foreach (var t in roadTfs) foreach (var r in t.GetComponentsInChildren(true)) { roadRendAll++; if (r.gameObject.activeInHierarchy && r.enabled) roadRendActive++; foreach (var m in r.sharedMaterials) if (m != null && m.shader != null && m.shader.name.Contains("Toon") && roadMat == null) roadMat = m; } L("실태 — Farm=" + farms.Length + " Soil(전체/활성)=" + soilAll + "/" + soilActive + " · Road 트랜스폼=" + roadTfs.Count + " 렌더러(전체/활성)=" + roadRendAll + "/" + roadRendActive); L(" Soil mat=" + (soilMat ? soilMat.name + " sh=" + soilMat.shader.name + " color=" + Hx(MatColor(soilMat).gamma) : "없음")); L(" Road mat=" + (roadMat ? roadMat.name + " sh=" + roadMat.shader.name + " Dif=" + Hx(roadMat.GetColor("_DiffuseColor").gamma) + " Shd=" + Hx(roadMat.GetColor("_ShadowDiffuseColor").gamma) : "없음")); // ── 섬 윗면 렌더러 ────────────────────────────────────────────────── var topR = new System.Collections.Generic.List(); foreach (var r in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) foreach (var m in r.sharedMaterials) if (m != null && m == topMat) { topR.Add(r); break; } var camPd = MakePd(); // 띠 픽셀 = 섬윗면 그룹 중 「초록이 아닌」 픽셀 (전 기준으로 고정한다) var mask = IdMask(camPd, W, H, topR); var texB = Grab(camPd, W, H); System.IO.File.WriteAllBytes(Dir + "/r2_before_pd.png", UnityEngine.ImageConversion.EncodeToPNG(texB)); var pxB = texB.GetPixels32(); var band = new byte[W * H]; int nb = 0; for (int i = 0; i < mask.Length; i++) { if (mask[i] != 1) continue; var p = pxB[i]; if (p.g > p.r + 10 && p.g > p.b + 10) continue; // 초록(풀밭 바닥) 제외 band[i] = 1; nb++; } L("섬윗면 그룹 px=" + Count(mask, 1) + " · 그중 띠(비초록) px=" + nb); L("[전] 띠 " + Stat(pxB, band, 1)); UnityEngine.Object.DestroyImmediate(texB); // ── 텍스처 후보 스윕 (초록 행 원본 바이트 유지) ───────────────────── var srcBytes = System.IO.File.ReadAllBytes("Assets/WL/Look/Farm/Textures/WL_IslandTop_Demo.png"); for (int ci = 1; ci < TopCand.Length; ci++) { var tex = MakeTopTex(srcBytes, TopCand[ci][0], TopCand[ci][1]); var m = new UnityEngine.Material(topMat); m.name = topMat.name + "_r" + ci; m.SetTexture("_BaseMap", tex); m.SetTexture("_ShadowBaseMap", tex); SwapMat(topR, topMat, m); yield return null; yield return null; var t2 = Grab(camPd, W, H); var p2 = t2.GetPixels32(); L("[후보" + ci + " 모래#" + TopCand[ci][0] + " 흙#" + TopCand[ci][1] + "] 띠 " + Stat(p2, band, 1)); if (ci == 1) System.IO.File.WriteAllBytes(Dir + "/r2_cand1_pd.png", UnityEngine.ImageConversion.EncodeToPNG(t2)); UnityEngine.Object.DestroyImmediate(t2); SwapMat(topR, m, topMat); UnityEngine.Object.DestroyImmediate(m); UnityEngine.Object.DestroyImmediate(tex); } // ── 길·밭 머티리얼 프로브 (평평한 판 · 섬 위) ─────────────────────── var pc = UnityEngine.GameObject.FindGameObjectWithTag("Player"); var org = pc != null ? pc.transform.position : UnityEngine.Vector3.zero; var cands = new[] { "D4D6BE", "C6C8B0", "B5B69E", "A8A992", "9A9B84", "CCCBB5" }; var camTopP = MakeTopProbe(org.x, org.z + 3f, 5f); yield return ProbeRow(camTopP, roadMat, cands, org + new UnityEngine.Vector3(0f, 1.2f, 3f), "길(Toon)"); yield return ProbeRow(camTopP, soilMat, cands, org + new UnityEngine.Vector3(0f, 1.2f, 4.6f), "밭(Soil셰이더)"); Flush(); } System.Collections.IEnumerator ProbeRow(UnityEngine.Camera cam, UnityEngine.Material baseMat, string[] cands, UnityEngine.Vector3 origin, string tag) { if (baseMat == null) { L(tag + " — 머티리얼 없음, 프로브 생략"); yield break; } var root = new UnityEngine.GameObject("~816rP2"); var mats = new System.Collections.Generic.List(); var rends = new System.Collections.Generic.List(); var pos = new UnityEngine.Vector3[cands.Length]; bool toon = baseMat.HasProperty("_DiffuseColor"); var sr = toon && baseMat.HasProperty("_ShadowDiffuseColor") ? new UnityEngine.Color(0.3596f, 0.2741f, 0.2051f, 1f) : UnityEngine.Color.white; // 데모 흙 그림자비 for (int i = 0; i < cands.Length; i++) { var lin = Hex(cands[i]).linear; var m = new UnityEngine.Material(baseMat); m.name = baseMat.name + "_p" + i; if (toon) { m.SetTexture("_BaseMap", null); m.SetTexture("_ShadowBaseMap", null); m.SetColor("_DiffuseColor", lin); m.SetColor("_ShadowDiffuseColor", new UnityEngine.Color(lin.r * sr.r, lin.g * sr.g, lin.b * sr.b, 1f)); } else SetMatColor(m, lin); mats.Add(m); var q = UnityEngine.GameObject.CreatePrimitive(UnityEngine.PrimitiveType.Quad); q.transform.SetParent(root.transform); q.transform.rotation = UnityEngine.Quaternion.Euler(90f, 0f, 0f); q.transform.localScale = new UnityEngine.Vector3(1.1f, 1.1f, 1f); pos[i] = origin + new UnityEngine.Vector3((i - (cands.Length - 1) * 0.5f) * 1.3f, 0f, 0f); q.transform.position = pos[i]; var rr = q.GetComponent(); rr.sharedMaterial = m; rends.Add(rr); UnityEngine.Object.DestroyImmediate(q.GetComponent()); } yield return null; yield return null; var tex = Grab(cam, 1024, 1024); var px = tex.GetPixels32(); for (int i = 0; i < cands.Length; i++) L(" " + tag + " #" + cands[i] + " -> " + Patch(cam, px, pos[i], 1024, 8)); System.IO.File.WriteAllBytes(Dir + "/r2_probe_" + (toon ? "road" : "soil") + ".png", UnityEngine.ImageConversion.EncodeToPNG(tex)); UnityEngine.Object.DestroyImmediate(tex); UnityEngine.Object.DestroyImmediate(root); foreach (var m in mats) UnityEngine.Object.DestroyImmediate(m); } static UnityEngine.Color MatColor(UnityEngine.Material m) { if (m == null) return UnityEngine.Color.black; if (m.HasProperty("_BaseColor")) return m.GetColor("_BaseColor"); if (m.HasProperty("_Color")) return m.GetColor("_Color"); return UnityEngine.Color.black; } static void SetMatColor(UnityEngine.Material m, UnityEngine.Color c) { if (m.HasProperty("_BaseColor")) m.SetColor("_BaseColor", c); if (m.HasProperty("_Color")) m.SetColor("_Color", c); } static UnityEngine.Texture2D MakeTopTex(byte[] srcBytes, string sandHex, string dirtHex) { var tex = new UnityEngine.Texture2D(2, 2, UnityEngine.TextureFormat.RGBA32, false); UnityEngine.ImageConversion.LoadImage(tex, srcBytes); tex.filterMode = UnityEngine.FilterMode.Point; tex.wrapMode = UnityEngine.TextureWrapMode.Clamp; var px = tex.GetPixels32(); var sand = Hex32c(sandHex); var dirt = Hex32c(dirtHex); var sandSrc = new UnityEngine.Color32(0xCC, 0xCB, 0xB5, 255); var dirtSrc = new UnityEngine.Color32(0x80, 0x70, 0x57, 255); for (int i = 0; i < px.Length; i++) { if (Same(px[i], sandSrc)) px[i] = sand; else if (Same(px[i], dirtSrc)) px[i] = dirt; } tex.SetPixels32(px); tex.Apply(false); return tex; } static bool Same(UnityEngine.Color32 a, UnityEngine.Color32 b) { return a.r == b.r && a.g == b.g && a.b == b.b; } static void SwapMat(System.Collections.Generic.List rs, UnityEngine.Material from, UnityEngine.Material to) { foreach (var r in rs) { var ms = r.sharedMaterials; bool hit = false; for (int i = 0; i < ms.Length; i++) if (ms[i] == from) { ms[i] = to; hit = true; } if (hit) r.sharedMaterials = ms; } } static byte[] IdMask(UnityEngine.Camera cam, int w, int h, System.Collections.Generic.List top) { var unlit = UnityEngine.Shader.Find("Universal Render Pipeline/Unlit"); var saved = new System.Collections.Generic.Dictionary(); var mats = new System.Collections.Generic.List(); var black = new UnityEngine.Material(unlit); black.SetColor("_BaseColor", UnityEngine.Color.black); mats.Add(black); var blue = new UnityEngine.Material(unlit); blue.SetColor("_BaseColor", new UnityEngine.Color(0f, 0f, 1f, 1f)); mats.Add(blue); foreach (var r in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) { if (r == null || !r.enabled) continue; var use = top.Contains(r) ? blue : black; saved[r] = r.sharedMaterials; var arr = new UnityEngine.Material[r.sharedMaterials.Length]; for (int i = 0; i < arr.Length; i++) arr[i] = use; r.sharedMaterials = arr; } var tex = Grab(cam, w, h); var px = tex.GetPixels32(); var mask = new byte[w * h]; for (int i = 0; i < px.Length; i++) if (px[i].b > 150 && px[i].r < 90 && px[i].g < 90) mask[i] = 1; UnityEngine.Object.DestroyImmediate(tex); foreach (var kv in saved) if (kv.Key != null) kv.Key.sharedMaterials = kv.Value; foreach (var m in mats) UnityEngine.Object.DestroyImmediate(m); return mask; } static int Count(byte[] m, byte id) { int n = 0; for (int i = 0; i < m.Length; i++) if (m[i] == id) n++; return n; } static string Stat(UnityEngine.Color32[] px, byte[] mask, byte id) { long r = 0, g = 0, b2 = 0; int n = 0; var uniq = new System.Collections.Generic.Dictionary(); for (int i = 0; i < mask.Length && i < px.Length; i++) { if (mask[i] != id) continue; var p = px[i]; r += p.r; g += p.g; b2 += p.b; n++; int k = (p.r << 16) | (p.g << 8) | p.b; uniq[k] = uniq.ContainsKey(k) ? uniq[k] + 1 : 1; } if (n == 0) return "px0"; var list = new System.Collections.Generic.List>(uniq); list.Sort((A, B) => B.Value.CompareTo(A.Value)); var top = new System.Text.StringBuilder(); for (int i = 0; i < System.Math.Min(4, list.Count); i++) top.Append(string.Format(" #{0:X6}x{1}", list[i].Key, list[i].Value)); return string.Format("n={0} avg=#{1:X2}{2:X2}{3:X2} uniq={4} top:{5}", n, r / n, g / n, b2 / n, uniq.Count, top); } static string Patch(UnityEngine.Camera cam, UnityEngine.Color32[] px, UnityEngine.Vector3 w, int size, int rad) { var v = cam.WorldToViewportPoint(w); int cx = (int)(v.x * size), cy = (int)(v.y * size); long r = 0, g = 0, b = 0; int n = 0; for (int y = cy - rad; y <= cy + rad; y++) for (int x = cx - rad; x <= cx + rad; x++) { if (x < 0 || y < 0 || x >= size || y >= size) continue; var p = px[y * size + x]; r += p.r; g += p.g; b += p.b; n++; } return n == 0 ? "px0" : string.Format("#{0:X2}{1:X2}{2:X2} ({3},{4},{5})", r / n, g / n, b / n, r / n, g / n, b / n); } static UnityEngine.Color Hex(string h) { return new UnityEngine.Color(System.Convert.ToInt32(h.Substring(0, 2), 16) / 255f, System.Convert.ToInt32(h.Substring(2, 2), 16) / 255f, System.Convert.ToInt32(h.Substring(4, 2), 16) / 255f, 1f); } static UnityEngine.Color32 Hex32c(string h) { return new UnityEngine.Color32((byte)System.Convert.ToInt32(h.Substring(0, 2), 16), (byte)System.Convert.ToInt32(h.Substring(2, 2), 16), (byte)System.Convert.ToInt32(h.Substring(4, 2), 16), 255); } static string Hx(UnityEngine.Color c) { return string.Format("#{0:X2}{1:X2}{2:X2}", (int)(UnityEngine.Mathf.Clamp01(c.r) * 255), (int)(UnityEngine.Mathf.Clamp01(c.g) * 255), (int)(UnityEngine.Mathf.Clamp01(c.b) * 255)); } static UnityEngine.Camera MakePd() { UnityEngine.Vector3 tgt = UnityEngine.Vector3.zero; var pc = UnityEngine.GameObject.FindGameObjectWithTag("Player"); if (pc != null) tgt = pc.transform.position; var go = UnityEngine.GameObject.Find("~816r2Pd"); if (go == null) go = new UnityEngine.GameObject("~816r2Pd"); var c = go.GetComponent(); if (c == null) c = go.AddComponent(); c.orthographic = false; c.fieldOfView = 60f; 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.aspect = (float)W / H; c.enabled = false; return c; } static UnityEngine.Camera MakeTopProbe(float x, float z, float size) { var go = UnityEngine.GameObject.Find("~816r2Tp"); if (go == null) go = new UnityEngine.GameObject("~816r2Tp"); var c = go.GetComponent(); if (c == null) c = go.AddComponent(); c.orthographic = true; c.orthographicSize = size; c.nearClipPlane = 0.1f; c.farClipPlane = 200f; c.transform.rotation = UnityEngine.Quaternion.Euler(90f, 0f, 0f); c.transform.position = new UnityEngine.Vector3(x, 40f, z); c.aspect = 1f; 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 void L(string s) { sb.AppendLine(s); Flush(); } static void Flush() { System.IO.File.WriteAllText("AgentScripts/WL816r_PLAY2.txt", sb.ToString()); UnityEngine.Debug.Log("[816r play2]\n" + sb.ToString()); } }