- capture_screenshot → RGB24 PNG (알파 정보 자동 제거, 일반 사진) - capture_alpha_screenshot → RGBA32 PNG (배경 투명, OBS/합성 소스용) - HDR/Linear RT → sRGB RT 로 Blit 한 후 ReadPixels — Spout(OBS) 결과와 동일 톤 - 코루틴 기반 임시 고해상도 캡처(captureWidth/Height) 제거 — 카메라 해상도 그대로 → Spout 송신 한 프레임 깜빡임 부작용 사라짐 - screenshot.Initialize() 시그니처 host 인자 제거(코루틴 불필요) - UXML 인스펙터: captureWidth/Height 필드 제거 + 안내 갱신 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
132 lines
5.0 KiB
C#
132 lines
5.0 KiB
C#
using UnityEngine;
|
|
using System;
|
|
using System.IO;
|
|
|
|
/// <summary>
|
|
/// 스크린샷 캡처 관리.
|
|
/// RenderStreamOutput.CaptureTexture (후처리 + 알파 합성 결과) 를 PNG 로 저장.
|
|
/// - CaptureScreenshot() : RGB 만 (알파 = 1, 일반 사진)
|
|
/// - CaptureAlphaScreenshot() : RGBA 그대로 (배경 투명, 합성 소스용)
|
|
/// 카메라 해상도 그대로 ReadPixels — 별도 임시 RT/카메라 변경 없음.
|
|
/// </summary>
|
|
[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<string> log;
|
|
private Action<string> logError;
|
|
|
|
public void Initialize(Action<string> log, Action<string> logError)
|
|
{
|
|
this.log = log;
|
|
this.logError = logError;
|
|
|
|
if (renderStream == null)
|
|
renderStream = UnityEngine.Object.FindFirstObjectByType<RenderStreamOutput>();
|
|
|
|
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}");
|
|
}
|
|
}
|
|
|
|
/// <summary>RGB 만, 알파는 1 로 저장하는 일반 PNG (평범한 사진)</summary>
|
|
public void CaptureScreenshot()
|
|
{
|
|
if (renderStream == null || renderStream.CaptureTexture == null)
|
|
{
|
|
logError?.Invoke("RenderStreamOutput.CaptureTexture 가 준비되지 않았습니다");
|
|
return;
|
|
}
|
|
SaveRtAsPng(renderStream.CaptureTexture, GenerateFileName("png"), TextureFormat.RGB24);
|
|
}
|
|
|
|
/// <summary>RGBA 모두 저장 — 배경 투명, OBS/합성 소스용</summary>
|
|
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 을 관리하므로 별도 정리 없음
|
|
}
|
|
}
|