219 lines
11 KiB
C#
219 lines
11 KiB
C#
// 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")
|
||
+ "(R−B " + ((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);
|
||
}
|
||
}
|