Project_WL/AgentScripts/WL816j2_Verify.cs

286 lines
16 KiB
C#
Raw Normal View History

// WL-816j2 — ② 🔴 판정은 **PD 카메라 구도**(MainCamera · FOV 60 · 45° · 22 m · 1280×720)에서.
// 무늬 선 3단 · 띠 폭 3단 · 거품 색 중립화 · 사방 확인 · 성능(드로우콜·ms/frame) 전/후.
// 🔴 Play 중 에셋 수정 0 — 물은 런타임 Material 복사본, 띠는 컴포넌트의 런타임 오버라이드.
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using WL.Look.Farm;
using FIIsland = CryingSnow.FarmingIsland.Island;
public static class WL816j2_Verify
{
public const string D = "Screenshots_WL/WL816j2/";
public const int PW = 1280, PH = 720; // 🔴 PD 화면과 같은 해상도·화각
public const string C_Light = "Color_1ec6cccf8ead489ebb917674f7e8b3b1";
public const string C_Foam = "Color_e1f155248d144786a62fc1585ca21e9a";
public const string R_FoamDistance = "Vector1_101dc4546b684d40bc2ff2fc59ab43d5";
public const string R_FoamEdge = "Vector1_34f757bec6b8422b9aef3b9d97117a6e";
public static void Open()
{
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/Scenes/InGame.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
}
public static void Start()
{
var go = GameObject.Find("~WL816j2V");
if (go != null) Object.DestroyImmediate(go);
go = new GameObject("~WL816j2V");
go.AddComponent<WL816j2_VerifyRunner>();
}
public static Texture2D Grab(Camera cam, int w, int h)
{
var rt = new RenderTexture(w, h, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
rt.antiAliasing = 1; rt.Create();
var pT = cam.targetTexture; var pA = RenderTexture.active;
cam.targetTexture = rt; cam.Render(); RenderTexture.active = rt;
var t = new Texture2D(w, h, TextureFormat.RGB24, false);
t.ReadPixels(new Rect(0, 0, w, h), 0, 0); t.Apply();
RenderTexture.active = pA; cam.targetTexture = pT; rt.Release(); Object.DestroyImmediate(rt);
return t;
}
/// <summary>PD 구도로 찍고 ⓐ 물 무늬 선 대비 ⓑ 거품(밝은 픽셀) 평균색·비율을 잰다.</summary>
public static string Shot(Camera cam, string path)
{
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
var t = Grab(cam, PW, PH);
System.IO.File.WriteAllBytes(path, t.EncodeToPNG());
var px = t.GetPixels32();
// ⓐ 무늬 선 대비 — 🔴 **바다만 있는 왼쪽 아래 구역**(섬·UI 가 절대 안 들어온다)에서만 잰다
double sum = 0, sum2 = 0; int n = 0;
for (int y = 20; y < 320; y += 2)
for (int x = 20; x < 340; x += 2)
{
var c = px[y * PW + x];
float lum = (0.299f * c.r + 0.587f * c.g + 0.114f * c.b) / 255f;
sum += lum; sum2 += lum * lum; n++;
}
double mean = n > 0 ? sum / n : 0, sd = n > 0 ? System.Math.Sqrt(System.Math.Max(0, sum2 / n - mean * mean)) : 0;
// ⓑ 거품(밝은 픽셀) — 화면 아래 55 %(UI 회피)에서 비율과 색
long fr = 0, fg = 0, fb = 0; int fn = 0, tot = 0;
int y1 = (int)(PH * 0.55f);
for (int y = 0; y < y1; y++)
for (int x = 0; x < PW; x += 2)
{
var c = px[y * PW + x]; tot++;
if (c.r > 200 && c.g > 195 && c.b > 195) { fr += c.r; fg += c.g; fb += c.b; fn++; }
}
Object.DestroyImmediate(t);
string foam = fn == 0 ? "거품 0%" :
"거품 " + (100f * fn / tot).ToString("F2") + "% 색 #"
+ ((int)(fr / fn)).ToString("X2") + ((int)(fg / fn)).ToString("X2") + ((int)(fb / fn)).ToString("X2")
+ " (RB " + ((int)(fr / fn) - (int)(fb / fn)) + ")";
return "바다 무늬대비 σ=" + sd.ToString("F4") + " · " + foam + " → " + path;
}
public static float Ms(Camera cam, int frames)
{
var rt = new RenderTexture(PW, PH, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
rt.antiAliasing = 1; rt.Create();
var pT = cam.targetTexture; 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 = pT; rt.Release(); Object.DestroyImmediate(rt);
return (float)sw.Elapsed.TotalMilliseconds / frames;
}
public static void Strip(string[] paths, string outPath)
{
var ts = new Texture2D[paths.Length]; int w = 0, h = 0;
for (int i = 0; i < paths.Length; i++)
{
if (!System.IO.File.Exists(paths[i])) return;
ts[i] = new Texture2D(2, 2, TextureFormat.RGB24, false);
ts[i].LoadImage(System.IO.File.ReadAllBytes(paths[i]));
w = Mathf.Max(w, ts[i].width); h += ts[i].height;
}
var o = new Texture2D(w, h, TextureFormat.RGB24, false);
var f = new Color32[w * h];
for (int i = 0; i < f.Length; i++) f[i] = new Color32(24, 24, 28, 255);
o.SetPixels32(f);
int y = h;
for (int i = 0; i < ts.Length; i++) { y -= ts[i].height; o.SetPixels32(0, y, ts[i].width, ts[i].height, ts[i].GetPixels32()); }
o.Apply();
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(outPath));
System.IO.File.WriteAllBytes(outPath, o.EncodeToPNG());
Object.DestroyImmediate(o);
for (int i = 0; i < ts.Length; i++) Object.DestroyImmediate(ts[i]);
}
}
public class WL816j2_VerifyRunner : MonoBehaviour
{
static StringBuilder sb;
static void L(string s) { sb.AppendLine(s); }
const string D = WL816j2_Verify.D;
const string MatDir = "Assets/WL/Look/Farm/Materials/";
void Start() { StartCoroutine(Co()); }
IEnumerator Co()
{
sb = new StringBuilder();
L("=== WL-816j2 — PD 구도(FOV 60 · 45° · 22 m · 1280×720)에서 다시 맞춘다 ===");
var op = SceneManager.LoadSceneAsync("Level01", LoadSceneMode.Additive);
while (op != null && !op.isDone) yield return null;
for (int i = 0; i < 8; i++) yield return new WaitForSeconds(1f);
Renderer water = null;
foreach (var r in Object.FindObjectsByType<Renderer>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
if (r != null && r.gameObject.scene.name == "Level01" && r.name == "Water") water = r;
Camera live = null;
foreach (var c in Object.FindObjectsByType<Camera>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
{
if (!c.gameObject.activeInHierarchy || !c.enabled || c.targetTexture != null) continue;
if (c.name.StartsWith("~WL816")) continue;
if (live == null || c.depth > live.depth) live = c;
}
var foam = Object.FindFirstObjectByType<WLShoreFoam>(FindObjectsInactive.Include);
if (water == null || live == null) { L("🔴 물/카메라 없음"); Done(); yield break; }
L("카메라 " + live.name + " pos=" + live.transform.position.ToString("F2") + " 각=" + live.transform.eulerAngles.ToString("F0")
+ " fov=" + live.fieldOfView + " · 거리 " + live.transform.position.magnitude.ToString("F1") + " m");
L("물 = " + water.sharedMaterial.name + " · 둘레 띠 = " + (foam == null ? "🔴 없음" : WLShoreFoam.LastLog));
var E = water.sharedMaterial;
var Dm = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MatDir + "Farm_Water_WL_D.mat");
// ── 1) 지금(main 상태 = 물 D · 띠 없음) ─────────────────────
if (foam != null) foam.GetComponent<MeshRenderer>().enabled = false;
water.sharedMaterial = Dm; yield return null; yield return new WaitForSeconds(0.3f);
L("");
L("1) 지금(main · 물 D · 띠 없음) " + WL816j2_Verify.Shot(live, D + "a_now_pd.png"));
// ── 2) 무늬 선 3단 (띠 없이 · 물만) ──────────────────────────
L("");
L("2) 무늬 흰 선 3단 — LightColor (물색 0.36/0.69/0.82 에 가까울수록 은은)");
var lc = new[] { new Color(0.80f, 0.90f, 0.96f), new Color(0.60f, 0.82f, 0.91f), new Color(0.45f, 0.74f, 0.86f) };
var ls = new string[3];
for (int i = 0; i < 3; i++)
{
var m = new Material(E); m.SetColor(WL816j2_Verify.C_Light, lc[i]);
water.sharedMaterial = m; yield return null; yield return new WaitForSeconds(0.2f);
ls[i] = D + "b_line" + (i + 1) + ".png";
L(" " + lc[i] + " " + WL816j2_Verify.Shot(live, ls[i]));
}
WL816j2_Verify.Strip(ls, D + "b_line_steps.png");
// 데모(FI 원본) 물의 선 대비 = 기준
var FI = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MatDir + "Farm_SimpleWater_Demo.mat");
water.sharedMaterial = FI; yield return null; yield return new WaitForSeconds(0.2f);
L(" [기준] 데모 물(FI SimpleWater) " + WL816j2_Verify.Shot(live, D + "b_line0_demo.png"));
// ── 3) 띠 폭 3단 (물 = E · 선 중간값) ───────────────────────
var pick = new Material(E); pick.SetColor(WL816j2_Verify.C_Light, lc[1]);
water.sharedMaterial = pick; yield return null;
L("");
L("3) 둘레 띠 폭 3단 (PD 구도)");
if (foam != null)
{
foam.GetComponent<MeshRenderer>().enabled = true;
var ws = new string[3]; float[] wid = { 0.9f, 1.6f, 2.6f };
for (int i = 0; i < 3; i++)
{
foam.widthOverride = wid[i]; foam.Rebuild();
yield return null; yield return new WaitForSeconds(0.2f);
ws[i] = D + "c_band" + (i + 1) + ".png";
L(" 폭 " + wid[i] + " m · " + WLShoreFoam.LastLog);
L(" " + WL816j2_Verify.Shot(live, ws[i]));
}
WL816j2_Verify.Strip(ws, D + "c_band_steps.png");
foam.widthOverride = 0f; foam.Rebuild(); yield return null;
}
else L(" 🔴 띠 컴포넌트가 없다");
// ── 4) 거품 색 중립화 — 화면에서 RB 를 0 에 가깝게 ─────────
L("");
L("4) 거품 색 — PD 캡처가 분홍빛이었다. 화면에서 재서 중립 흰색으로 맞춘다(RB 가 0 에 가까울수록 중립)");
if (foam != null)
{
var mr = foam.GetComponent<MeshRenderer>();
var tints = new[] { new Color(1f,1f,1f,0.92f), new Color(0.93f,0.97f,1f,0.92f), new Color(0.86f,0.95f,1f,0.90f) };
var cs = new string[3];
for (int i = 0; i < 3; i++)
{
var mi = new Material(foam.cfg.shoreFoamMaterial); mi.SetColor("_BaseColor", tints[i]);
mr.sharedMaterial = mi; yield return null; yield return new WaitForSeconds(0.2f);
cs[i] = D + "d_tint" + (i + 1) + ".png";
L(" _BaseColor " + tints[i] + " " + WL816j2_Verify.Shot(live, cs[i]));
}
WL816j2_Verify.Strip(cs, D + "d_tint_steps.png");
}
// ── 5) 사방 확인 + 확장 ─────────────────────────────────────
// ── 4-b) 물가 확대 (띠가 어떻게 보이는지) ───────────────────
if (foam != null)
{
var mr2 = foam.GetComponent<MeshRenderer>();
mr2.sharedMaterial = foam.cfg.shoreFoamMaterial;
var b = mr2.bounds;
var zgo = new GameObject("~WL816j2Zoom"); zgo.hideFlags = HideFlags.DontSave;
var zc = zgo.AddComponent<Camera>();
zc.fieldOfView = 34f; zc.nearClipPlane = 0.1f; zc.farClipPlane = 2000f;
zc.clearFlags = CameraClearFlags.Skybox; zc.cullingMask = ~0; zc.depth = -100f;
zgo.AddComponent<UniversalAdditionalCameraData>().renderPostProcessing = true;
zgo.transform.position = new Vector3(b.center.x, 3.2f, b.min.z - 9f);
zgo.transform.LookAt(new Vector3(b.center.x, -1f, b.min.z + 2f));
mr2.enabled = false; yield return null; yield return null;
L("");
L("4-b) 물가 확대 — 띠 off " + WL816j2_Verify.Shot(zc, D + "f_zoom_off.png"));
mr2.enabled = true; yield return null; yield return null;
L(" 물가 확대 — 띠 on " + WL816j2_Verify.Shot(zc, D + "f_zoom_on.png"));
WL816j2_Verify.Strip(new[] { D + "f_zoom_off.png", D + "f_zoom_on.png" }, D + "f_zoom_pair.png");
Object.DestroyImmediate(zgo);
}
L("");
L("5) 사방 확인(위에서) + 섬 확장");
var top = new GameObject("~WL816j2Top"); top.hideFlags = HideFlags.DontSave;
var tc = top.AddComponent<Camera>();
tc.fieldOfView = 55f; tc.nearClipPlane = 0.1f; tc.farClipPlane = 2000f;
tc.clearFlags = CameraClearFlags.Skybox; tc.cullingMask = ~0; tc.depth = -100f;
top.AddComponent<UniversalAdditionalCameraData>().renderPostProcessing = true;
top.transform.position = new Vector3(0f, 42f, -12f); top.transform.LookAt(Vector3.zero);
L(" 위에서 " + WL816j2_Verify.Shot(tc, D + "e_top_allsides.png"));
var islands = Object.FindObjectsByType<FIIsland>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
FIIsland target = null;
foreach (var isl in islands) if (isl != null && isl.IsUnlocked && isl.gameObject.activeInHierarchy) target = isl;
if (target != null)
{
target.gameObject.SetActive(false); yield return new WaitForSeconds(2.5f);
L(" 1칸 잠금 → " + WLShoreFoam.LastLog);
L(" " + WL816j2_Verify.Shot(tc, D + "e_top_locked.png"));
target.gameObject.SetActive(true); yield return new WaitForSeconds(3.5f);
L(" 다시 열기 → " + WLShoreFoam.LastLog + " · 띠 재생성 " + WL.Look.Farm.WLIslandLook.ShoreFoamRebuilds + "회");
L(" " + WL816j2_Verify.Shot(tc, D + "e_top_unlocked.png"));
}
WL816j2_Verify.Strip(new[] { D + "e_top_allsides.png", D + "e_top_locked.png", D + "e_top_unlocked.png" }, D + "e_expand_steps.png");
// ── 6) 성능 — 띠 on/off 교대 2회 ────────────────────────────
L("");
L("6) 성능 (PD 구도 1280×720 · 60프레임 오프스크린 · 순서 교대 2회)");
var fr = foam != null ? foam.GetComponent<MeshRenderer>() : null;
for (int round = 0; round < 2; round++)
{
if (fr != null) { fr.enabled = false; yield return null; yield return null; }
L(" " + round + "회차 띠 off = " + WL816j2_Verify.Ms(live, 60).ToString("F3") + " ms/frame");
if (fr != null) { fr.enabled = true; yield return null; yield return null; }
L(" " + round + "회차 띠 on = " + WL816j2_Verify.Ms(live, 60).ToString("F3") + " ms/frame");
}
L(" 띠 = 렌더러 1 · 머티리얼 1 · 드로우콜 +1 · " + WLShoreFoam.LastLog);
L(" 화면 통계(에디터) drawCalls=" + UnityEditor.UnityStats.drawCalls + " batches=" + UnityEditor.UnityStats.batches
+ " tris=" + UnityEditor.UnityStats.triangles);
Object.DestroyImmediate(top);
Done();
}
void Done()
{
System.IO.File.WriteAllText("AgentScripts/WL816j2_VERIFY.txt", sb.ToString());
Debug.Log("[816j2 verify]\n" + sb);
}
}