Project_WL/AgentScripts/WL816q_Play.cs

269 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// WL-816q — 「그라데이션(구름 띠)이 왜 안 보이나」 요소별 ON/OFF 실측.
// 에셋 무변경(SO·머티리얼은 전부 런타임 복제본). Debug.LogError 금지.
public static class WL816q_Play
{
public static void Start()
{
var go = UnityEngine.GameObject.Find("~WL816qPlay");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816qPlay");
go.AddComponent<WL816q_PlayRunner>();
}
}
public class WL816q_PlayRunner : UnityEngine.MonoBehaviour
{
static System.Text.StringBuilder sb;
const int W = 1080, H = 1920;
const int TS = 768;
static string Dir = "Screenshots_WL/WL816q";
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 < 7; i++) yield return new UnityEngine.WaitForSeconds(1f);
var g = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLIslandGrass>(UnityEngine.FindObjectsInactive.Include);
if (g == null) { L("WLIslandGrass 없음 — 중단"); Flush(); yield break; }
var src = WL.Look.Farm.WLIslandLookSettings.Instance;
var rt = UnityEngine.Object.Instantiate(src);
// scatter 정의를 새 인스턴스로 깊은 복사 — 에셋 쪽 객체를 절대 만지지 않는다
if (rt.scatter != null)
{
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;
// ── 요소 진단 ────────────────────────────────────────────────
L("=== 요소 진단 === t=" + UnityEngine.Time.timeSinceLevelLoad);
L("ⓑ WLReferenceLook.IsApplied=" + WL.Look.Arena.WLReferenceLook.IsApplied
+ " · Outlined=" + WL.Look.Arena.WLReferenceLook.OutlinedRenderers
+ " · Contrast=" + WL.Look.Arena.WLReferenceLook.ContrastMaterials);
int hull = 0, toonR = 0;
var rends = UnityEngine.Object.FindObjectsByType<UnityEngine.Renderer>(UnityEngine.FindObjectsSortMode.None);
for (int i = 0; i < rends.Length; i++)
{
var ms = rends[i].sharedMaterials;
for (int m = 0; m < ms.Length; m++)
{
if (ms[m] == null || ms[m].shader == null) continue;
if (ms[m].shader.name == "WL/HullOutline") hull++;
if (ms[m].shader.name.Contains("Toon")) toonR++;
}
}
L("ⓑ 씬 안 HullOutline 머티리얼 슬롯=" + hull + " · Toon 슬롯=" + toonR);
var vols = UnityEngine.Object.FindObjectsByType<UnityEngine.Rendering.Volume>(UnityEngine.FindObjectsSortMode.None);
L("ⓓ Volume 개수=" + vols.Length + (vols.Length > 0 ? " 첫 프로필=" + (vols[0].sharedProfile ? vols[0].sharedProfile.name : "-") : ""));
var sun = UnityEngine.RenderSettings.sun;
var lights = UnityEngine.Object.FindObjectsByType<UnityEngine.Light>(UnityEngine.FindObjectsSortMode.None);
for (int i = 0; i < lights.Length; i++)
if (lights[i].type == UnityEngine.LightType.Directional)
L("ⓐ Directional " + lights[i].name + " shadows=" + lights[i].shadows + " int=" + lights[i].intensity + " rot=" + lights[i].transform.eulerAngles);
var top = rt.islandTopMaterial;
var grassMat = (rt.scatter != null && rt.scatter.Length > 0) ? rt.scatter[0].material : null;
L("ⓒⓔ 바닥=" + (top ? top.name : "-") + " sh=" + (top && top.shader ? top.shader.name : "-") + " " + MatDump(top));
L("ⓒⓔ 풀 =" + (grassMat ? grassMat.name : "-") + " sh=" + (grassMat && grassMat.shader ? grassMat.shader.name : "-") + " " + MatDump(grassMat));
L("ⓔ 그림자비(Shadow/Diffuse) 바닥=" + RatioStr(top) + " · 풀=" + RatioStr(grassMat));
var b = g.CalculateInstancesBounds();
L("섬 바운즈 center=" + b.center + " size=" + b.size);
var camTop = MakeTop(b.center.x, b.center.z, 6f);
var camPd = MakePd();
L("탑다운 = ortho6 @(" + b.center.x.ToString("F2") + ",60," + b.center.z.ToString("F2") + ") · PD = 12m/45°/fov60");
// ── A: 풀 끔 (바닥만) ───────────────────────────────────────
rt.grassEnabled = 0; g.Rebuild(); yield return null; yield return null;
L("[A 바닥만] " + Shot(camTop, TS, TS, "A_top_groundonly") + " | " + Stat());
Shot(camPd, W, H, "A_pd_groundonly");
// ── B: 현재 풀 (816n 상태) ──────────────────────────────────
rt.grassEnabled = 1; g.Rebuild(); yield return null; yield return null;
L("[B 현재풀] " + Shot(camTop, TS, TS, "B_top_before") + " | " + Stat());
Shot(camPd, W, H, "B_pd_before");
// ── C: 풀 그림자색만 데모 비율로 (다른 값 전부 동일) ────────
if (grassMat != null)
{
var clone = new UnityEngine.Material(grassMat);
clone.name = grassMat.name + "_816qTest";
var dif = clone.GetColor("_DiffuseColor");
clone.SetColor("_ShadowDiffuseColor", new UnityEngine.Color(dif.r * 0.3232f, dif.g * 0.3414f, dif.b * 0.3948f, dif.a));
for (int i = 0; i < rt.scatter.Length; i++)
if (rt.scatter[i].material == grassMat) rt.scatter[i].material = clone;
g.Rebuild(); yield return null; yield return null;
L("[C 풀그림자=데모비율] " + Shot(camTop, TS, TS, "C_top_shadowfix") + " | " + Stat());
Shot(camPd, W, H, "C_pd_shadowfix");
}
// ── D: 참고 — 데모 기준 캡처에 같은 지표 ────────────────────
Ref("Screenshots_WL/WL816f/x_demo_ground.png", "D 데모참고(스케일 미상)");
Ref("Screenshots_WL/WL816f/x_ours_ground.png", "D 우리참고(스케일 미상)");
Flush();
}
// ─────────────────────────────────────────── 지표
// LF = 8×8 블록 평균 밝기의 표준편차(= 띠 세기) · HF = 블록 내 편차 평균(= 포기 잡음)
// levels = 블록 밝기를 8단계로 양자화했을 때 면적 0.5% 이상인 단계 수(= 띠 단계)
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 (!Ground(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 (!Ground(p)) continue;
hf += System.Math.Abs(Lum(p) - mean); hfn++;
}
}
double bs1 = 0, bs2 = 0; int bn = 0;
var lv = new int[40];
for (int i = 0; i < bm.Length; i++) { if (!bok[i]) continue; bs1 += bm[i]; bs2 += (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 = bs1 / bn;
double lf = System.Math.Sqrt(System.Math.Max(0, bs2 / bn - mu * mu));
int levels = 0; for (int i = 0; i < 40; i++) if (lv[i] >= bn * 0.005) levels++;
// 에지% — 인접 픽셀 밝기차 8 이상
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 (!Ground(p)) continue;
var r = px[y * w + x + 1]; var d = px[(y + 1) * w + x];
edn++;
if ((Ground(r) && System.Math.Abs(Lum(p) - Lum(r)) > 8) || (Ground(d) && System.Math.Abs(Lum(p) - Lum(d)) > 8)) ed++;
}
return string.Format("지면 {0:F1}% · 띠세기LF={1:F2} · 포기잡음HF={2:F2} · LF/HF={3:F2} · 띠단계={4} · 에지%={5:F2} · 평균밝기={6:F1}",
100.0 * edn / (w * h), lf, hf / hfn, lf / System.Math.Max(0.01, hf / hfn), levels, 100.0 * ed / System.Math.Max(1, edn), mu);
}
static bool Ground(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 void Ref(string path, string tag)
{
if (!System.IO.File.Exists(path)) { L(tag + " : 파일 없음 " + path); return; }
var tex = new UnityEngine.Texture2D(2, 2, UnityEngine.TextureFormat.RGB24, false);
if (!UnityEngine.ImageConversion.LoadImage(tex, System.IO.File.ReadAllBytes(path))) { L(tag + " : 읽기 실패"); return; }
L(tag + " " + tex.width + "x" + tex.height + " : " + Metric(tex.GetPixels32(), tex.width, tex.height));
UnityEngine.Object.DestroyImmediate(tex);
}
static string MatDump(UnityEngine.Material m)
{
if (m == null) return "";
return "Shades=" + F(m, "_Shades") + " Bright=" + F(m, "_Brightness") + " MinDark=" + F(m, "_MinimumDarkness")
+ " CloudStr=" + F(m, "_Cloud_Strength") + " CloudDens=" + F(m, "_Cloud_Density") + " Cover=" + F(m, "_Cloud_Cover")
+ " Change=" + F(m, "_Cloud_Change") + " Move=" + (m.HasProperty("_Cloud_Movement") ? m.GetVector("_Cloud_Movement").ToString() : "-")
+ " Dif=" + C(m, "_DiffuseColor") + " Shd=" + C(m, "_ShadowDiffuseColor")
+ " kw=[" + string.Join(",", m.shaderKeywords) + "]";
}
static string F(UnityEngine.Material m, string n) { return m.HasProperty(n) ? m.GetFloat(n).ToString("F4") : "-"; }
static string C(UnityEngine.Material m, string n) { if (!m.HasProperty(n)) return "-"; var c = m.GetColor(n); return "(" + c.r.ToString("F3") + "," + c.g.ToString("F3") + "," + c.b.ToString("F3") + ")"; }
static string RatioStr(UnityEngine.Material m)
{
if (m == null || !m.HasProperty("_DiffuseColor") || !m.HasProperty("_ShadowDiffuseColor")) return "-";
var d = m.GetColor("_DiffuseColor"); var s = m.GetColor("_ShadowDiffuseColor");
return "(" + Safe(s.r, d.r) + "," + Safe(s.g, d.g) + "," + Safe(s.b, d.b) + ")";
}
static string Safe(float a, float b) { return b < 0.001f ? "-" : (a / b).ToString("F3"); }
static string Stat()
{
return "인스턴스=" + WL.Look.Farm.WLIslandGrass.Instances + " 삼각형=" + WL.Look.Farm.WLIslandGrass.Triangles
+ " 드로우콜+" + WL.Look.Farm.WLIslandGrass.DrawnConfigs + " 타일=" + WL.Look.Farm.WLIslandGrass.Tiles;
}
// ─────────────────────────────────────────── 카메라·캡처
static UnityEngine.Camera MakeTop(float cx, float cz, float size)
{
var go = UnityEngine.GameObject.Find("~816qTop");
if (go == null) go = new UnityEngine.GameObject("~816qTop");
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, 60f, cz);
c.enabled = false; return c;
}
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("~816qPd");
if (go == null) go = new UnityEngine.GameObject("~816qPd");
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 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 void L(string s) { sb.AppendLine(s); Flush(); }
static void Flush()
{
System.IO.File.WriteAllText("AgentScripts/WL816q_PLAY.txt", sb.ToString());
UnityEngine.Debug.Log("[816q play]\n" + sb.ToString());
}
}