Project_WL/AgentScripts/WL816r_Play3.cs

190 lines
9.6 KiB
C#
Raw Permalink Normal View History

// WL-816r 3차 — 같은 프레임 안에서 「띠 / 초록바닥」 비를 재서 데모의 「흙 / 풀」 비에 맞춘다.
// 구름이 흘러 밝기가 변해도 같은 프레임의 비는 흔들리지 않는다(816q 교훈).
public static class WL816r_Play3
{
public static void Start()
{
var go = UnityEngine.GameObject.Find("~WL816rPlay3");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816rPlay3");
go.AddComponent<WL816r_Runner3>();
}
}
public class WL816r_Runner3 : UnityEngine.MonoBehaviour
{
static System.Text.StringBuilder sb;
const int W = 1080, H = 1920;
static string Dir = "Screenshots_WL/WL816r";
// 데모 실측: 흙 #A4A88B(164,168,139) / 풀 #97C284(151,194,132) → 비
static readonly float[] TargetRatio = { 164f / 151f, 168f / 194f, 139f / 132f };
static readonly string[][] Cand = {
new[]{"CCCBB5","807057"}, // 0 현행
new[]{"96B39C","708D76"},
new[]{"A2BCA4","7C967E"},
new[]{"AEC4AC","88A086"},
new[]{"BACCB4","94A88E"},
new[]{"8AA894","648276"},
};
void Start() { StartCoroutine(Co()); }
System.Collections.IEnumerator Co()
{
sb = new System.Text.StringBuilder();
System.IO.Directory.CreateDirectory(Dir);
L("데모 목표비 흙/풀 = " + TargetRatio[0].ToString("F4") + "," + TargetRatio[1].ToString("F4") + "," + TargetRatio[2].ToString("F4"));
var cfg = WL.Look.Farm.WLIslandLookSettings.Instance;
var topMat = cfg.islandTopMaterial;
var topR = new System.Collections.Generic.List<UnityEngine.Renderer>();
foreach (var r in UnityEngine.Object.FindObjectsByType<UnityEngine.Renderer>(UnityEngine.FindObjectsSortMode.None))
foreach (var m in r.sharedMaterials) if (m != null && m == topMat) { topR.Add(r); break; }
var cam = MakePd();
var mask = IdMask(cam, W, H, topR);
// 초록/띠 분류는 「현행」 프레임 한 번으로 고정한다
var t0 = Grab(cam, W, H);
var p0 = t0.GetPixels32();
var kind = new byte[W * H];
int ng = 0, nb = 0;
for (int i = 0; i < mask.Length; i++)
{
if (mask[i] != 1) continue;
var p = p0[i];
if (p.g > p.r + 20 && p.g > p.b + 20) { kind[i] = 1; ng++; } // 초록 바닥
else if (p.r > 150) { kind[i] = 2; nb++; } // 🔴 평평한 윗면 띠만(수직 절벽 제외)
}
L("초록px=" + ng + " 윗면띠px=" + nb);
UnityEngine.Object.DestroyImmediate(t0);
var srcBytes = System.IO.File.ReadAllBytes("Assets/WL/Look/Farm/Textures/WL_IslandTop_Demo.png");
for (int ci = 0; ci < Cand.Length; ci++)
{
UnityEngine.Material m = null; UnityEngine.Texture2D tex = null;
if (ci > 0)
{
tex = MakeTopTex(srcBytes, Cand[ci][0], Cand[ci][1]);
m = new UnityEngine.Material(topMat); m.name = topMat.name + "_c" + ci;
m.SetTexture("_BaseMap", tex); m.SetTexture("_ShadowBaseMap", tex);
SwapMat(topR, topMat, m);
yield return null; yield return null;
}
var t = Grab(cam, W, H);
var px = t.GetPixels32();
var g = Avg(px, kind, 1);
var b = Avg(px, kind, 2);
float[] ratio = { b[0] / UnityEngine.Mathf.Max(1f, g[0]), b[1] / UnityEngine.Mathf.Max(1f, g[1]), b[2] / UnityEngine.Mathf.Max(1f, g[2]) };
float err = UnityEngine.Mathf.Abs(ratio[0] - TargetRatio[0]) + UnityEngine.Mathf.Abs(ratio[1] - TargetRatio[1]) + UnityEngine.Mathf.Abs(ratio[2] - TargetRatio[2]);
L(string.Format("[{0}] 모래#{1} 흙#{2} | 초록={3} 띠={4} | 비={5:F4},{6:F4},{7:F4} | 오차={8:F4}",
ci, Cand[ci][0], Cand[ci][1], Hx(g), Hx(b), ratio[0], ratio[1], ratio[2], err));
if (ci == 0) System.IO.File.WriteAllBytes(Dir + "/r3_before.png", UnityEngine.ImageConversion.EncodeToPNG(t));
System.IO.File.WriteAllBytes(Dir + "/r3_c" + ci + ".png", UnityEngine.ImageConversion.EncodeToPNG(t));
UnityEngine.Object.DestroyImmediate(t);
if (ci > 0) { SwapMat(topR, m, topMat); UnityEngine.Object.DestroyImmediate(m); UnityEngine.Object.DestroyImmediate(tex); }
}
Flush();
}
static float[] Avg(UnityEngine.Color32[] px, byte[] kind, byte id)
{
double r = 0, g = 0, b = 0; int n = 0;
for (int i = 0; i < kind.Length && i < px.Length; i++)
{
if (kind[i] != id) continue;
r += px[i].r; g += px[i].g; b += px[i].b; n++;
}
if (n == 0) return new float[] { 1, 1, 1 };
return new float[] { (float)(r / n), (float)(g / n), (float)(b / n) };
}
static string Hx(float[] c) { return string.Format("#{0:X2}{1:X2}{2:X2}", (int)c[0], (int)c[1], (int)c[2]); }
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 (px[i].r == sandSrc.r && px[i].g == sandSrc.g && px[i].b == sandSrc.b) px[i] = sand;
else if (px[i].r == dirtSrc.r && px[i].g == dirtSrc.g && px[i].b == dirtSrc.b) px[i] = dirt;
}
tex.SetPixels32(px); tex.Apply(false);
return tex;
}
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 void SwapMat(System.Collections.Generic.List<UnityEngine.Renderer> 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<UnityEngine.Renderer> top)
{
var unlit = UnityEngine.Shader.Find("Universal Render Pipeline/Unlit");
var saved = new System.Collections.Generic.Dictionary<UnityEngine.Renderer, UnityEngine.Material[]>();
var black = new UnityEngine.Material(unlit); black.SetColor("_BaseColor", UnityEngine.Color.black);
var blue = new UnityEngine.Material(unlit); blue.SetColor("_BaseColor", new UnityEngine.Color(0f, 0f, 1f, 1f));
foreach (var r in UnityEngine.Object.FindObjectsByType<UnityEngine.Renderer>(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;
UnityEngine.Object.DestroyImmediate(black); UnityEngine.Object.DestroyImmediate(blue);
return mask;
}
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("~816r3Pd"); if (go == null) go = new UnityEngine.GameObject("~816r3Pd");
var c = go.GetComponent<UnityEngine.Camera>(); if (c == null) c = go.AddComponent<UnityEngine.Camera>();
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.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_PLAY3.txt", sb.ToString());
UnityEngine.Debug.Log("[816r play3]\n" + sb.ToString());
}
}