using UnityEngine; using System; using System.IO; /// /// 스크린샷 캡처 관리. /// RenderStreamOutput.CaptureTexture (후처리 + 알파 합성 결과) 를 PNG 로 저장. /// - CaptureScreenshot() : RGB 만 (알파 = 1, 일반 사진) /// - CaptureAlphaScreenshot() : RGBA 그대로 (배경 투명, 합성 소스용) /// 카메라 해상도 그대로 ReadPixels — 별도 임시 RT/카메라 변경 없음. /// [Serializable] public class ScreenshotManager { [Tooltip("RenderStreamOutput 컴포넌트 (비어있으면 씬에서 자동 검색)")] public RenderStreamOutput renderStream; [Tooltip("스크린샷 저장 경로 (비어있으면 프로젝트 루트의 Screenshots/)")] public string screenshotSavePath = ""; [Tooltip("파일명 앞에 붙을 접두사")] public string screenshotFilePrefix = "Screenshot"; [Tooltip("(하위 호환) 외부 코드가 .screenshotCamera 로 접근하면 RenderStreamOutput.MainCam 반환")] public Camera screenshotCamera => renderStream != null ? renderStream.MainCam : null; private Action log; private Action logError; public void Initialize(Action log, Action logError) { this.log = log; this.logError = logError; if (renderStream == null) renderStream = UnityEngine.Object.FindFirstObjectByType(); if (renderStream == null) logError?.Invoke("RenderStreamOutput 컴포넌트를 찾을 수 없습니다 — 알파 합성 캡처 불가"); if (string.IsNullOrEmpty(screenshotSavePath)) screenshotSavePath = Path.Combine(Application.dataPath, "..", "Screenshots"); if (!Directory.Exists(screenshotSavePath)) { Directory.CreateDirectory(screenshotSavePath); log?.Invoke($"Screenshots 폴더 생성됨: {screenshotSavePath}"); } } /// RGB 만, 알파는 1 로 저장하는 일반 PNG (평범한 사진) public void CaptureScreenshot() { if (renderStream == null || renderStream.CaptureTexture == null) { logError?.Invoke("RenderStreamOutput.CaptureTexture 가 준비되지 않았습니다"); return; } SaveRtAsPng(renderStream.CaptureTexture, GenerateFileName("png"), TextureFormat.RGB24); } /// RGBA 모두 저장 — 배경 투명, OBS/합성 소스용 public void CaptureAlphaScreenshot() { if (renderStream == null || renderStream.CaptureTexture == null) { logError?.Invoke("RenderStreamOutput.CaptureTexture 가 준비되지 않았습니다"); return; } SaveRtAsPng(renderStream.CaptureTexture, GenerateFileName("png", "_Alpha"), TextureFormat.RGBA32); } public void OpenScreenshotFolder() { if (Directory.Exists(screenshotSavePath)) { System.Diagnostics.Process.Start(screenshotSavePath); log?.Invoke($"저장 폴더 열기: {screenshotSavePath}"); } else { logError?.Invoke($"저장 폴더가 존재하지 않습니다: {screenshotSavePath}"); } } private void SaveRtAsPng(RenderTexture rt, string fileName, TextureFormat format) { string fullPath = Path.Combine(screenshotSavePath, fileName); RenderTexture prevActive = RenderTexture.active; RenderTexture sRgbRT = null; Texture2D tex = null; try { // HDR/Linear RT → sRGB RT 로 Blit. ReadPixels 는 색공간 변환을 안 하므로 이걸 거치지 // 않으면 PNG 가 Linear 값을 sRGB 슬롯에 그대로 담아 OBS(Spout 결과)와 다르게 보임. sRgbRT = RenderTexture.GetTemporary(rt.width, rt.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); Graphics.Blit(rt, sRgbRT); RenderTexture.active = sRgbRT; tex = new Texture2D(rt.width, rt.height, format, false); tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); File.WriteAllBytes(fullPath, tex.EncodeToPNG()); log?.Invoke($"스크린샷 저장 완료: {fullPath} ({rt.width}x{rt.height})"); } catch (Exception e) { logError?.Invoke($"스크린샷 저장 실패 ({fileName}): {e.Message}"); } finally { RenderTexture.active = prevActive; if (sRgbRT != null) RenderTexture.ReleaseTemporary(sRgbRT); if (tex != null) UnityEngine.Object.Destroy(tex); } } private string GenerateFileName(string extension, string suffix = "") { string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); return $"{screenshotFilePrefix}{suffix}_{timestamp}.{extension}"; } public void Cleanup() { // RenderStreamOutput 이 모든 RT/Material lifecycle 을 관리하므로 별도 정리 없음 } }