225 lines
10 KiB
C#
225 lines
10 KiB
C#
// WL-814t 캡처 — 레퍼런스 느낌 4요소 단독/단계/종합 (Play 중 한 프레임 안에서 전부 렌더)
|
||
// 한 프레임 안에서 찍는 이유: 구름 그림자(_Time)·포즈가 프레임마다 움직여 세션 간 비교가 노이즈투성이가 된다(814s 미확인⑤).
|
||
// run_script --file AgentScripts/WL814t_Capture.cs --entry WL814t_Capture.Run
|
||
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using WL.Look.Arena;
|
||
|
||
public static class WL814t_Capture
|
||
{
|
||
const string Dir = @"E:\NerdNavis\WL_wt\gameplay\Screenshots_WL\WL814t";
|
||
const int W = 1080, H = 1920;
|
||
const float Close = 3.4f; // 도트A 프리셋과 같은 직교 반높이 = 비교 기준
|
||
const float Wide = 10f; // 아레나 씬의 실제 카메라
|
||
|
||
static Camera s_cam;
|
||
static Vector3 s_camPos, s_charCenter;
|
||
static float s_camSize;
|
||
static System.Text.StringBuilder s_log;
|
||
|
||
public static string Run()
|
||
{
|
||
s_log = new System.Text.StringBuilder();
|
||
System.IO.Directory.CreateDirectory(Dir);
|
||
|
||
s_cam = Camera.main;
|
||
if (s_cam == null) { var cs = Object.FindObjectsByType<Camera>(FindObjectsSortMode.None); if (cs.Length > 0) s_cam = cs[0]; }
|
||
if (s_cam == null) return "no camera";
|
||
s_camPos = s_cam.transform.position; s_camSize = s_cam.orthographicSize;
|
||
|
||
var pc = GameObject.Find("PC_LH_M05");
|
||
var rs = pc != null ? pc.GetComponentsInChildren<Renderer>(false) : new Renderer[0];
|
||
Bounds b = new Bounds(pc != null ? pc.transform.position : Vector3.zero, Vector3.zero);
|
||
bool first = true;
|
||
foreach (var r in rs) { if (r == null || !r.enabled) continue; if (first) { b = r.bounds; first = false; } else b.Encapsulate(r.bounds); }
|
||
s_charCenter = b.center;
|
||
s_log.AppendLine("charCenter=" + s_charCenter.ToString("F3") + " bodyH=" + b.size.y.ToString("F3")
|
||
+ " · 화면 캐릭터 키 @ortho3.4 = " + (b.size.y / (2f * Close) * H).ToString("F0") + " px · @ortho10 = " + (b.size.y / (2f * Wide) * H).ToString("F0") + " px");
|
||
|
||
var cfg = WLReferenceLookSettings.Instance;
|
||
if (cfg == null) return "no settings asset";
|
||
|
||
// ── ⓐ 지금(814s) — 아무것도 안 얹은 상태
|
||
Shot2("a_now", Nothing(cfg), Close, 0);
|
||
Shot2("a_now_wide", Nothing(cfg), Wide, 0);
|
||
Shot2("a_now_pixel", Nothing(cfg), Close, 240);
|
||
|
||
// ── 4요소 단독
|
||
Shot2("b_outline_only", Only(cfg, true, false, false, false), Close, 0);
|
||
Shot2("c_contrast_only", Only(cfg, false, true, false, false), Close, 0);
|
||
Shot2("d_mood_only", Only(cfg, false, false, true, false), Close, 0);
|
||
Shot2("e_rim_only", Only(cfg, false, false, false, true), Close, 0);
|
||
|
||
// ── Ⓐ 외곽선 4단 — 두께는 **아레나 실제 카메라(직교 10)** 기준 px (814u 실측 기준과 같은 축)
|
||
float[] ws = { 1f, 2f, 3f, 4f };
|
||
for (int i = 0; i < ws.Length; i++)
|
||
{
|
||
var o = Only(cfg, true, false, false, false);
|
||
o.outlineWidth = ws[i]; o.outlineWidthBackground = Mathf.Max(1f, ws[i] * 2.5f);
|
||
Shot2("o" + (i + 1) + "_outline_" + ws[i].ToString("F0") + "px", o, Close, 0);
|
||
}
|
||
// 외곽선 색 — 균일 검정 vs 표면색 연동(레퍼런스는 부위마다 선 색이 다르다)
|
||
{
|
||
var o = Only(cfg, true, false, false, false); o.outlineWidth = 1f;
|
||
o.outlineColor = Color.black; o.outlineTintAmount = 0f; Shot2("o5_color_uniformblack", o, Close, 0);
|
||
o.outlineTintAmount = 1f; Shot2("o6_color_surfacetint", o, Close, 0);
|
||
o.outlineColor = new Color(0.043f, 0.031f, 0.067f, 1f); o.outlineTintAmount = 0f; Shot2("o7_color_darkviolet", o, Close, 0);
|
||
}
|
||
|
||
// ── Ⓐ 면적 실측용 (직교 10 = 인게임 크기 · 외곽선을 마젠타로 칠해 픽셀을 센다)
|
||
MeasureOutlineArea(cfg, pc);
|
||
|
||
// ── Ⓑ 대비 3단
|
||
float[] mds = { 0.10f, 0.05f, 0.0f };
|
||
for (int i = 0; i < mds.Length; i++)
|
||
{
|
||
var o = Only(cfg, false, true, false, false); o.minimumDarkness = mds[i]; o.charShades = 0;
|
||
Shot2("c" + (i + 1) + "_darkness_" + mds[i].ToString("F2"), o, Close, 0);
|
||
}
|
||
|
||
// ── Ⓑ2 캐릭터 색단계 7 / 4 / 3 (레퍼런스2 = 부위당 2~3단)
|
||
int[] shades = { 0, 4, 3 };
|
||
for (int i = 0; i < shades.Length; i++)
|
||
{
|
||
var o = Only(cfg, false, true, false, false); o.charShades = shades[i];
|
||
Shot2("s" + (i + 1) + "_shades_" + (shades[i] == 0 ? 7 : shades[i]), o, Close, 0);
|
||
}
|
||
|
||
// ── Ⓒ 분위기 3단
|
||
float[] ms = { 0.35f, 0.65f, 1.0f };
|
||
for (int i = 0; i < ms.Length; i++)
|
||
{
|
||
var o = Only(cfg, false, false, true, false); o.moodStrength = ms[i];
|
||
Shot2("m" + (i + 1) + "_mood_" + ms[i].ToString("F2"), o, Close, 0);
|
||
}
|
||
|
||
// ── Ⓓ 림 3단
|
||
float[] rss = { 0.6f, 1.25f, 2.2f };
|
||
for (int i = 0; i < rss.Length; i++)
|
||
{
|
||
var o = Only(cfg, false, false, false, true); o.rimStrength = rss[i];
|
||
Shot2("r" + (i + 1) + "_rim_" + rss[i].ToString("F2"), o, Close, 0);
|
||
}
|
||
|
||
// ── ⓕ 종합안 (비픽셀)
|
||
var fin = WLReferenceLook.Opt.FromSettings(cfg);
|
||
Shot2("f_final", fin, Close, 0);
|
||
Shot2("f_final_wide", fin, Wide, 0);
|
||
|
||
// ── ⓕ2 종합안 + 도트 — 도트A 프리셋(orthoSize 3.4 · pixelHeight 240)
|
||
var fp = fin; fp.outlineWidth = cfg.outlineWidthPixelMode; fp.outlineWidthBackground = Mathf.Max(0.5f, cfg.outlineWidthPixelMode * 0.5f);
|
||
Shot2("f_final_pixel", fp, Close, 240);
|
||
var fp2 = fp; fp2.outlineWidth = cfg.outlineWidthPixelMode * 2f; fp2.outlineWidthBackground = cfg.outlineWidthPixelMode;
|
||
Shot2("f_final_pixel_w2", fp2, Close, 240);
|
||
Shot2("f_final_pixel_thick", fin, Close, 240); // 비픽셀 두께 그대로 → 저해상도에서 뭉개지는지
|
||
|
||
// ── 원복 확인
|
||
Shot2("z_restored_check", Nothing(cfg), Close, 0);
|
||
s_cam.transform.position = s_camPos; s_cam.orthographicSize = s_camSize;
|
||
|
||
s_log.AppendLine("DONE");
|
||
return s_log.ToString();
|
||
}
|
||
|
||
/// <summary>외곽선 띠가 캐릭터 면적의 몇 %를 먹는지 재기 위한 렌더 묶음(파이썬이 픽셀을 센다).</summary>
|
||
static void MeasureOutlineArea(WLReferenceLookSettings cfg, GameObject pc)
|
||
{
|
||
var rs = pc != null ? pc.GetComponentsInChildren<Renderer>(true) : new Renderer[0];
|
||
var on = new List<Renderer>();
|
||
foreach (var r in rs) if (r != null && r.enabled) on.Add(r);
|
||
|
||
// 배경만(캐릭터 끔)
|
||
WLReferenceLook.Restore();
|
||
foreach (var r in on) r.enabled = false;
|
||
s_cam.orthographicSize = Wide; s_cam.transform.position = s_charCenter - s_cam.transform.forward * 30f;
|
||
Shot("mx_bg", 0);
|
||
foreach (var r in on) r.enabled = true;
|
||
|
||
// 외곽선 없음
|
||
Shot2("mx_w0", Nothing(cfg), Wide, 0);
|
||
|
||
// 마젠타 외곽선 1~4 px (배경엔 안 넣는다 = 캐릭터만 센다)
|
||
float[] ws = { 1f, 2f, 3f, 4f };
|
||
for (int i = 0; i < ws.Length; i++)
|
||
{
|
||
var o = Only(cfg, true, false, false, false);
|
||
o.outlineWidth = ws[i]; o.outlineOnBackground = false;
|
||
o.outlineColor = new Color(1f, 0f, 1f, 1f); o.outlineTintAmount = 0f;
|
||
Shot2("mx_w" + ws[i].ToString("F0"), o, Wide, 0);
|
||
}
|
||
}
|
||
|
||
static WLReferenceLook.Opt Nothing(WLReferenceLookSettings cfg)
|
||
{
|
||
var o = WLReferenceLook.Opt.FromSettings(cfg);
|
||
o.outline = false; o.contrast = false; o.mood = false; o.rim = false;
|
||
return o;
|
||
}
|
||
|
||
static WLReferenceLook.Opt Only(WLReferenceLookSettings cfg, bool a, bool b, bool c, bool d)
|
||
{
|
||
var o = WLReferenceLook.Opt.FromSettings(cfg);
|
||
o.outline = a; o.contrast = b; o.mood = c; o.rim = d;
|
||
return o;
|
||
}
|
||
|
||
/// <summary>카메라를 먼저 맞춘 뒤(줌 비례 두께가 카메라를 읽는다) 적용 → 렌더.</summary>
|
||
static void Shot2(string name, WLReferenceLook.Opt o, float orthoSize, int pixelHeight)
|
||
{
|
||
s_cam.orthographicSize = orthoSize;
|
||
s_cam.transform.position = s_charCenter - s_cam.transform.forward * 30f;
|
||
WLReferenceLook.Apply(o, WLReferenceLookSettings.Instance);
|
||
Shot(name, pixelHeight);
|
||
}
|
||
|
||
/// <summary>한 장 렌더. pixelHeight 0 = 1080×1920 그대로, >0 = 저해상도 RT 로 찍어 점보간 확대(도트).</summary>
|
||
static void Shot(string name, int pixelHeight)
|
||
{
|
||
int rw = W, rh = H;
|
||
if (pixelHeight > 0) { rh = pixelHeight; rw = Mathf.RoundToInt(pixelHeight * (float)W / H); }
|
||
|
||
var rt = new RenderTexture(rw, rh, 24, RenderTextureFormat.ARGB32);
|
||
rt.antiAliasing = 1;
|
||
rt.filterMode = pixelHeight > 0 ? FilterMode.Point : FilterMode.Bilinear;
|
||
rt.Create();
|
||
|
||
var prevTarget = s_cam.targetTexture;
|
||
s_cam.targetTexture = rt;
|
||
s_cam.Render();
|
||
s_cam.targetTexture = prevTarget;
|
||
|
||
var prevActive = RenderTexture.active;
|
||
RenderTexture.active = rt;
|
||
var src = new Texture2D(rw, rh, TextureFormat.RGB24, false);
|
||
src.ReadPixels(new Rect(0, 0, rw, rh), 0, 0);
|
||
src.Apply();
|
||
RenderTexture.active = prevActive;
|
||
|
||
Texture2D outTex = src;
|
||
if (pixelHeight > 0)
|
||
{
|
||
outTex = new Texture2D(W, H, TextureFormat.RGB24, false);
|
||
var sp = src.GetPixels32();
|
||
var dp = new Color32[W * H];
|
||
for (int y = 0; y < H; y++)
|
||
{
|
||
int sy = Mathf.Min(rh - 1, y * rh / H);
|
||
for (int x = 0; x < W; x++)
|
||
{
|
||
int sx = Mathf.Min(rw - 1, x * rw / W);
|
||
dp[y * W + x] = sp[sy * rw + sx];
|
||
}
|
||
}
|
||
outTex.SetPixels32(dp); outTex.Apply();
|
||
}
|
||
|
||
System.IO.File.WriteAllBytes(System.IO.Path.Combine(Dir, name + ".png"), outTex.EncodeToPNG());
|
||
s_log.AppendLine("shot " + name + " " + rw + "x" + rh + " · ortho " + s_cam.orthographicSize.ToString("F2") + " · " + WLReferenceLook.LastLog);
|
||
|
||
if (outTex != src) Object.Destroy(outTex);
|
||
Object.Destroy(src);
|
||
rt.Release(); Object.Destroy(rt);
|
||
}
|
||
}
|