Project_WL/AgentScripts/WL816j_Foam.cs

225 lines
12 KiB
C#
Raw Permalink Normal View History

// WL-816j — ③ 가장자리 물보라가 「값만으로」 되는지 실측 (가장 싼 해법 먼저)
// 깊이 텍스처가 살아 있는지 · 섬 벽이 수직이라 띠가 0 폭인지 · FoamDistance 를 키우면 되는지.
// 🔴 자기 완결 파일(run_script 는 파일 1개만 컴파일한다).
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public static class WL816j_Foam
{
public const string D = "Screenshots_WL/WL816j/";
public const float PlaneW = 1000f, Cells = 6f;
public const string R_Size = "Vector1_07238257334b4a149e675e2451801030";
public const string R_Speed = "Vector1_8e22a98f75c94b218c49e6c4805e799d";
public const string R_Strength = "Vector1_99cc56b5c6fd474082f07f00a6df94f9";
public const string R_NoiseSpeed = "Vector1_ad7e0fcd37f44d68b68de6f06bb43b96";
public const string R_FoamScale = "Vector1_11671d7a4cdb4d059a6c69e39f3f7b3f";
public const string R_FoamSpeed = "Vector1_3089b6a325a44c1686907af4ee3b0776";
public const string R_FoamDistance = "Vector1_101dc4546b684d40bc2ff2fc59ab43d5";
public const string R_FoamEdge = "Vector1_34f757bec6b8422b9aef3b9d97117a6e";
public const string R_DeepDistance = "Vector1_566e288f42864b8e9432d81fbdb83f38";
public const string C_Light = "Color_1ec6cccf8ead489ebb917674f7e8b3b1";
public static void Start()
{
var go = GameObject.Find("~WL816jFoam");
if (go != null) Object.DestroyImmediate(go);
go = new GameObject("~WL816jFoam");
go.AddComponent<WL816j_FoamRunner>();
Debug.Log("[816j] foam 러너 시작");
}
public static Material Make(Material src, float cellM, float flowMs)
{
var m = new Material(src);
float size = PlaneW / (cellM * Cells);
m.SetFloat(R_Size, size);
m.SetFloat(R_Strength, 0.3085f / size);
m.SetFloat(R_Speed, flowMs * size / PlaneW);
m.SetFloat(R_NoiseSpeed, flowMs * size / PlaneW);
return m;
}
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 tex = new Texture2D(w, h, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(0, 0, w, h), 0, 0); tex.Apply();
RenderTexture.active = pA; cam.targetTexture = pT;
rt.Release(); Object.DestroyImmediate(rt);
return tex;
}
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, white = 0;
for (int i = 0; i < px.Length; i += 7)
{ r += px[i].r; g += px[i].g; b += px[i].b; n++; if (px[i].r > 215 && px[i].g > 215 && px[i].b > 215) white++; }
Object.DestroyImmediate(t);
return "평균 #" + ((int)(r / n)).ToString("X2") + ((int)(g / n)).ToString("X2") + ((int)(b / n)).ToString("X2")
+ " 흰 " + (100f * white / n).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 += ts[i].width; h = Mathf.Max(h, ts[i].height);
}
var o = new Texture2D(w, h, TextureFormat.RGB24, false);
var fill = new Color32[w * h];
for (int i = 0; i < fill.Length; i++) fill[i] = new Color32(24, 24, 28, 255);
o.SetPixels32(fill);
int x = 0;
for (int i = 0; i < ts.Length; i++) { o.SetPixels32(x, 0, ts[i].width, ts[i].height, ts[i].GetPixels32()); x += ts[i].width; }
o.Apply();
System.IO.File.WriteAllBytes(outPath, o.EncodeToPNG());
Object.DestroyImmediate(o);
for (int i = 0; i < ts.Length; i++) Object.DestroyImmediate(ts[i]);
}
}
public class WL816j_FoamRunner : MonoBehaviour
{
static StringBuilder sb;
static void L(string s) { sb.AppendLine(s); }
const string D = WL816j_Foam.D;
void Start() { StartCoroutine(Co()); }
IEnumerator Co()
{
sb = new StringBuilder();
L("=== WL-816j ③ 가장자리 물보라 — 값만으로 되는가 ===");
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("~WL816j")) continue;
if (live == null || c.depth > live.depth) live = c;
}
if (water == null || live == null) { L("🔴 물/카메라 없음"); Done(); yield break; }
var baseMat = water.sharedMaterial;
var urp = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
L("1) 파이프라인 = " + (urp ? urp.name : "없음")
+ " · depthTexture=" + (urp ? urp.supportsCameraDepthTexture.ToString() : "?")
+ " · opaqueTexture=" + (urp ? urp.supportsCameraOpaqueTexture.ToString() : "?"));
var acd = live.GetComponent<UniversalAdditionalCameraData>();
L(" 게임 카메라 " + live.name + " pos=" + live.transform.position + " 각=" + live.transform.eulerAngles
+ " fov=" + live.fieldOfView + " · requiresDepth=" + (acd ? acd.requiresDepthOption.ToString() : "컴포넌트없음")
+ " · requiresColor=" + (acd ? acd.requiresColorOption.ToString() : "-"));
L(" 물 머티리얼 queue=" + baseMat.renderQueue + " shader=" + baseMat.shader.name);
// ── 2) 섬 가장자리 단면 ─────────────────────────────────────
float zc = -3.78f, shoreX = 0f;
for (float x = 4f; x < 90f; x += 0.25f)
{
RaycastHit h;
if (Physics.Raycast(new Vector3(x, 60f, zc), Vector3.down, out h, 200f) && h.point.y > -0.9f) shoreX = x;
}
var prof = new StringBuilder();
for (float d = -2f; d <= 3.01f; d += 0.5f)
{
RaycastHit h;
bool hit = Physics.Raycast(new Vector3(shoreX + d, 60f, zc), Vector3.down, out h, 200f);
prof.Append(" " + d.ToString("F1") + "→" + (hit ? h.point.y.ToString("F2") + "(" + h.collider.name + ")" : "없음"));
}
L("");
L("2) 해안선 x=" + shoreX.ToString("F2") + " (z=" + zc + ") · 물 y=-1 · 바깥으로 가며 바닥 높이 =" + prof);
var cgo = GameObject.Find("~WL816jShore2");
if (cgo == null) { cgo = new GameObject("~WL816jShore2"); cgo.hideFlags = HideFlags.DontSave; }
var cam = cgo.GetComponent<Camera>(); if (cam == null) cam = cgo.AddComponent<Camera>();
cam.orthographic = false; cam.fieldOfView = 32f; cam.nearClipPlane = 0.1f; cam.farClipPlane = 2000f;
cam.clearFlags = CameraClearFlags.Skybox; cam.cullingMask = ~0; cam.depth = -100f;
var ad = cgo.GetComponent<UniversalAdditionalCameraData>();
if (ad == null) ad = cgo.AddComponent<UniversalAdditionalCameraData>();
ad.renderPostProcessing = true;
cgo.transform.position = new Vector3(shoreX + 5.5f, 2.2f, zc + 5.5f);
cgo.transform.LookAt(new Vector3(shoreX - 0.5f, -1f, zc - 0.5f));
L(" 확대 카메라 pos=" + cgo.transform.position);
// ── 3) 깊이 살아 있나 ───────────────────────────────────────
L("");
L("3) 깊이 텍스처 동작 — DeepDistance 만 바꿔 화면색이 변하는가");
foreach (float dd in new float[] { 0.5f, 7f, 400f })
{
var m = new Material(baseMat); m.SetFloat(WL816j_Foam.R_DeepDistance, dd);
water.sharedMaterial = m; yield return null; yield return null;
L(" DeepDistance " + dd + " → " + WL816j_Foam.Shot(cam, D + "h_deep_" + dd + ".png", 720, 1280));
}
// ── 4) 값만으로 되는가 ──────────────────────────────────────
L("");
L("4) 🔴 값만으로 되는가 — FoamEdge 1 고정 · FoamDistance 훑기 (무늬 1.67 m · 거품알 0.75 m)");
var sweep = new float[] { 2.2f, 6f, 20f, 60f, 200f };
var shots = new string[sweep.Length];
for (int i = 0; i < sweep.Length; i++)
{
var m = WL816j_Foam.Make(baseMat, 1.67f, 0.8f);
m.SetFloat(WL816j_Foam.R_FoamScale, 1000f / 0.75f);
m.SetFloat(WL816j_Foam.R_FoamSpeed, 0.5f * (1000f / 0.75f) / 1000f);
m.SetFloat(WL816j_Foam.R_FoamEdge, 1f);
m.SetFloat(WL816j_Foam.R_FoamDistance, sweep[i]);
water.sharedMaterial = m; yield return null; yield return null;
shots[i] = D + "h_foamdist" + i + ".png";
L(" FoamDistance " + sweep[i] + " → " + WL816j_Foam.Shot(cam, shots[i], 720, 1280));
}
WL816j_Foam.Strip(shots, D + "h_foamdist_sweep.png");
// ── 5) 무늬 대비 3단 ────────────────────────────────────────
L("");
L("5) 무늬 대비 3단 — 흰 줄 밝기(LightColor)만. WaterColor/DarkColor 는 816h 채택값 그대로.");
var lc = new Color[] { new Color(1f, 1f, 1f, 1f), new Color(0.80f, 0.90f, 0.96f, 1f), new Color(0.62f, 0.78f, 0.88f, 1f) };
var lcShots = new string[3];
for (int i = 0; i < 3; i++)
{
var m = WL816j_Foam.Make(baseMat, 1.67f, 0.8f);
m.SetColor(WL816j_Foam.C_Light, lc[i]);
water.sharedMaterial = m; yield return null; yield return null;
lcShots[i] = D + "i_contrast" + (i + 1) + ".png";
L(" LightColor " + lc[i] + " → " + WL816j_Foam.Shot(live, lcShots[i], 1080, 1920));
}
WL816j_Foam.Strip(lcShots, D + "i_contrast_steps.png");
// ── 6) 무늬 크기 (게임 카메라) ──────────────────────────────
L("");
L("6) 무늬 크기 (게임 카메라 · 대비는 중간값) — 지금 21.3 m / 4 m / 2.5 m / 1.67 m");
var cellShots = new string[4];
float[] cs = { 21.3f, 4f, 2.5f, 1.67f };
for (int i = 0; i < 4; i++)
{
Material m;
if (i == 0) m = baseMat;
else { m = WL816j_Foam.Make(baseMat, cs[i], 0.8f); m.SetColor(WL816j_Foam.C_Light, lc[1]); }
water.sharedMaterial = m; yield return null; yield return null;
cellShots[i] = D + "j_cell_game" + i + ".png";
L(" 1칸 " + cs[i] + " m → " + WL816j_Foam.Shot(live, cellShots[i], 1080, 1920));
}
WL816j_Foam.Strip(cellShots, D + "j_cell_game_steps.png");
water.sharedMaterial = baseMat; yield return null;
Done();
}
void Done()
{
System.IO.File.WriteAllText("AgentScripts/WL816j_FOAM.txt", sb.ToString());
Debug.Log("[816j foam]\n" + sb);
}
}