using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering.RenderGraphModule; /// /// 카메라 전환 시 크로스 디졸브 블렌딩을 위한 URP Renderer Feature /// Unity 6 Render Graph API 사용 /// public class CameraBlendRendererFeature : ScriptableRendererFeature { [System.Serializable] public class Settings { // BeforeRenderingPostProcessing으로 변경 - 포스트 프로세싱 전에 블렌딩 public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; public Material blendMaterial; } public Settings settings = new Settings(); private CameraBlendRenderPass blendPass; public override void Create() { blendPass = new CameraBlendRenderPass(settings); blendPass.renderPassEvent = settings.renderPassEvent; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (settings.blendMaterial == null) return; // 게임 카메라만 처리 if (renderingData.cameraData.cameraType != CameraType.Game) return; // 블렌딩이 활성화된 경우에만 패스 추가 if (CameraBlendController.IsBlending && CameraBlendController.BlendTexture != null) { renderer.EnqueuePass(blendPass); } } protected override void Dispose(bool disposing) { blendPass?.Dispose(); } } public class CameraBlendRenderPass : ScriptableRenderPass { private CameraBlendRendererFeature.Settings settings; private static readonly int BlendAmountProperty = Shader.PropertyToID("_BlendAmount"); private static readonly int PrevTexProperty = Shader.PropertyToID("_PrevTex"); private class PassData { public Material blendMaterial; public float blendAmount; public RenderTexture blendTexture; public TextureHandle sourceTexture; public TextureHandle destinationTexture; } public CameraBlendRenderPass(CameraBlendRendererFeature.Settings settings) { this.settings = settings; profilingSampler = new ProfilingSampler("Camera Blend Pass"); ConfigureInput(ScriptableRenderPassInput.Color); } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { if (settings.blendMaterial == null) return; if (!CameraBlendController.IsBlending || CameraBlendController.BlendTexture == null) return; var resourceData = frameData.Get(); var cameraData = frameData.Get(); // 백버퍼인 경우 스킵 (BeforeRenderingPostProcessing에서는 일반적으로 false) if (resourceData.isActiveTargetBackBuffer) { Debug.LogWarning("[CameraBlend] isActiveTargetBackBuffer - skipping"); return; } var source = resourceData.activeColorTexture; // source가 유효한지 확인 if (!source.IsValid()) { Debug.LogWarning("[CameraBlend] source texture is not valid"); return; } // cameraColorDesc 사용 - 카메라 색상 텍스처 설명자 var cameraTargetDesc = renderGraph.GetTextureDesc(resourceData.cameraColor); cameraTargetDesc.name = "_CameraBlendDestination"; cameraTargetDesc.clearBuffer = false; var destination = renderGraph.CreateTexture(cameraTargetDesc); // 블렌딩 패스 using (var builder = renderGraph.AddRasterRenderPass("Camera Blend", out var passData, profilingSampler)) { passData.blendMaterial = settings.blendMaterial; passData.blendAmount = CameraBlendController.BlendAmount; passData.blendTexture = CameraBlendController.BlendTexture; passData.sourceTexture = source; builder.UseTexture(source, AccessFlags.Read); builder.SetRenderAttachment(destination, 0, AccessFlags.Write); builder.AllowPassCulling(false); builder.SetRenderFunc((PassData data, RasterGraphContext context) => { data.blendMaterial.SetFloat(BlendAmountProperty, data.blendAmount); data.blendMaterial.SetTexture(PrevTexProperty, data.blendTexture); Blitter.BlitTexture(context.cmd, data.sourceTexture, new Vector4(1, 1, 0, 0), data.blendMaterial, 0); }); } // 결과를 source로 복사 using (var builder = renderGraph.AddRasterRenderPass("Camera Blend Copy Back", out var copyData, profilingSampler)) { copyData.sourceTexture = destination; builder.UseTexture(destination, AccessFlags.Read); builder.SetRenderAttachment(source, 0, AccessFlags.Write); builder.AllowPassCulling(false); builder.SetRenderFunc((PassData data, RasterGraphContext context) => { Blitter.BlitTexture(context.cmd, data.sourceTexture, new Vector4(1, 1, 0, 0), 0, false); }); } } [System.Obsolete("This rendering path is for compatibility mode only (when Render Graph is disabled). Use Render Graph API instead.", false)] public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { // Legacy path } public void Dispose() { } } /// /// 카메라 블렌딩을 제어하는 정적 컨트롤러 /// public static class CameraBlendController { private static bool isBlending = false; private static float blendAmount = 1f; private static RenderTexture blendTexture; public static bool IsBlending { get => isBlending; set => isBlending = value; } public static float BlendAmount { get => blendAmount; set => blendAmount = Mathf.Clamp01(value); } public static RenderTexture BlendTexture { get => blendTexture; set => blendTexture = value; } public static void StartBlend(RenderTexture texture) { blendTexture = texture; blendAmount = 0f; isBlending = true; } public static void EndBlend() { isBlending = false; blendAmount = 1f; blendTexture = null; } public static void Reset() { EndBlend(); } }