From 5087ebc5af92811c8cc640e67b4416c7bb65eab9 Mon Sep 17 00:00:00 2001 From: user Date: Wed, 21 Jan 2026 09:51:36 +0900 Subject: [PATCH] =?UTF-8?q?Refactor:=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20?= =?UTF-8?q?=EB=B8=94=EB=A0=8C=EB=94=A9=EC=9D=84=20UI=20=EC=98=A4=EB=B2=84?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 렌더 패스 기반 블렌딩 제거 (CameraBlendRendererFeature, CameraBlend.shader) - UI RawImage 기반 CameraBlendOverlay 추가 - 스냅샷/실시간 블렌딩 모두 UI 오버레이 방식으로 통합 - 메인 카메라의 실제 위치를 저장하여 화면 튀는 문제 수정 Co-Authored-By: Claude Opus 4.5 --- .../Controllers/CameraController.cs | 253 ++++++++------- .../Rendering/CameraBlend.mat | 39 --- .../Rendering/CameraBlend.mat.meta | 8 - .../Rendering/CameraBlend.shader | 57 ---- .../Rendering/CameraBlend.shader.meta | 9 - .../Rendering/CameraBlendRendererFeature.cs | 290 ------------------ .../CameraBlendRendererFeature.cs.meta | 2 - .../{Rendering.meta => UI.meta} | 2 +- .../UI/CameraBlendOverlay.cs | 126 ++++++++ .../UI/CameraBlendOverlay.cs.meta | 2 + 10 files changed, 250 insertions(+), 538 deletions(-) delete mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.mat delete mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.mat.meta delete mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.shader delete mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.shader.meta delete mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlendRendererFeature.cs delete mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlendRendererFeature.cs.meta rename Assets/Scripts/Streamingle/StreamingleControl/{Rendering.meta => UI.meta} (77%) create mode 100644 Assets/Scripts/Streamingle/StreamingleControl/UI/CameraBlendOverlay.cs create mode 100644 Assets/Scripts/Streamingle/StreamingleControl/UI/CameraBlendOverlay.cs.meta diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs index 63935b1d..705f3949 100644 --- a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs @@ -280,8 +280,7 @@ public class CameraManager : MonoBehaviour, IController [SerializeField] private bool useRealtimeBlend = true; // 블렌드용 렌더 텍스처와 카메라 - private RenderTexture blendRenderTexture; - private RenderTexture prevCameraRenderTexture; // 실시간 블렌딩용 이전 카메라 렌더 텍스처 + private RenderTexture prevCameraRenderTexture; // 블렌딩용 이전 카메라 렌더 텍스처 private Camera blendCamera; private CinemachineCamera currentCamera; @@ -350,13 +349,11 @@ public class CameraManager : MonoBehaviour, IController } // 블렌드 리소스 정리 - CameraBlendController.Reset(); - - if (blendRenderTexture != null) + if (prevCameraRenderTexture != null) { - blendRenderTexture.Release(); - Destroy(blendRenderTexture); - blendRenderTexture = null; + prevCameraRenderTexture.Release(); + Destroy(prevCameraRenderTexture); + prevCameraRenderTexture = null; } if (blendCamera != null) @@ -785,7 +782,12 @@ public class CameraManager : MonoBehaviour, IController if (blendCoroutine != null) { StopCoroutine(blendCoroutine); - CameraBlendController.EndBlend(); + // UI 오버레이 숨기기 + var overlay = CameraBlendOverlay.Instance; + if (overlay != null) + { + overlay.Hide(); + } } blendCoroutine = StartCoroutine(BlendTransitionCoroutine(index, customBlendTime ?? blendTime)); @@ -923,38 +925,6 @@ public class CameraManager : MonoBehaviour, IController } - /// - /// 블렌드용 렌더 텍스처 생성/갱신 - /// - private void EnsureBlendRenderTexture() - { - int width = Screen.width; - int height = Screen.height; - - // 이미 적절한 크기의 텍스처가 있으면 재사용 - if (blendRenderTexture != null && - blendRenderTexture.width == width && - blendRenderTexture.height == height) - { - return; - } - - // 기존 텍스처 해제 - if (blendRenderTexture != null) - { - blendRenderTexture.Release(); - Destroy(blendRenderTexture); - } - - // 새 렌더 텍스처 생성 - 색상 전용 (depth 없음), 리니어 색공간에서 올바른 블렌딩 - var descriptor = new RenderTextureDescriptor(width, height, RenderTextureFormat.ARGB32, 0); // depth = 0 - descriptor.sRGB = true; // sRGB 텍스처로 설정하여 리니어 파이프라인과 일치 - blendRenderTexture = new RenderTexture(descriptor); - blendRenderTexture.name = "CameraBlendRT"; - blendRenderTexture.Create(); - - } - /// /// 크로스 디졸브 블렌드 전환 코루틴 /// @@ -971,16 +941,15 @@ public class CameraManager : MonoBehaviour, IController } /// - /// 스냅샷 블렌딩 (이전 화면 정지) + /// 스냅샷 블렌딩 (이전 화면 정지) - UI 오버레이 방식 /// private IEnumerator SnapshotBlendTransitionCoroutine(int targetIndex, float duration) { isBlending = true; - // 블렌드 텍스처 준비 - EnsureBlendRenderTexture(); - - if (blendRenderTexture == null) + // 메인 카메라의 현재 위치/회전/FOV를 저장 + Camera mainCamera = Camera.main; + if (mainCamera == null) { SetImmediate(targetIndex); isBlending = false; @@ -988,80 +957,14 @@ public class CameraManager : MonoBehaviour, IController yield break; } - // 렌더 패스에서 현재 프레임(포스트 프로세싱 적용 후)을 캡처하도록 요청 - CameraBlendController.RequestCapture(blendRenderTexture); - - // 캡처가 완료될 때까지 대기 (다음 프레임 렌더링 후) - yield return new WaitForEndOfFrame(); - yield return null; // 렌더 패스가 실행될 때까지 한 프레임 더 대기 - - // 캡처 완료 확인 - if (!CameraBlendController.CaptureReady) - { - CameraBlendController.EndBlend(); - SetImmediate(targetIndex); - isBlending = false; - blendCoroutine = null; - yield break; - } - - // 블렌딩 시작 - BlendAmount = 1 (이전 카메라만 보임) - CameraBlendController.StartBlendAfterCapture(); - CameraBlendController.BlendAmount = 1f; - - // 카메라 전환 (이제 BlendAmount=1이므로 캡처된 이전 화면만 보임) - SetImmediate(targetIndex); - - // 블렌드 진행: 1 → 0 (이전 카메라가 서서히 사라지고 새 카메라가 드러남) - float elapsed = 0f; - while (elapsed < duration) - { - elapsed += Time.deltaTime; - float t = Mathf.Clamp01(elapsed / duration); - // 부드러운 이징 적용 (SmoothStep) - t = t * t * (3f - 2f * t); - // 1에서 0으로: 이전 카메라가 서서히 사라짐 - CameraBlendController.BlendAmount = 1f - t; - yield return null; - } - - CameraBlendController.BlendAmount = 0f; - - // 블렌딩 종료 - CameraBlendController.EndBlend(); - - isBlending = false; - blendCoroutine = null; - } - - /// - /// 실시간 블렌딩 (두 카메라 동시 렌더링) - /// 이전 카메라 위치를 저장해두고 그 위치에서 렌더링, 서서히 사라지면서 새 카메라가 드러남 - /// - private IEnumerator RealtimeBlendTransitionCoroutine(int targetIndex, float duration) - { - isBlending = true; - - // 이전 카메라 프리셋 저장 - var prevPreset = currentPreset; - if (prevPreset == null || prevPreset.virtualCamera == null) - { - SetImmediate(targetIndex); - isBlending = false; - blendCoroutine = null; - yield break; - } - - // SetImmediate 호출 전에 이전 카메라의 위치/회전/FOV를 저장 - Vector3 prevCameraPosition = prevPreset.virtualCamera.transform.position; - Quaternion prevCameraRotation = prevPreset.virtualCamera.transform.rotation; - float prevCameraFOV = prevPreset.virtualCamera.Lens.FieldOfView; + Vector3 prevCameraPosition = mainCamera.transform.position; + Quaternion prevCameraRotation = mainCamera.transform.rotation; + float prevCameraFOV = mainCamera.fieldOfView; // 블렌드 텍스처 준비 - EnsureBlendRenderTexture(); EnsurePrevCameraRenderTexture(); - if (blendRenderTexture == null || prevCameraRenderTexture == null) + if (prevCameraRenderTexture == null) { SetImmediate(targetIndex); isBlending = false; @@ -1079,34 +982,122 @@ public class CameraManager : MonoBehaviour, IController yield break; } + // UI 오버레이 준비 + var overlay = CameraBlendOverlay.GetOrCreate(); + + // 블렌드 카메라를 현재 메인 카메라 위치로 설정하고 렌더링 (스냅샷 캡처) + blendCamera.transform.SetPositionAndRotation(prevCameraPosition, prevCameraRotation); + blendCamera.fieldOfView = prevCameraFOV; + blendCamera.targetTexture = prevCameraRenderTexture; + blendCamera.Render(); + + // UI 오버레이 표시 (알파 1 = 완전 불투명) + overlay.Show(prevCameraRenderTexture); + overlay.SetAlpha(1f); + + // 새 카메라로 전환 + SetImmediate(targetIndex); + + // 블렌드 진행: 알파 1 → 0 (스냅샷이 서서히 사라지고 새 카메라가 드러남) + float elapsed = 0f; + while (elapsed < duration) + { + // 다음 프레임까지 대기 + yield return null; + + elapsed += Time.deltaTime; + float t = Mathf.Clamp01(elapsed / duration); + // 부드러운 이징 적용 (SmoothStep) + t = t * t * (3f - 2f * t); + // 알파를 1에서 0으로: 스냅샷이 서서히 사라짐 + overlay.SetAlpha(1f - t); + } + + // 블렌딩 종료 + overlay.Hide(); + + isBlending = false; + blendCoroutine = null; + } + + /// + /// 실시간 블렌딩 (UI 오버레이 방식) + /// 이전 카메라 위치를 저장해두고 그 위치에서 렌더링, UI로 표시하고 서서히 사라지게 함 + /// + private IEnumerator RealtimeBlendTransitionCoroutine(int targetIndex, float duration) + { + isBlending = true; + + // 이전 카메라 프리셋 저장 + var prevPreset = currentPreset; + if (prevPreset == null || prevPreset.virtualCamera == null) + { + SetImmediate(targetIndex); + isBlending = false; + blendCoroutine = null; + yield break; + } + + // 메인 카메라의 현재 위치/회전/FOV를 저장 (Cinemachine이 적용한 실제 위치) + Camera mainCamera = Camera.main; + if (mainCamera == null) + { + SetImmediate(targetIndex); + isBlending = false; + blendCoroutine = null; + yield break; + } + + Vector3 prevCameraPosition = mainCamera.transform.position; + Quaternion prevCameraRotation = mainCamera.transform.rotation; + float prevCameraFOV = mainCamera.fieldOfView; + + // 블렌드 텍스처 준비 + EnsurePrevCameraRenderTexture(); + + if (prevCameraRenderTexture == null) + { + SetImmediate(targetIndex); + isBlending = false; + blendCoroutine = null; + yield break; + } + + // 블렌드 카메라 준비 + EnsureBlendCamera(); + if (blendCamera == null) + { + SetImmediate(targetIndex); + isBlending = false; + blendCoroutine = null; + yield break; + } + + // UI 오버레이 준비 + var overlay = CameraBlendOverlay.GetOrCreate(); + // 블렌드 카메라를 이전 카메라 위치로 설정하고 첫 프레임 렌더링 blendCamera.transform.SetPositionAndRotation(prevCameraPosition, prevCameraRotation); blendCamera.fieldOfView = prevCameraFOV; blendCamera.targetTexture = prevCameraRenderTexture; - - // Camera.Render()로 렌더링 (URP에서도 포스트 프로세싱 적용됨) blendCamera.Render(); - Graphics.Blit(prevCameraRenderTexture, blendRenderTexture); - // 실시간 블렌딩 시작 (BlendAmount = 1에서 시작, 이전 카메라가 100% 보임) - CameraBlendController.StartRealtimeBlend(blendRenderTexture); - CameraBlendController.BlendAmount = 1f; + // UI 오버레이 표시 (알파 1 = 완전 불투명) + overlay.Show(prevCameraRenderTexture); + overlay.SetAlpha(1f); // 새 카메라로 전환 (메인 카메라는 이제 새 위치에서 렌더링됨) SetImmediate(targetIndex); - // 블렌드 진행: 1 → 0 (이전 카메라 화면이 서서히 사라지고 새 카메라가 드러남) + // 블렌드 진행: 알파 1 → 0 (이전 카메라 화면이 서서히 사라짐) float elapsed = 0f; while (elapsed < duration) { - // 이전 카메라 시점에서 먼저 렌더링 (메인 카메라 렌더링 전에) + // 이전 카메라 시점에서 렌더링 blendCamera.transform.SetPositionAndRotation(prevCameraPosition, prevCameraRotation); blendCamera.fieldOfView = prevCameraFOV; blendCamera.targetTexture = prevCameraRenderTexture; - - // Camera.Render()로 렌더링 (URP에서도 포스트 프로세싱 적용됨) blendCamera.Render(); - Graphics.Blit(prevCameraRenderTexture, blendRenderTexture); // 다음 프레임까지 대기 yield return null; @@ -1115,14 +1106,12 @@ public class CameraManager : MonoBehaviour, IController float t = Mathf.Clamp01(elapsed / duration); // 부드러운 이징 적용 (SmoothStep) t = t * t * (3f - 2f * t); - // 1에서 0으로: 이전 카메라가 서서히 사라짐 - CameraBlendController.BlendAmount = 1f - t; + // 알파를 1에서 0으로: 이전 카메라가 서서히 사라짐 + overlay.SetAlpha(1f - t); } - CameraBlendController.BlendAmount = 0f; - // 블렌딩 종료 - CameraBlendController.EndBlend(); + overlay.Hide(); isBlending = false; blendCoroutine = null; diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.mat b/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.mat deleted file mode 100644 index fbcb8667..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.mat +++ /dev/null @@ -1,39 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 8 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: CameraBlend - m_Shader: {fileID: 4800000, guid: 76944497fc2bcdd479fc435a5d4d8447, type: 3} - m_Parent: {fileID: 0} - m_ModifiedSerializedProperties: 0 - m_ValidKeywords: [] - m_InvalidKeywords: [] - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_LockedProperties: - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _PrevTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Ints: [] - m_Floats: - - _BlendAmount: 0.000075280666 - m_Colors: [] - m_BuildTextureStacks: [] - m_AllowLocking: 1 diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.mat.meta b/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.mat.meta deleted file mode 100644 index c0796a4e..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: f563d7f0c3fc17647ad56388fb936427 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.shader b/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.shader deleted file mode 100644 index 5585439e..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.shader +++ /dev/null @@ -1,57 +0,0 @@ -Shader "Streamingle/CameraBlend" -{ - Properties - { - _PrevTex ("Previous Camera", 2D) = "white" {} - _BlendAmount ("Blend Amount", Range(0, 1)) = 0 - } - - SubShader - { - Tags - { - "RenderType" = "Opaque" - "RenderPipeline" = "UniversalPipeline" - } - - Pass - { - Name "CameraBlend" - ZTest Always - ZWrite Off - Cull Off - - HLSLPROGRAM - #pragma vertex Vert - #pragma fragment Frag - - #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" - #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl" - - TEXTURE2D(_PrevTex); - SAMPLER(sampler_PrevTex); - - float _BlendAmount; - - float4 Frag(Varyings input) : SV_Target - { - float2 uv = input.texcoord; - - // 현재 카메라 (Blitter에서 전달됨) - 동일한 샘플러 사용 - float4 currentColor = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv); - - // 이전 카메라 (캡처된 텍스처) - 동일한 Linear 샘플러 사용 - float4 prevColor = SAMPLE_TEXTURE2D(_PrevTex, sampler_LinearClamp, uv); - - // 리니어 공간에서 블렌딩 (BlendAmount: 1 = 이전, 0 = 현재) - // 이전 카메라(B)가 위에 덮이고 서서히 사라짐 - float4 finalColor = lerp(currentColor, prevColor, _BlendAmount); - - return finalColor; - } - ENDHLSL - } - } - - FallBack Off -} diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.shader.meta b/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.shader.meta deleted file mode 100644 index 60cb11b3..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlend.shader.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: 76944497fc2bcdd479fc435a5d4d8447 -ShaderImporter: - externalObjects: {} - defaultTextures: [] - nonModifiableTextures: [] - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlendRendererFeature.cs b/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlendRendererFeature.cs deleted file mode 100644 index 1a5a7da4..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlendRendererFeature.cs +++ /dev/null @@ -1,290 +0,0 @@ -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 - { - // 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(); - var cameraData = frameData.Get(); - - // 백버퍼인 경우 스킵 - 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("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("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; - 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; - } - - /// - /// 다음 프레임에서 현재 화면을 캡처하도록 요청합니다. (스냅샷 모드) - /// - public static void RequestCapture(RenderTexture targetTexture) - { - blendTexture = targetTexture; - captureRequested = true; - captureReady = false; - isRealtimeMode = false; - } - - /// - /// 캡처가 완료된 후 블렌딩을 시작합니다. (스냅샷 모드) - /// BlendAmount = 1에서 시작 (이전 카메라 100% 보임) - /// - public static void StartBlendAfterCapture() - { - if (captureReady && blendTexture != null) - { - blendAmount = 1f; - isBlending = true; - captureRequested = false; - } - } - - /// - /// 실시간 블렌딩을 시작합니다. (매 프레임 이전 카메라 렌더링) - /// BlendAmount = 1에서 시작 (이전 카메라 100% 보임) - /// - 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(); - } -} diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlendRendererFeature.cs.meta b/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlendRendererFeature.cs.meta deleted file mode 100644 index c1c2037b..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Rendering/CameraBlendRendererFeature.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 14b4e395bb7cf744d87b2c1885014709 \ No newline at end of file diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Rendering.meta b/Assets/Scripts/Streamingle/StreamingleControl/UI.meta similarity index 77% rename from Assets/Scripts/Streamingle/StreamingleControl/Rendering.meta rename to Assets/Scripts/Streamingle/StreamingleControl/UI.meta index fb98b7ed..feb434a9 100644 --- a/Assets/Scripts/Streamingle/StreamingleControl/Rendering.meta +++ b/Assets/Scripts/Streamingle/StreamingleControl/UI.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 634dec0a07bb460468b7e86f02bbc5b9 +guid: 055b59bee33be324c88ea76f3ea4786b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/Scripts/Streamingle/StreamingleControl/UI/CameraBlendOverlay.cs b/Assets/Scripts/Streamingle/StreamingleControl/UI/CameraBlendOverlay.cs new file mode 100644 index 00000000..a1d3375c --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/UI/CameraBlendOverlay.cs @@ -0,0 +1,126 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace Streamingle +{ + /// + /// 카메라 블렌딩용 UI 오버레이 + /// RawImage 하나로 이전 카메라 화면을 표시하고 서서히 사라지게 함 + /// + public class CameraBlendOverlay : MonoBehaviour + { + private static CameraBlendOverlay instance; + public static CameraBlendOverlay Instance => instance; + + private Canvas canvas; + private RawImage rawImage; + private CanvasGroup canvasGroup; + + private void Awake() + { + if (instance != null && instance != this) + { + Destroy(gameObject); + return; + } + instance = this; + DontDestroyOnLoad(gameObject); + + SetupUI(); + } + + private void SetupUI() + { + // Canvas 설정 + canvas = gameObject.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvas.sortingOrder = 9999; // 최상단에 표시 + + // CanvasScaler 추가 (화면 크기 맞춤) + var scaler = gameObject.AddComponent(); + scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + scaler.referenceResolution = new Vector2(1920, 1080); + scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; + scaler.matchWidthOrHeight = 0.5f; + + // GraphicRaycaster는 추가하지 않음 (클릭 방해 안함) + + // RawImage 생성 + GameObject imageObj = new GameObject("BlendImage"); + imageObj.transform.SetParent(transform, false); + + rawImage = imageObj.AddComponent(); + rawImage.raycastTarget = false; // 레이캐스트 비활성화 + + // 화면 전체를 덮도록 설정 + RectTransform rectTransform = rawImage.rectTransform; + rectTransform.anchorMin = Vector2.zero; + rectTransform.anchorMax = Vector2.one; + rectTransform.offsetMin = Vector2.zero; + rectTransform.offsetMax = Vector2.zero; + + // CanvasGroup 추가 (알파 조절용) + canvasGroup = imageObj.AddComponent(); + canvasGroup.blocksRaycasts = false; + canvasGroup.interactable = false; + + // 초기에는 숨김 + Hide(); + } + + /// + /// 오버레이 표시 및 텍스처 설정 + /// + public void Show(RenderTexture texture) + { + if (rawImage == null) return; + + rawImage.texture = texture; + rawImage.enabled = true; + canvasGroup.alpha = 1f; + } + + /// + /// 알파값 설정 (0 = 투명, 1 = 불투명) + /// + public void SetAlpha(float alpha) + { + if (canvasGroup == null) return; + canvasGroup.alpha = alpha; + } + + /// + /// 오버레이 숨김 + /// + public void Hide() + { + if (rawImage == null) return; + + rawImage.enabled = false; + rawImage.texture = null; + if (canvasGroup != null) + { + canvasGroup.alpha = 0f; + } + } + + /// + /// 싱글톤 인스턴스 생성 (없으면 자동 생성) + /// + public static CameraBlendOverlay GetOrCreate() + { + if (instance != null) return instance; + + GameObject obj = new GameObject("CameraBlendOverlay"); + return obj.AddComponent(); + } + + private void OnDestroy() + { + if (instance == this) + { + instance = null; + } + } + } +} diff --git a/Assets/Scripts/Streamingle/StreamingleControl/UI/CameraBlendOverlay.cs.meta b/Assets/Scripts/Streamingle/StreamingleControl/UI/CameraBlendOverlay.cs.meta new file mode 100644 index 00000000..ca57270e --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/UI/CameraBlendOverlay.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c072c526296e95d4bb6a11943cb2018b \ No newline at end of file