225 lines
12 KiB
C#
225 lines
12 KiB
C#
|
|
// 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);
|
||
|
|
}
|
||
|
|
}
|