// 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; } /// 점(Point) 확대 — 도트 격자를 그대로 키운다. 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); } /// 도트A: pixelHeight 240 저해상도 렌더 → 점 확대. 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.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; // ⓐ 일반 모드 — 같은 위치·각도 · 퍼스펙티브 · 풀해상도(도트 없음) 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) — 더 가깝고 낮게 cam.transform.position = new UnityEngine.Vector3(0f, 1.15f, 0f) + UnityEngine.Quaternion.Euler(0f, 205f, 0f) * new UnityEngine.Vector3(0f, 0f, -4.2f) + UnityEngine.Vector3.up * 0.55f; cam.transform.LookAt(new UnityEngine.Vector3(0f, 0.75f, 0f)); Save(DotA(cam, 2.0f), "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; // ⓓ 비교용 — 새 아레나(도트A) 렌더를 먼저 잡아 둔다 cam.transform.position = camPos; cam.transform.rotation = camRot; cam.orthographic = true; cam.orthographicSize = 3.4f; var mine = DotA(cam, 3.4f); 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.FindObjectsSortMode.None)) if (c.gameObject.name == "Main Camera") demoCam = c; UnityEngine.Texture2D demoHalf = null; if (demoCam != null) { // 데모의 마을 중심(House 2채 + Light_Post 일대)을 우리와 같은 내림각 43.61°·도트A 규격으로 var dt = UnityEngine.Object.FindFirstObjectByType(); var fxz = new UnityEngine.Vector3(94f, 0f, 78f); float fy = dt != null ? dt.SampleHeight(fxz) + dt.transform.position.y : 4f; var focus = new UnityEngine.Vector3(fxz.x, fy, fxz.z); 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)); demoCam.transform.position = focus + UnityEngine.Vector3.up * camUp - dir * camHoriz; demoCam.transform.LookAt(focus + UnityEngine.Vector3.up * 1.06f); demoCam.orthographic = true; demoCam.orthographicSize = 3.4f; var d = DotA(demoCam, 3.4f); 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(); } }