Project_WL/AgentScripts/WL814q_Capture.cs

198 lines
10 KiB
C#
Raw Permalink Normal View History

// WL-814q — 캡처 4장(+개요) · 1080×1920 · Screenshots_WL/WL814q/
public static class WL814q_Capture
{
const string Out = "Screenshots_WL/WL814q/";
const string ScenePath = "Assets/WL/Look/Arena/Scenes/WL_ArenaProto.unity";
const string DemoPath = "Assets/3DPixelArtEnvironment/Demo/Demo.unity";
const int W = 1080, H = 1920;
static UnityEngine.Texture2D Shot(UnityEngine.Camera cam, int w, int h)
{
var rt = new UnityEngine.RenderTexture(w, h, 24, UnityEngine.RenderTextureFormat.ARGB32,
UnityEngine.RenderTextureReadWrite.sRGB);
rt.filterMode = UnityEngine.FilterMode.Point;
rt.antiAliasing = 1;
rt.Create();
var prev = cam.targetTexture;
cam.targetTexture = rt;
cam.Render();
var oldActive = UnityEngine.RenderTexture.active;
UnityEngine.RenderTexture.active = rt;
var tex = new UnityEngine.Texture2D(w, h, UnityEngine.TextureFormat.RGB24, false);
tex.ReadPixels(new UnityEngine.Rect(0, 0, w, h), 0, 0);
tex.Apply();
UnityEngine.RenderTexture.active = oldActive;
cam.targetTexture = prev;
rt.Release();
UnityEngine.Object.DestroyImmediate(rt);
return tex;
}
/// <summary>점(Point) 확대 — 도트 격자를 그대로 키운다.</summary>
static UnityEngine.Texture2D Upscale(UnityEngine.Texture2D src, int w, int h)
{
var d = new UnityEngine.Texture2D(w, h, UnityEngine.TextureFormat.RGB24, false);
var sp = src.GetPixels32();
var dp = new UnityEngine.Color32[w * h];
for (int y = 0; y < h; y++)
{
int sy = y * src.height / h; if (sy >= src.height) sy = src.height - 1;
for (int x = 0; x < w; x++)
{
int sx = x * src.width / w; if (sx >= src.width) sx = src.width - 1;
dp[y * w + x] = sp[sy * src.width + sx];
}
}
d.SetPixels32(dp); d.Apply();
return d;
}
static void Save(UnityEngine.Texture2D t, string name)
{
var dir = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), Out);
System.IO.Directory.CreateDirectory(dir);
System.IO.File.WriteAllBytes(System.IO.Path.Combine(dir, name), UnityEngine.ImageConversion.EncodeToPNG(t));
UnityEngine.Object.DestroyImmediate(t);
}
/// <summary>도트A: pixelHeight 240 저해상도 렌더 → 점 확대.</summary>
static UnityEngine.Texture2D DotA(UnityEngine.Camera cam, float orthoSize)
{
bool o = cam.orthographic; float os = cam.orthographicSize;
cam.orthographic = true; cam.orthographicSize = orthoSize;
int ph = 240, pw = UnityEngine.Mathf.RoundToInt(240f * W / H); // 135×240
var small = Shot(cam, pw, ph);
var big = Upscale(small, W, H);
UnityEngine.Object.DestroyImmediate(small);
cam.orthographic = o; cam.orthographicSize = os;
return big;
}
static UnityEngine.Texture2D SideBySide(UnityEngine.Texture2D a, UnityEngine.Texture2D b, int gap)
{
int w = a.width + gap + b.width, h = UnityEngine.Mathf.Max(a.height, b.height);
var d = new UnityEngine.Texture2D(w, h, UnityEngine.TextureFormat.RGB24, false);
var px = new UnityEngine.Color32[w * h];
for (int i = 0; i < px.Length; i++) px[i] = new UnityEngine.Color32(18, 18, 22, 255);
d.SetPixels32(px); d.Apply();
UnityEngine.Graphics.CopyTexture(a, 0, 0, 0, 0, a.width, a.height, d, 0, 0, 0, 0);
UnityEngine.Graphics.CopyTexture(b, 0, 0, 0, 0, b.width, b.height, d, 0, 0, a.width + gap, 0);
return d;
}
public static string Run()
{
var sb = new System.Text.StringBuilder();
var scene = UnityEditor.SceneManagement.EditorSceneManager.OpenScene(ScenePath,
UnityEditor.SceneManagement.OpenSceneMode.Single);
// 🔴 Water_Plane 프리팹의 PlanarReflectionProbe 도 Camera 다 — 이름으로 고른다
UnityEngine.Camera cam = null;
foreach (var c in UnityEngine.Object.FindObjectsByType<UnityEngine.Camera>(
UnityEngine.FindObjectsSortMode.None))
if (c.gameObject.name == "Main Camera") cam = c;
if (cam == null) return "NO CAMERA";
var camPos = cam.transform.position;
var camRot = cam.transform.rotation;
// 🔴 씬을 연 직후 첫 렌더는 터레인 머티리얼/스플랫이 아직 안 붙는다 — 버리는 렌더 2장으로 예열
for (int i = 0; i < 2; i++) UnityEngine.Object.DestroyImmediate(Shot(cam, 64, 64));
// ⓐ 일반 모드 — 같은 위치·각도 · 퍼스펙티브 · 풀해상도(도트 없음)
cam.orthographic = false;
// 주시점까지 거리에서 세로 화각이 ortho 3.4 와 같아지도록 FOV 를 맞춘다(같은 구도 유지)
float dist = UnityEngine.Vector3.Distance(camPos, new UnityEngine.Vector3(0f, 1.06f, 0f));
cam.fieldOfView = 2f * UnityEngine.Mathf.Atan2(3.4f, dist) * UnityEngine.Mathf.Rad2Deg;
var a = Shot(cam, W, H);
Save(a, "a_normal_1080x1920.png");
sb.AppendLine("a_normal: perspective fov=" + cam.fieldOfView.ToString("F2") + " dist=" + dist.ToString("F3"));
cam.orthographic = true; cam.orthographicSize = 3.4f;
// ⓑ 도트A — 같은 구도 · orthoSize 3.4 · pixelHeight 240
Save(DotA(cam, 3.4f), "b_dotA_1080x1920.png");
sb.AppendLine("b_dotA: ortho 3.4 · 135x240 -> point x8");
// ⓒ 캐릭터가 보이는 눈높이 구도(도트A) — 더 가깝고 낮게
// 캐릭터를 크게 — 도트A 각도(43.61°) 유지 · orthoSize 만 줄이고 주시점을 앞으로 밀어 캐릭터를 아래쪽에
cam.transform.position = camPos; cam.transform.rotation = camRot;
var fwd = cam.transform.forward; fwd.y = 0f; fwd.Normalize();
cam.transform.position = camPos + fwd * 1.9f;
Save(DotA(cam, 2.2f), "c_dotA_eye_1080x1920.png");
sb.AppendLine("c_dotA_eye: ortho 2.0 · 135x240 -> point x8 · pos=" + cam.transform.position);
// 개요(참고) — 아레나 전체 + 연못 + 풍차 (퍼스펙티브 3/4 뷰)
cam.orthographic = false;
cam.fieldOfView = 40f;
cam.transform.position = new UnityEngine.Vector3(30f, 20f, -33f);
cam.transform.LookAt(new UnityEngine.Vector3(1f, 1.5f, -1f));
Save(Shot(cam, W, H), "e_overview_1080x1920.png");
sb.AppendLine("e_overview: perspective fov40 @" + cam.transform.position);
cam.orthographic = true; cam.orthographicSize = 3.4f;
// ⓓ 비교용 — 데모의 부품은 우리보다 5~6배 크다(House 9.2 m · Tree 13.5 m vs 캐릭터 1.19 m).
// 도트A(ortho 3.4)로 데모를 찍으면 집 벽 한 조각만 나온다 → 두 씬 모두 ortho 10 · 같은 내림각 43.61° 로 맞춘다.
const float CmpOrtho = 10f;
float kk = CmpOrtho / 3.4f; // 각도 유지 · 카메라만 뒤로(near 0.3 에 잘리지 않게)
cam.transform.position = camPos * kk; cam.transform.rotation = camRot;
cam.orthographic = true; cam.orthographicSize = CmpOrtho;
var mine = DotA(cam, CmpOrtho);
var mineHalf = Upscale(mine, W / 2, H / 2);
UnityEngine.Object.DestroyImmediate(mine);
// 텍스처를 씬 전환에서 지키기
mineHalf.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
// 데모 씬 — 같은 규격(도트A)으로. 🔴 저장하지 않는다.
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(DemoPath,
UnityEditor.SceneManagement.OpenSceneMode.Single);
UnityEngine.Camera demoCam = null;
foreach (var c in UnityEngine.Object.FindObjectsByType<UnityEngine.Camera>(
UnityEngine.FindObjectsSortMode.None))
if (c.gameObject.name == "Main Camera") demoCam = c;
UnityEngine.Texture2D demoHalf = null;
if (demoCam != null)
{
// 데모의 집 앞(House (2))을 기준점으로 — 오브젝트 바운즈에서 직접 발밑을 잡는다(지형 추정 금지)
var focus = new UnityEngine.Vector3(94f, 4f, 78f);
var hg = UnityEngine.GameObject.Find("House (2)");
if (hg == null) hg = UnityEngine.GameObject.Find("Light_Post");
if (hg != null)
{
var hr = hg.GetComponentInChildren<UnityEngine.Renderer>();
if (hr != null)
{
var hb = hr.bounds;
focus = new UnityEngine.Vector3(hb.center.x, hb.min.y, hb.center.z);
sb.AppendLine("demo focus = " + hg.name + " bounds " + hb.size + " @ " + focus);
}
}
float camUp = 5.4335f, camHoriz = 4.5911f;
float yaw = 215f;
var dir = new UnityEngine.Vector3(UnityEngine.Mathf.Sin(yaw * UnityEngine.Mathf.Deg2Rad), 0f,
UnityEngine.Mathf.Cos(yaw * UnityEngine.Mathf.Deg2Rad));
float k = CmpOrtho / 3.4f; // 같은 각도를 유지한 채 카메라만 뒤로
demoCam.transform.position = focus + UnityEngine.Vector3.up * camUp * k - dir * camHoriz * k;
demoCam.transform.LookAt(focus + UnityEngine.Vector3.up * 1.06f);
demoCam.orthographic = true; demoCam.orthographicSize = CmpOrtho;
var d = DotA(demoCam, CmpOrtho);
demoHalf = Upscale(d, W / 2, H / 2);
demoHalf.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
UnityEngine.Object.DestroyImmediate(d);
sb.AppendLine("demo shot ok @" + demoCam.transform.position);
}
else sb.AppendLine("DEMO CAMERA NOT FOUND");
if (demoHalf != null)
{
var cmp = SideBySide(demoHalf, mineHalf, 16);
Save(cmp, "d_compare_demo_vs_arena.png");
sb.AppendLine("d_compare: left=Critter Demo · right=WL_ArenaProto (both dotA 240p)");
UnityEngine.Object.DestroyImmediate(demoHalf);
}
UnityEngine.Object.DestroyImmediate(mineHalf);
// 🔴 데모 씬은 저장하지 않고 원래 씬으로 되돌린다
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(ScenePath,
UnityEditor.SceneManagement.OpenSceneMode.Single);
sb.AppendLine("OUT " + System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), Out));
return sb.ToString();
}
}