173 lines
8.5 KiB
C#
173 lines
8.5 KiB
C#
|
|
// WL-816o 진단 — 화면에 그려진 구름 띠 경계 ↔ CPU 가 계산한 띠 경계가 왜 어긋나는가.
|
|||
|
|
public static class WL816o_Diag
|
|||
|
|
{
|
|||
|
|
public static void Start()
|
|||
|
|
{
|
|||
|
|
var go = UnityEngine.GameObject.Find("~WL816oDiag");
|
|||
|
|
if (go != null) UnityEngine.Object.DestroyImmediate(go);
|
|||
|
|
go = new UnityEngine.GameObject("~WL816oDiag");
|
|||
|
|
go.AddComponent<WL816o_DiagRunner>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public class WL816o_DiagRunner : UnityEngine.MonoBehaviour
|
|||
|
|
{
|
|||
|
|
const int TW = 512;
|
|||
|
|
static System.Text.StringBuilder sb;
|
|||
|
|
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 < 6; i++) yield return new UnityEngine.WaitForSeconds(1f);
|
|||
|
|
|
|||
|
|
var g = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLIslandGrass>(UnityEngine.FindObjectsInactive.Include);
|
|||
|
|
var cfg = WL.Look.Farm.WLIslandLookSettings.Instance;
|
|||
|
|
var mat = cfg.cloudEdgeSourceMaterial != null ? cfg.cloudEdgeSourceMaterial : cfg.islandTopMaterial;
|
|||
|
|
|
|||
|
|
// 실제로 화면에 보이는 섬 윗면 렌더러가 무슨 머티리얼을 쓰는가 (런타임 교체 후)
|
|||
|
|
var isls = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Island>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
|
|||
|
|
for (int i = 0; i < isls.Length && i < 3; i++)
|
|||
|
|
{
|
|||
|
|
var r = isls[i].GetComponent<UnityEngine.Renderer>();
|
|||
|
|
L("Island[" + i + "] pos=" + isls[i].transform.position + " mats=" + (r ? Names(r) : "-"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var b = g.CalculateInstancesBounds();
|
|||
|
|
float size = UnityEngine.Mathf.Max(b.size.x, b.size.z) * 0.5f;
|
|||
|
|
float cx = b.center.x, cz = b.center.z;
|
|||
|
|
float y = isls.Length > 0 ? isls[0].transform.position.y : b.center.y;
|
|||
|
|
L("bounds c=" + b.center + " size=" + b.size + " · ortho size=" + size + " · 윗면 y=" + y);
|
|||
|
|
|
|||
|
|
bool was = g.enabled; g.enabled = false;
|
|||
|
|
var cam = MakeTop(cx, cz, size);
|
|||
|
|
var tex = GrabCam(cam, TW, TW);
|
|||
|
|
g.enabled = was;
|
|||
|
|
var px = tex.GetPixels32();
|
|||
|
|
|
|||
|
|
var f = new WL.Look.Farm.WLCloudShadowField();
|
|||
|
|
if (!f.Configure(mat)) { L("Configure 실패 " + f.Why); Flush(); yield break; }
|
|||
|
|
L("field: " + f.Dump());
|
|||
|
|
L("_Time.y(global)=" + UnityEngine.Shader.GetGlobalVector("_Time").y + " timeSinceLevelLoad=" + UnityEngine.Time.timeSinceLevelLoad);
|
|||
|
|
|
|||
|
|
// 화면 밝기 계단 확인 (초록 픽셀만)
|
|||
|
|
var hist = new System.Collections.Generic.Dictionary<int, int>();
|
|||
|
|
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)) continue;
|
|||
|
|
int l = Lum(p); if (!hist.ContainsKey(l)) hist[l] = 0; hist[l]++;
|
|||
|
|
}
|
|||
|
|
var keys = new System.Collections.Generic.List<int>(hist.Keys); keys.Sort();
|
|||
|
|
var top = new System.Collections.Generic.List<string>();
|
|||
|
|
for (int i = 0; i < keys.Count; i++) if (hist[keys[i]] > 2000) top.Add(keys[i] + "×" + hist[keys[i]]);
|
|||
|
|
L("초록 밝기 계단(2000픽셀 이상) = " + string.Join(" ", top.ToArray()) + " (고유값 " + keys.Count + ")");
|
|||
|
|
|
|||
|
|
// 가운데 줄 — 화면 경계 위치 vs CPU 띠 경계 위치 (월드 X)
|
|||
|
|
for (int r = 0; r < 3; r++)
|
|||
|
|
{
|
|||
|
|
int iy = 128 + r * 128;
|
|||
|
|
float wz = cz + (((iy + 0.5f) / TW) - 0.5f) * 2f * size;
|
|||
|
|
var scr = new System.Collections.Generic.List<string>();
|
|||
|
|
var cpu = new System.Collections.Generic.List<string>();
|
|||
|
|
int prevBand = int.MinValue;
|
|||
|
|
for (int ix = 1; ix < TW - 1; ix++)
|
|||
|
|
{
|
|||
|
|
float wx = cx + (((ix + 0.5f) / TW) - 0.5f) * 2f * size;
|
|||
|
|
var A = px[iy * TW + ix - 1]; var B = px[iy * TW + ix];
|
|||
|
|
if (Green(A) && Green(B)) { int d = Lum(B) - Lum(A); if (d < 0) d = -d; if (d >= 6 && d <= 45) scr.Add(wx.ToString("F2")); }
|
|||
|
|
int bd = f.Band(wx, y, wz);
|
|||
|
|
if (prevBand != int.MinValue && bd != prevBand) cpu.Add(wx.ToString("F2") + "(" + prevBand + "→" + bd + ")");
|
|||
|
|
prevBand = bd;
|
|||
|
|
}
|
|||
|
|
L("row z=" + wz.ToString("F2") + " 화면경계X=[" + string.Join(" ", scr.ToArray()) + "]");
|
|||
|
|
L(" CPU경계X=[" + string.Join(" ", cpu.ToArray()) + "] cloudiness(x=" + cx.ToString("F1") + ")="
|
|||
|
|
+ f.Cloudiness(cx, y, wz).ToString("F4"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 시간 오프셋 스캔 — 가장 잘 맞는 Δt 를 찾는다
|
|||
|
|
float t0 = f.TimeValue;
|
|||
|
|
string best = ""; float bestScore = -1f, bestDt = 0f;
|
|||
|
|
for (float dt = -12f; dt <= 12.01f; dt += 0.5f)
|
|||
|
|
{
|
|||
|
|
f.TimeValue = t0 + dt;
|
|||
|
|
float s = Agree(px, f, cx, cz, size, y);
|
|||
|
|
if (s > bestScore) { bestScore = s; bestDt = dt; }
|
|||
|
|
if (System.Math.Abs(dt) < 0.01f) best = "Δt=0 일치율 " + (s * 100f).ToString("F1") + "%";
|
|||
|
|
}
|
|||
|
|
f.TimeValue = t0;
|
|||
|
|
L(best + " · 최적 Δt=" + bestDt.ToString("F1") + " 일치율 " + (bestScore * 100f).ToString("F1") + "%");
|
|||
|
|
|
|||
|
|
UnityEngine.Object.DestroyImmediate(tex);
|
|||
|
|
Flush();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static float Agree(UnityEngine.Color32[] px, WL.Look.Farm.WLCloudShadowField f, float cx, float cz, float size, float y)
|
|||
|
|
{
|
|||
|
|
int shown = 0, matched = 0;
|
|||
|
|
for (int iy = 8; iy < TW - 8; iy += 4)
|
|||
|
|
{
|
|||
|
|
float wz = cz + (((iy + 0.5f) / TW) - 0.5f) * 2f * size;
|
|||
|
|
for (int ix = 2; ix < TW - 3; ix += 2)
|
|||
|
|
{
|
|||
|
|
var A = px[iy * TW + ix]; var B = px[iy * TW + ix + 1];
|
|||
|
|
if (!Green(A) || !Green(B)) continue;
|
|||
|
|
int d = Lum(B) - Lum(A); if (d < 0) d = -d;
|
|||
|
|
if (d < 6 || d > 45) continue;
|
|||
|
|
shown++;
|
|||
|
|
float w0 = cx + (((ix + 0.5f) / TW) - 0.5f) * 2f * size;
|
|||
|
|
float w1 = cx + (((ix + 3.5f) / TW) - 0.5f) * 2f * size;
|
|||
|
|
float wm = cx + (((ix - 1.5f) / TW) - 0.5f) * 2f * size;
|
|||
|
|
int b0 = f.Band(wm, y, wz), b1 = f.Band(w1, y, wz);
|
|||
|
|
if (b0 != b1) matched++;
|
|||
|
|
_ = w0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return shown == 0 ? 0f : (float)matched / shown;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static bool Green(UnityEngine.Color32 p) { return p.g > p.b + 12 && p.g > p.r + 8 && p.g > 60; }
|
|||
|
|
static int Lum(UnityEngine.Color32 p) { return (p.r * 77 + p.g * 150 + p.b * 29) >> 8; }
|
|||
|
|
static string Names(UnityEngine.Renderer r)
|
|||
|
|
{ var m = r.sharedMaterials; var s = ""; for (int i = 0; i < m.Length; i++) s += (m[i] ? m[i].name : "-") + " "; return s; }
|
|||
|
|
|
|||
|
|
static UnityEngine.Camera MakeTop(float cx, float cz, float size)
|
|||
|
|
{
|
|||
|
|
var go = UnityEngine.GameObject.Find("~816oTopD");
|
|||
|
|
if (go == null) go = new UnityEngine.GameObject("~816oTopD");
|
|||
|
|
var c = go.GetComponent<UnityEngine.Camera>(); if (c == null) c = go.AddComponent<UnityEngine.Camera>();
|
|||
|
|
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(cx, 40f, cz);
|
|||
|
|
c.enabled = false;
|
|||
|
|
return c;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static UnityEngine.Texture2D GrabCam(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);
|
|||
|
|
System.IO.Directory.CreateDirectory("Screenshots_WL/WL816o");
|
|||
|
|
System.IO.File.WriteAllBytes("Screenshots_WL/WL816o/diag_top.png", UnityEngine.ImageConversion.EncodeToPNG(tex));
|
|||
|
|
return tex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void L(string s) { sb.AppendLine(s); }
|
|||
|
|
static void Flush()
|
|||
|
|
{
|
|||
|
|
System.IO.File.WriteAllText("AgentScripts/WL816o_DIAG.txt", sb.ToString());
|
|||
|
|
UnityEngine.Debug.Log("[816o diag]\n" + sb.ToString());
|
|||
|
|
}
|
|||
|
|
}
|