291 lines
9.4 KiB
C#
291 lines
9.4 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
|
|
/// <summary>
|
|
/// 카메라 전환 시 크로스 디졸브 블렌딩을 위한 URP Renderer Feature
|
|
/// Unity 6 Render Graph API 사용
|
|
/// </summary>
|
|
public class CameraBlendRendererFeature : ScriptableRendererFeature
|
|
{
|
|
[System.Serializable]
|
|
public class Settings
|
|
{
|
|
// AfterRenderingPostProcessing - 모든 포스트 프로세싱 적용 후 블렌딩
|
|
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
|
|
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.CaptureRequested ||
|
|
(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;
|
|
}
|
|
|
|
private class CapturePassData
|
|
{
|
|
public TextureHandle sourceTexture;
|
|
public RenderTexture targetTexture;
|
|
}
|
|
|
|
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;
|
|
|
|
var resourceData = frameData.Get<UniversalResourceData>();
|
|
var cameraData = frameData.Get<UniversalCameraData>();
|
|
|
|
// 백버퍼인 경우 스킵
|
|
if (resourceData.isActiveTargetBackBuffer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var source = resourceData.activeColorTexture;
|
|
|
|
// source가 유효한지 확인
|
|
if (!source.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 캡처 요청 처리 - 현재 프레임(포스트 프로세싱 적용 후)을 캡처
|
|
if (CameraBlendController.CaptureRequested && CameraBlendController.BlendTexture != null)
|
|
{
|
|
// UnsafePass를 사용하여 RenderTexture에 직접 복사
|
|
using (var builder = renderGraph.AddUnsafePass<CapturePassData>("Camera Blend Capture", out var captureData, profilingSampler))
|
|
{
|
|
captureData.sourceTexture = source;
|
|
captureData.targetTexture = CameraBlendController.BlendTexture;
|
|
|
|
builder.UseTexture(source, AccessFlags.Read);
|
|
builder.AllowPassCulling(false);
|
|
|
|
builder.SetRenderFunc((CapturePassData data, UnsafeGraphContext context) =>
|
|
{
|
|
// NativeCommandBuffer를 통해 Blit 수행
|
|
var nativeCmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
|
nativeCmd.Blit(data.sourceTexture, data.targetTexture);
|
|
CameraBlendController.CaptureReady = true;
|
|
});
|
|
}
|
|
return; // 캡처만 하고 블렌딩은 다음 프레임부터
|
|
}
|
|
|
|
// 블렌딩이 활성화되지 않았으면 스킵
|
|
if (!CameraBlendController.IsBlending || CameraBlendController.BlendTexture == null)
|
|
return;
|
|
|
|
// cameraColorDesc 사용 - 카메라 색상 텍스처 설명자
|
|
var cameraTargetDesc = renderGraph.GetTextureDesc(resourceData.cameraColor);
|
|
cameraTargetDesc.name = "_CameraBlendDestination";
|
|
cameraTargetDesc.clearBuffer = false;
|
|
var destination = renderGraph.CreateTexture(cameraTargetDesc);
|
|
|
|
// 블렌딩 패스
|
|
using (var builder = renderGraph.AddRasterRenderPass<PassData>("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<PassData>("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()
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 카메라 블렌딩을 제어하는 정적 컨트롤러
|
|
/// </summary>
|
|
public static class CameraBlendController
|
|
{
|
|
private static bool isBlending = false;
|
|
private static float blendAmount = 1f;
|
|
private static RenderTexture blendTexture;
|
|
private static bool captureRequested = false;
|
|
private static bool captureReady = false;
|
|
private static bool isRealtimeMode = false;
|
|
|
|
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 bool CaptureRequested
|
|
{
|
|
get => captureRequested;
|
|
set => captureRequested = value;
|
|
}
|
|
|
|
public static bool CaptureReady
|
|
{
|
|
get => captureReady;
|
|
set => captureReady = value;
|
|
}
|
|
|
|
public static bool IsRealtimeMode
|
|
{
|
|
get => isRealtimeMode;
|
|
set => isRealtimeMode = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 다음 프레임에서 현재 화면을 캡처하도록 요청합니다. (스냅샷 모드)
|
|
/// </summary>
|
|
public static void RequestCapture(RenderTexture targetTexture)
|
|
{
|
|
blendTexture = targetTexture;
|
|
captureRequested = true;
|
|
captureReady = false;
|
|
isRealtimeMode = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 캡처가 완료된 후 블렌딩을 시작합니다. (스냅샷 모드)
|
|
/// BlendAmount = 1에서 시작 (이전 카메라 100% 보임)
|
|
/// </summary>
|
|
public static void StartBlendAfterCapture()
|
|
{
|
|
if (captureReady && blendTexture != null)
|
|
{
|
|
blendAmount = 1f;
|
|
isBlending = true;
|
|
captureRequested = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 실시간 블렌딩을 시작합니다. (매 프레임 이전 카메라 렌더링)
|
|
/// BlendAmount = 1에서 시작 (이전 카메라 100% 보임)
|
|
/// </summary>
|
|
public static void StartRealtimeBlend(RenderTexture texture)
|
|
{
|
|
blendTexture = texture;
|
|
blendAmount = 1f;
|
|
isBlending = true;
|
|
isRealtimeMode = true;
|
|
captureRequested = false;
|
|
captureReady = false;
|
|
}
|
|
|
|
public static void StartBlend(RenderTexture texture)
|
|
{
|
|
blendTexture = texture;
|
|
blendAmount = 0f;
|
|
isBlending = true;
|
|
}
|
|
|
|
public static void EndBlend()
|
|
{
|
|
isBlending = false;
|
|
blendAmount = 1f;
|
|
blendTexture = null;
|
|
captureRequested = false;
|
|
captureReady = false;
|
|
isRealtimeMode = false;
|
|
}
|
|
|
|
public static void Reset()
|
|
{
|
|
EndBlend();
|
|
}
|
|
}
|