Project_WL/AgentScripts/WL816k_Probe.cs

219 lines
11 KiB
C#
Raw Permalink 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-816k — ② 데모 물 셰이더를 섬에 씌워 본다 (PD 구도 · 거리 15 m)
// 실측 목표: ⓐ 깨지는가 ⓑ 무늬 크기가 UV 기반인가(평면 100배) ⓒ 섬과 만나는 선에 거품이 생기는가
// 🔴 Play 중 에셋 수정 0 — 전부 런타임 Material 복사본.
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using WL.Look.Farm;
public static class WL816k_Probe
{
public const string D = "Screenshots_WL/WL816k/";
public const int PW = 1280, PH = 720;
public const string MatDir = "Assets/WL/Look/Farm/Materials/";
public static void Open()
{
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/Scenes/InGame.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
}
public static void Start()
{
var go = GameObject.Find("~WL816kP");
if (go != null) Object.DestroyImmediate(go);
go = new GameObject("~WL816kP");
go.AddComponent<WL816k_ProbeRunner>();
}
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, int w, int h)
{
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
var t = Grab(cam, w, h);
System.IO.File.WriteAllBytes(path, t.EncodeToPNG());
var px = t.GetPixels32();
// 바다만 있는 왼쪽 아래 구역
long r = 0, g = 0, b = 0; int n = 0; double sum = 0, sum2 = 0;
int xm = Mathf.Min(340, w), ym = Mathf.Min(320, h);
for (int y = 20; y < ym; y += 2)
for (int x = 20; x < xm; x += 2)
{
var c = px[y * w + x]; r += c.r; g += c.g; b += c.b; n++;
float lum = (0.299f * c.r + 0.587f * c.g + 0.114f * c.b) / 255f;
sum += lum; sum2 += lum * lum;
}
double mean = n > 0 ? sum / n : 0;
double sd = n > 0 ? System.Math.Sqrt(System.Math.Max(0, sum2 / n - mean * mean)) : 0;
// 화면 아래 55 % 에서 밝은/어두운 픽셀
int fn = 0, dn = 0, tot = 0; long fr = 0, fg = 0, fb = 0;
int y1 = (int)(h * 0.55f);
for (int y = 0; y < y1; y++)
for (int x = 0; x < w; x += 2)
{
var c = px[y * w + x]; tot++;
if (c.r > 200 && c.g > 195 && c.b > 195) { fr += c.r; fg += c.g; fb += c.b; fn++; }
if (c.r < 40 && c.g < 40 && c.b < 40) dn++;
}
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 "바다 #" + ((int)(r / n)).ToString("X2") + ((int)(g / n)).ToString("X2") + ((int)(b / n)).ToString("X2")
+ " σ=" + sd.ToString("F4") + " · " + foam + " · 검정 " + (100f * dn / tot).ToString("F2") + "% → " + path;
}
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 WL816k_ProbeRunner : MonoBehaviour
{
static StringBuilder sb;
static void L(string s) { sb.AppendLine(s); }
const string D = WL816k_Probe.D;
const string MatDir = WL816k_Probe.MatDir;
const int PW = WL816k_Probe.PW, PH = WL816k_Probe.PH;
Camera live, zoom;
Renderer water;
MeshRenderer band;
void Start() { StartCoroutine(Co()); }
IEnumerator Co()
{
sb = new StringBuilder();
L("=== WL-816k ② 데모 물 셰이더를 섬에 (PD 구도 · 거리 15 m) ===");
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);
foreach (var r in Object.FindObjectsByType<Renderer>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
if (r != null && r.gameObject.scene.name == "Level01" && r.name == "Water") water = r;
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);
band = foam != null ? foam.GetComponent<MeshRenderer>() : null;
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.name + " pos=" + water.transform.position.ToString("F2")
+ " scale=" + water.transform.localScale.ToString("F1") + " · 지금 mat=" + water.sharedMaterial.name
+ "(queue " + water.sharedMaterial.renderQueue + ")");
L("둘레 띠 = " + (band == null ? "🔴 없음" : "있음 · " + WLShoreFoam.LastLog));
var E = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MatDir + "Farm_Water_WL_E.mat");
var Demo = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MatDir + "Farm_Water_Demo.mat");
var FI = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MatDir + "Farm_SimpleWater_Demo.mat");
L("머티리얼: E=" + (E == null ? "null" : "q" + E.renderQueue) + " Demo=" + (Demo == null ? "null" : "q" + Demo.renderQueue)
+ " FI=" + (FI == null ? "null" : "q" + FI.renderQueue));
if (Demo == null) { L("🔴 Farm_Water_Demo 없음"); Done(); yield break; }
// 물가 확대 카메라
var zgo = new GameObject("~WL816kZ"); zgo.hideFlags = HideFlags.DontSave;
zoom = zgo.AddComponent<Camera>();
zoom.fieldOfView = 34f; zoom.nearClipPlane = 0.1f; zoom.farClipPlane = 2000f;
zoom.clearFlags = CameraClearFlags.Skybox; zoom.cullingMask = ~0; zoom.depth = -100f;
zgo.AddComponent<UniversalAdditionalCameraData>().renderPostProcessing = true;
Bounds b = band != null ? band.bounds : new Bounds(Vector3.zero, new Vector3(16, 1, 16));
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));
// ── 1) 지금(E + 띠) ────────────────────────────────────
L("");
L("1) 지금 상태 (물 E + 띠 on)");
yield return Show(E, true, "a1_now");
// ── 2) 데모 물 그대로 (띠 off) ──────────────────────────
L("");
L("2) 데모 물 그대로 — 원본 값 · 띠 off");
yield return Show(Demo, false, "b1_demo_raw");
// ── 3) 데모 물 + queue 3000 ────────────────────────────
L("");
L("3) 데모 물 · renderQueue 3000 (투명 정렬) · 띠 off");
var q3 = new Material(Demo); q3.renderQueue = 3000;
yield return Show(q3, false, "b2_demo_q3000");
// ── 4) 무늬가 UV 기반인지 — 평면이 100배라 100배 스케일이 필요한가 ──
L("");
L("4) 무늬 스케일 — 데모 평면 10 m vs 섬 물 1000 m (UV 기반이면 ×100 이 필요)");
float[] fs = { 50f, 500f, 5000f };
var names = new string[3];
for (int i = 0; i < 3; i++)
{
var m = new Material(Demo); m.renderQueue = 3000;
m.SetFloat("_FoamScale", fs[i]);
m.SetFloat("_Bands", 78.52f * (fs[i] / 50f));
names[i] = D + "c_scale" + (i + 1) + ".png";
yield return Show(m, false, "c_scale" + (i + 1), " _FoamScale " + fs[i] + " _Bands " + (78.52f * (fs[i] / 50f)));
}
WL816k_Probe.Strip(names, D + "c_scale_steps.png");
// ── 5) 데모 씬 물 자체를 나란히 (기준) ──────────────────
L("");
L("5) [기준] FI SimpleWater (816h 이전 물)");
if (FI != null) yield return Show(FI, false, "d_fi");
Object.DestroyImmediate(zgo);
// 원복
water.sharedMaterial = E; if (band != null) band.enabled = true;
Done();
}
IEnumerator Show(Material m, bool bandOn, string tag, string note = null)
{
water.sharedMaterial = m;
if (band != null) band.enabled = bandOn;
yield return null; yield return new WaitForSeconds(0.35f);
if (note != null) L(note);
L(" PD " + WL816k_Probe.Shot(live, D + tag + ".png", PW, PH));
L(" 물가 " + WL816k_Probe.Shot(zoom, D + tag + "_zoom.png", PW, PH));
}
void Done()
{
System.IO.File.WriteAllText("AgentScripts/WL816k_PROBE.txt", sb.ToString());
Debug.Log("[816k probe]\n" + sb);
}
}