ADD : 카메라 블렌딩 기능 업데이트
This commit is contained in:
parent
c1ae4b4379
commit
8298b80dde
BIN
Assets/Resources/Settings/Streamingle Render Pipeline Asset_Renderer.asset
(Stored with Git LFS)
BIN
Assets/Resources/Settings/Streamingle Render Pipeline Asset_Renderer.asset
(Stored with Git LFS)
Binary file not shown.
@ -1,4 +1,6 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityRawInput;
|
||||
using System.Linq;
|
||||
@ -190,6 +192,16 @@ public class CameraManager : MonoBehaviour, IController
|
||||
[Tooltip("수동으로 회전 중심점을 지정합니다. (useAvatarHeadAsTarget이 false일 때 사용)")]
|
||||
[SerializeField] private Transform manualRotationTarget;
|
||||
|
||||
[Header("Camera Blend Transition")]
|
||||
[Tooltip("블렌드 전환 사용 여부 (크로스 디졸브)")]
|
||||
[SerializeField] private bool useBlendTransition = false;
|
||||
[Tooltip("블렌드 전환 시간 (초)")]
|
||||
[SerializeField, Range(0.1f, 2f)] private float blendTime = 0.5f;
|
||||
|
||||
// 블렌드용 렌더 텍스처와 카메라
|
||||
private RenderTexture blendRenderTexture;
|
||||
private Camera blendCamera;
|
||||
|
||||
private CinemachineCamera currentCamera;
|
||||
private InputHandler inputHandler;
|
||||
private CameraPreset currentPreset;
|
||||
@ -212,6 +224,10 @@ public class CameraManager : MonoBehaviour, IController
|
||||
// 스트림덱 연동
|
||||
private StreamDeckServerManager streamDeckManager;
|
||||
|
||||
// 블렌드 전환 상태
|
||||
private Coroutine blendCoroutine;
|
||||
private bool isBlending = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@ -250,6 +266,22 @@ public class CameraManager : MonoBehaviour, IController
|
||||
RawInput.OnKeyDown -= HandleRawKeyDown;
|
||||
RawInput.Stop();
|
||||
}
|
||||
|
||||
// 블렌드 리소스 정리
|
||||
CameraBlendController.Reset();
|
||||
|
||||
if (blendRenderTexture != null)
|
||||
{
|
||||
blendRenderTexture.Release();
|
||||
Destroy(blendRenderTexture);
|
||||
blendRenderTexture = null;
|
||||
}
|
||||
|
||||
if (blendCamera != null)
|
||||
{
|
||||
Destroy(blendCamera.gameObject);
|
||||
blendCamera = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@ -618,20 +650,48 @@ public class CameraManager : MonoBehaviour, IController
|
||||
|
||||
#region Camera Management
|
||||
public void Set(int index)
|
||||
{
|
||||
// 블렌드 전환 사용 시
|
||||
if (useBlendTransition)
|
||||
{
|
||||
SetWithBlend(index);
|
||||
return;
|
||||
}
|
||||
|
||||
SetImmediate(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 크로스 디졸브 블렌드와 함께 카메라 전환
|
||||
/// </summary>
|
||||
public void SetWithBlend(int index, float? customBlendTime = null)
|
||||
{
|
||||
if (isBlending) return;
|
||||
|
||||
if (!ValidateCameraIndex(index)) return;
|
||||
|
||||
// 같은 카메라로 전환 시도 시 무시
|
||||
if (currentPreset != null && cameraPresets.IndexOf(currentPreset) == index)
|
||||
return;
|
||||
|
||||
// 기존 코루틴 중단
|
||||
if (blendCoroutine != null)
|
||||
{
|
||||
StopCoroutine(blendCoroutine);
|
||||
CameraBlendController.EndBlend();
|
||||
}
|
||||
|
||||
blendCoroutine = StartCoroutine(BlendTransitionCoroutine(index, customBlendTime ?? blendTime));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 즉시 카메라 전환 (페이드 없음)
|
||||
/// </summary>
|
||||
public void SetImmediate(int index)
|
||||
{
|
||||
Debug.Log($"[CameraManager] 카메라 {index}번으로 전환 시작 (총 {cameraPresets?.Count ?? 0}개)");
|
||||
|
||||
if (cameraPresets == null)
|
||||
{
|
||||
Debug.LogError("[CameraManager] cameraPresets가 null입니다!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 0 || index >= cameraPresets.Count)
|
||||
{
|
||||
Debug.LogError($"[CameraManager] 잘못된 인덱스: {index}, 유효 범위: 0-{cameraPresets.Count - 1}");
|
||||
return;
|
||||
}
|
||||
if (!ValidateCameraIndex(index)) return;
|
||||
|
||||
var newPreset = cameraPresets[index];
|
||||
|
||||
@ -661,6 +721,169 @@ public class CameraManager : MonoBehaviour, IController
|
||||
Debug.Log($"[CameraManager] 카메라 전환 완료: {newCameraName}");
|
||||
}
|
||||
|
||||
private bool ValidateCameraIndex(int index)
|
||||
{
|
||||
if (cameraPresets == null)
|
||||
{
|
||||
Debug.LogError("[CameraManager] cameraPresets가 null입니다!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index < 0 || index >= cameraPresets.Count)
|
||||
{
|
||||
Debug.LogError($"[CameraManager] 잘못된 인덱스: {index}, 유효 범위: 0-{cameraPresets.Count - 1}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 블렌드용 카메라와 렌더 텍스처 생성
|
||||
/// </summary>
|
||||
private void EnsureBlendCamera()
|
||||
{
|
||||
if (blendCamera != null) return;
|
||||
|
||||
// 메인 카메라 찾기
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera == null)
|
||||
{
|
||||
Debug.LogError("[CameraManager] 메인 카메라를 찾을 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 블렌드용 카메라 생성
|
||||
GameObject blendCamObj = new GameObject("BlendCamera");
|
||||
blendCamObj.transform.SetParent(mainCamera.transform.parent);
|
||||
blendCamera = blendCamObj.AddComponent<Camera>();
|
||||
|
||||
// 메인 카메라 설정 복사
|
||||
blendCamera.CopyFrom(mainCamera);
|
||||
blendCamera.depth = mainCamera.depth - 1; // 메인 카메라보다 먼저 렌더링
|
||||
blendCamera.enabled = false; // 수동으로 렌더링할 것임
|
||||
|
||||
// URP 카메라 데이터 복사 (포스트 프로세싱 등)
|
||||
var mainCameraData = mainCamera.GetUniversalAdditionalCameraData();
|
||||
var blendCameraData = blendCamera.GetUniversalAdditionalCameraData();
|
||||
|
||||
if (mainCameraData != null && blendCameraData != null)
|
||||
{
|
||||
blendCameraData.renderPostProcessing = mainCameraData.renderPostProcessing;
|
||||
blendCameraData.antialiasing = mainCameraData.antialiasing;
|
||||
blendCameraData.antialiasingQuality = mainCameraData.antialiasingQuality;
|
||||
blendCameraData.renderShadows = mainCameraData.renderShadows;
|
||||
blendCameraData.requiresColorOption = mainCameraData.requiresColorOption;
|
||||
blendCameraData.requiresDepthOption = mainCameraData.requiresDepthOption;
|
||||
blendCameraData.dithering = mainCameraData.dithering;
|
||||
blendCameraData.stopNaN = mainCameraData.stopNaN;
|
||||
blendCameraData.volumeLayerMask = mainCameraData.volumeLayerMask;
|
||||
blendCameraData.volumeTrigger = mainCameraData.volumeTrigger;
|
||||
}
|
||||
|
||||
Debug.Log("[CameraManager] 블렌드 카메라 생성 완료 (URP 설정 포함)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 블렌드용 렌더 텍스처 생성/갱신
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
// 새 렌더 텍스처 생성
|
||||
blendRenderTexture = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32);
|
||||
blendRenderTexture.name = "CameraBlendRT";
|
||||
blendRenderTexture.Create();
|
||||
|
||||
Debug.Log($"[CameraManager] 블렌드 렌더 텍스처 생성: {width}x{height}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 크로스 디졸브 블렌드 전환 코루틴
|
||||
/// </summary>
|
||||
private IEnumerator BlendTransitionCoroutine(int targetIndex, float duration)
|
||||
{
|
||||
isBlending = true;
|
||||
|
||||
// 블렌드 카메라/텍스처 준비
|
||||
EnsureBlendCamera();
|
||||
EnsureBlendRenderTexture();
|
||||
|
||||
if (blendCamera == null || blendRenderTexture == null)
|
||||
{
|
||||
Debug.LogError("[CameraManager] 블렌드 카메라 또는 텍스처 생성 실패");
|
||||
SetImmediate(targetIndex);
|
||||
isBlending = false;
|
||||
blendCoroutine = null;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 메인 카메라의 현재 위치/회전 복사 (Cinemachine이 제어하는 실제 카메라)
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera != null)
|
||||
{
|
||||
blendCamera.transform.position = mainCamera.transform.position;
|
||||
blendCamera.transform.rotation = mainCamera.transform.rotation;
|
||||
blendCamera.fieldOfView = mainCamera.fieldOfView;
|
||||
}
|
||||
|
||||
// 블렌드 카메라로 현재 화면을 렌더 텍스처에 렌더링
|
||||
blendCamera.targetTexture = blendRenderTexture;
|
||||
blendCamera.enabled = true;
|
||||
blendCamera.Render();
|
||||
blendCamera.enabled = false;
|
||||
|
||||
Debug.Log($"[CameraManager] 블렌드 시작 - Duration: {duration}s");
|
||||
|
||||
// 블렌딩 시작 - BlendAmount = 0 (이전 카메라 A만 보임)
|
||||
CameraBlendController.StartBlend(blendRenderTexture);
|
||||
|
||||
// 한 프레임 대기 - 블렌딩이 적용된 상태로 렌더링되도록
|
||||
yield return null;
|
||||
|
||||
// 카메라 전환 (이제 BlendAmount=0이므로 A만 보임)
|
||||
SetImmediate(targetIndex);
|
||||
|
||||
// 블렌드 진행: 0 → 1 (A에서 B로)
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
// 부드러운 이징 적용 (SmoothStep)
|
||||
t = t * t * (3f - 2f * t);
|
||||
CameraBlendController.BlendAmount = t;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
CameraBlendController.BlendAmount = 1f;
|
||||
|
||||
// 블렌딩 종료
|
||||
CameraBlendController.EndBlend();
|
||||
|
||||
Debug.Log("[CameraManager] 블렌드 완료");
|
||||
|
||||
isBlending = false;
|
||||
blendCoroutine = null;
|
||||
}
|
||||
|
||||
private void UpdateCameraPriorities(CinemachineCamera newCamera)
|
||||
{
|
||||
if (newCamera == null)
|
||||
|
||||
@ -12,9 +12,50 @@ public class CameraManagerEditor : Editor
|
||||
private bool isApplicationPlaying;
|
||||
private bool isListening = false;
|
||||
|
||||
// Foldout 상태
|
||||
private bool showCameraPresets = true;
|
||||
private bool showControlSettings = true;
|
||||
private bool showSmoothingSettings = true;
|
||||
private bool showZoomSettings = true;
|
||||
private bool showRotationTarget = true;
|
||||
private bool showBlendSettings = true;
|
||||
|
||||
// SerializedProperties
|
||||
private SerializedProperty rotationSensitivityProp;
|
||||
private SerializedProperty panSpeedProp;
|
||||
private SerializedProperty zoomSpeedProp;
|
||||
private SerializedProperty orbitSpeedProp;
|
||||
private SerializedProperty movementSmoothingProp;
|
||||
private SerializedProperty rotationSmoothingProp;
|
||||
private SerializedProperty minZoomDistanceProp;
|
||||
private SerializedProperty maxZoomDistanceProp;
|
||||
private SerializedProperty useAvatarHeadAsTargetProp;
|
||||
private SerializedProperty manualRotationTargetProp;
|
||||
private SerializedProperty useBlendTransitionProp;
|
||||
private SerializedProperty blendTimeProp;
|
||||
|
||||
// 스타일
|
||||
private GUIStyle headerStyle;
|
||||
private GUIStyle sectionBoxStyle;
|
||||
private GUIStyle presetBoxStyle;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
isApplicationPlaying = Application.isPlaying;
|
||||
|
||||
// SerializedProperties 가져오기
|
||||
rotationSensitivityProp = serializedObject.FindProperty("rotationSensitivity");
|
||||
panSpeedProp = serializedObject.FindProperty("panSpeed");
|
||||
zoomSpeedProp = serializedObject.FindProperty("zoomSpeed");
|
||||
orbitSpeedProp = serializedObject.FindProperty("orbitSpeed");
|
||||
movementSmoothingProp = serializedObject.FindProperty("movementSmoothing");
|
||||
rotationSmoothingProp = serializedObject.FindProperty("rotationSmoothing");
|
||||
minZoomDistanceProp = serializedObject.FindProperty("minZoomDistance");
|
||||
maxZoomDistanceProp = serializedObject.FindProperty("maxZoomDistance");
|
||||
useAvatarHeadAsTargetProp = serializedObject.FindProperty("useAvatarHeadAsTarget");
|
||||
manualRotationTargetProp = serializedObject.FindProperty("manualRotationTarget");
|
||||
useBlendTransitionProp = serializedObject.FindProperty("useBlendTransition");
|
||||
blendTimeProp = serializedObject.FindProperty("blendTime");
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
@ -22,6 +63,36 @@ public class CameraManagerEditor : Editor
|
||||
StopListening();
|
||||
}
|
||||
|
||||
private void InitializeStyles()
|
||||
{
|
||||
if (headerStyle == null)
|
||||
{
|
||||
headerStyle = new GUIStyle(EditorStyles.boldLabel)
|
||||
{
|
||||
fontSize = 12,
|
||||
margin = new RectOffset(0, 0, 5, 5)
|
||||
};
|
||||
}
|
||||
|
||||
if (sectionBoxStyle == null)
|
||||
{
|
||||
sectionBoxStyle = new GUIStyle(EditorStyles.helpBox)
|
||||
{
|
||||
padding = new RectOffset(10, 10, 10, 10),
|
||||
margin = new RectOffset(0, 0, 5, 5)
|
||||
};
|
||||
}
|
||||
|
||||
if (presetBoxStyle == null)
|
||||
{
|
||||
presetBoxStyle = new GUIStyle(GUI.skin.box)
|
||||
{
|
||||
padding = new RectOffset(8, 8, 8, 8),
|
||||
margin = new RectOffset(0, 0, 3, 3)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void StartListening()
|
||||
{
|
||||
if (!isApplicationPlaying)
|
||||
@ -39,17 +110,176 @@ public class CameraManagerEditor : Editor
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
CameraManager manager = (CameraManager)target;
|
||||
EditorGUILayout.Space(10);
|
||||
serializedObject.Update();
|
||||
InitializeStyles();
|
||||
|
||||
CameraManager manager = (CameraManager)target;
|
||||
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
EditorGUILayout.LabelField("카메라 프리셋 관리", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// 프리셋 추가 버튼
|
||||
if (GUILayout.Button("새 프리셋 추가", GUILayout.Height(30)))
|
||||
// 카메라 컨트롤 설정 섹션
|
||||
DrawControlSettingsSection();
|
||||
|
||||
// 스무딩 설정 섹션
|
||||
DrawSmoothingSection();
|
||||
|
||||
// 줌 제한 섹션
|
||||
DrawZoomLimitsSection();
|
||||
|
||||
// 회전 타겟 섹션
|
||||
DrawRotationTargetSection();
|
||||
|
||||
// 블렌드 전환 섹션
|
||||
DrawBlendTransitionSection();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// 카메라 프리셋 섹션
|
||||
DrawCameraPresetsSection(manager);
|
||||
|
||||
// 키 입력 감지 로직
|
||||
HandleKeyListening(manager);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void DrawControlSettingsSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(sectionBoxStyle);
|
||||
|
||||
showControlSettings = EditorGUILayout.Foldout(showControlSettings, "Camera Control Settings", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (showControlSettings)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUILayout.PropertyField(rotationSensitivityProp, new GUIContent("회전 감도", "마우스 회전 감도 (0.5 ~ 10)"));
|
||||
EditorGUILayout.PropertyField(panSpeedProp, new GUIContent("패닝 속도", "휠클릭 패닝 속도 (0.005 ~ 0.1)"));
|
||||
EditorGUILayout.PropertyField(zoomSpeedProp, new GUIContent("줌 속도", "마우스 휠 줌 속도 (0.05 ~ 0.5)"));
|
||||
EditorGUILayout.PropertyField(orbitSpeedProp, new GUIContent("오빗 속도", "궤도 회전 속도 (1 ~ 20)"));
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawSmoothingSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(sectionBoxStyle);
|
||||
|
||||
showSmoothingSettings = EditorGUILayout.Foldout(showSmoothingSettings, "Smoothing", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (showSmoothingSettings)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUILayout.PropertyField(movementSmoothingProp, new GUIContent("이동 스무딩", "카메라 이동 부드러움 (0 ~ 0.95)"));
|
||||
EditorGUILayout.PropertyField(rotationSmoothingProp, new GUIContent("회전 스무딩", "카메라 회전 부드러움 (0 ~ 0.95)"));
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawZoomLimitsSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(sectionBoxStyle);
|
||||
|
||||
showZoomSettings = EditorGUILayout.Foldout(showZoomSettings, "Zoom Limits", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (showZoomSettings)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUILayout.PropertyField(minZoomDistanceProp, new GUIContent("최소 줌 거리", "카메라 최소 거리"));
|
||||
EditorGUILayout.PropertyField(maxZoomDistanceProp, new GUIContent("최대 줌 거리", "카메라 최대 거리"));
|
||||
|
||||
// 경고 표시
|
||||
if (minZoomDistanceProp.floatValue >= maxZoomDistanceProp.floatValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("최소 줌 거리는 최대 줌 거리보다 작아야 합니다.", MessageType.Warning);
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawRotationTargetSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(sectionBoxStyle);
|
||||
|
||||
showRotationTarget = EditorGUILayout.Foldout(showRotationTarget, "Rotation Target", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (showRotationTarget)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUILayout.PropertyField(useAvatarHeadAsTargetProp, new GUIContent("아바타 머리 사용", "활성화하면 아바타 머리를 회전 중심으로 사용"));
|
||||
|
||||
EditorGUI.BeginDisabledGroup(useAvatarHeadAsTargetProp.boolValue);
|
||||
EditorGUILayout.PropertyField(manualRotationTargetProp, new GUIContent("수동 회전 타겟", "수동으로 지정하는 회전 중심점"));
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
if (useAvatarHeadAsTargetProp.boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("런타임에 CustomRetargetingScript를 가진 아바타의 Head 본을 자동으로 찾습니다.", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawBlendTransitionSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(sectionBoxStyle);
|
||||
|
||||
showBlendSettings = EditorGUILayout.Foldout(showBlendSettings, "Camera Blend Transition", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (showBlendSettings)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUILayout.PropertyField(useBlendTransitionProp, new GUIContent("블렌드 전환 사용", "카메라 전환 시 크로스 디졸브 효과 사용"));
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!useBlendTransitionProp.boolValue);
|
||||
EditorGUILayout.PropertyField(blendTimeProp, new GUIContent("블렌드 시간", "크로스 디졸브 소요 시간 (초)"));
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
if (useBlendTransitionProp.boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("URP Renderer에 CameraBlendRendererFeature가 추가되어 있어야 합니다.", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawCameraPresetsSection(CameraManager manager)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(sectionBoxStyle);
|
||||
|
||||
// 헤더
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
showCameraPresets = EditorGUILayout.Foldout(showCameraPresets, $"Camera Presets ({manager.cameraPresets.Count})", true, EditorStyles.foldoutHeader);
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
// 프리셋 추가 버튼
|
||||
if (GUILayout.Button("+ 프리셋 추가", GUILayout.Width(100), GUILayout.Height(20)))
|
||||
{
|
||||
// Scene에 있는 CinemachineCamera 컴포넌트를 가져와서 새 프리셋 생성
|
||||
var newCamera = FindFirstObjectByType<CinemachineCamera>();
|
||||
if (newCamera != null)
|
||||
{
|
||||
@ -61,59 +291,126 @@ public class CameraManagerEditor : Editor
|
||||
EditorUtility.DisplayDialog("알림", "Scene에 CinemachineCamera가 없습니다.", "확인");
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// 프리셋 리스트
|
||||
for (int i = 0; i < manager.cameraPresets.Count; i++)
|
||||
{
|
||||
var preset = manager.cameraPresets[i];
|
||||
|
||||
EditorGUILayout.BeginVertical(GUI.skin.box);
|
||||
|
||||
// 프리셋 헤더
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField($"프리셋 {i + 1}", EditorStyles.boldLabel, GUILayout.Width(60));
|
||||
|
||||
// 프리셋 이름 필드
|
||||
preset.presetName = EditorGUILayout.TextField(preset.presetName);
|
||||
|
||||
// 삭제 버튼
|
||||
if (GUILayout.Button("삭제", GUILayout.Width(50)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("프리셋 삭제",
|
||||
$"프리셋 {preset.presetName}을(를) 삭제하시겠습니까?",
|
||||
"삭제", "취소"))
|
||||
{
|
||||
manager.cameraPresets.RemoveAt(i);
|
||||
EditorUtility.SetDirty(target);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (showCameraPresets)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// 프리셋 설정
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
// 가상 카메라 필드
|
||||
preset.virtualCamera = (CinemachineCamera)EditorGUILayout.ObjectField(
|
||||
"가상 카메라", preset.virtualCamera, typeof(CinemachineCamera), true);
|
||||
|
||||
// 핫키 설정 UI
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("핫키", GUILayout.Width(100));
|
||||
|
||||
if (preset.hotkey.isRecording)
|
||||
if (manager.cameraPresets.Count == 0)
|
||||
{
|
||||
EditorGUILayout.LabelField("키를 눌렀다 떼면 저장됩니다...", EditorStyles.helpBox);
|
||||
EditorGUILayout.HelpBox("카메라 프리셋이 없습니다. '+ 프리셋 추가' 버튼을 눌러 추가하세요.", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField(preset.hotkey.ToString());
|
||||
// 프리셋 리스트
|
||||
for (int i = 0; i < manager.cameraPresets.Count; i++)
|
||||
{
|
||||
DrawPresetItem(manager, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("레코딩", GUILayout.Width(60)))
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawPresetItem(CameraManager manager, int index)
|
||||
{
|
||||
var preset = manager.cameraPresets[index];
|
||||
|
||||
// 활성 프리셋 표시를 위한 배경색
|
||||
bool isActive = Application.isPlaying && manager.CurrentPreset == preset;
|
||||
|
||||
if (isActive)
|
||||
{
|
||||
GUI.backgroundColor = new Color(0.5f, 0.8f, 0.5f);
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginVertical(presetBoxStyle);
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
// 프리셋 헤더
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
// 인덱스 번호
|
||||
GUILayout.Label($"{index + 1}", EditorStyles.boldLabel, GUILayout.Width(20));
|
||||
|
||||
// 프리셋 이름 필드
|
||||
EditorGUI.BeginChangeCheck();
|
||||
preset.presetName = EditorGUILayout.TextField(preset.presetName, GUILayout.MinWidth(100));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
EditorUtility.SetDirty(target);
|
||||
}
|
||||
|
||||
// 활성 표시
|
||||
if (isActive)
|
||||
{
|
||||
GUILayout.Label("[Active]", EditorStyles.miniLabel, GUILayout.Width(50));
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
// 위/아래 버튼
|
||||
EditorGUI.BeginDisabledGroup(index == 0);
|
||||
if (GUILayout.Button("▲", GUILayout.Width(25)))
|
||||
{
|
||||
SwapPresets(manager, index, index - 1);
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(index == manager.cameraPresets.Count - 1);
|
||||
if (GUILayout.Button("▼", GUILayout.Width(25)))
|
||||
{
|
||||
SwapPresets(manager, index, index + 1);
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
// 삭제 버튼
|
||||
GUI.backgroundColor = new Color(1f, 0.5f, 0.5f);
|
||||
if (GUILayout.Button("X", GUILayout.Width(25)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("프리셋 삭제",
|
||||
$"프리셋 '{preset.presetName}'을(를) 삭제하시겠습니까?",
|
||||
"삭제", "취소"))
|
||||
{
|
||||
manager.cameraPresets.RemoveAt(index);
|
||||
EditorUtility.SetDirty(target);
|
||||
return;
|
||||
}
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(3);
|
||||
|
||||
// 가상 카메라 필드
|
||||
EditorGUI.BeginChangeCheck();
|
||||
preset.virtualCamera = (CinemachineCamera)EditorGUILayout.ObjectField(
|
||||
"Virtual Camera", preset.virtualCamera, typeof(CinemachineCamera), true);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
EditorUtility.SetDirty(target);
|
||||
}
|
||||
|
||||
// 핫키 설정 UI
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Hotkey", GUILayout.Width(70));
|
||||
|
||||
if (preset.hotkey.isRecording)
|
||||
{
|
||||
GUI.backgroundColor = new Color(1f, 0.9f, 0.5f);
|
||||
EditorGUILayout.LabelField("키를 눌렀다 떼면 저장됩니다...", EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 핫키 표시
|
||||
string hotkeyDisplay = preset.hotkey?.ToString() ?? "설정되지 않음";
|
||||
EditorGUILayout.LabelField(hotkeyDisplay, EditorStyles.textField, GUILayout.MinWidth(80));
|
||||
|
||||
if (GUILayout.Button("Record", GUILayout.Width(60)))
|
||||
{
|
||||
foreach (var otherPreset in manager.cameraPresets)
|
||||
{
|
||||
@ -123,7 +420,7 @@ public class CameraManagerEditor : Editor
|
||||
preset.hotkey.rawKeys.Clear();
|
||||
StartListening();
|
||||
}
|
||||
if (GUILayout.Button("초기화", GUILayout.Width(60)))
|
||||
if (GUILayout.Button("Clear", GUILayout.Width(50)))
|
||||
{
|
||||
preset.hotkey.rawKeys.Clear();
|
||||
EditorUtility.SetDirty(target);
|
||||
@ -131,14 +428,20 @@ public class CameraManagerEditor : Editor
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.Space(2);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
private void SwapPresets(CameraManager manager, int indexA, int indexB)
|
||||
{
|
||||
var temp = manager.cameraPresets[indexA];
|
||||
manager.cameraPresets[indexA] = manager.cameraPresets[indexB];
|
||||
manager.cameraPresets[indexB] = temp;
|
||||
EditorUtility.SetDirty(target);
|
||||
}
|
||||
|
||||
// 키 입력 감지 로직
|
||||
private void HandleKeyListening(CameraManager manager)
|
||||
{
|
||||
if (isListening)
|
||||
{
|
||||
var e = Event.current;
|
||||
@ -150,7 +453,7 @@ public class CameraManagerEditor : Editor
|
||||
if (e.keyCode != KeyCode.Mouse0 && e.keyCode != KeyCode.Mouse1 && e.keyCode != KeyCode.Mouse2)
|
||||
{
|
||||
AddKey(e.keyCode);
|
||||
e.Use(); // 이벤트 소비
|
||||
e.Use();
|
||||
}
|
||||
}
|
||||
else if (e.type == EventType.KeyUp && currentKeys.Contains(e.keyCode))
|
||||
@ -163,7 +466,7 @@ public class CameraManagerEditor : Editor
|
||||
StopListening();
|
||||
Repaint();
|
||||
}
|
||||
e.Use(); // 이벤트 소비
|
||||
e.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,10 @@ public class InputHandler : MonoBehaviour
|
||||
// 현재 활성화된 입력 모드 (충돌 방지)
|
||||
private InputMode currentMode = InputMode.None;
|
||||
|
||||
// 포커스 복귀 시 입력 무시를 위한 변수
|
||||
private int focusIgnoreFrames = 0;
|
||||
private const int FOCUS_IGNORE_FRAME_COUNT = 10; // 포커스 복귀 후 무시할 프레임 수
|
||||
|
||||
private enum InputMode
|
||||
{
|
||||
None,
|
||||
@ -36,8 +40,45 @@ public class InputHandler : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity 포커스 콜백 - 포커스 복귀 시 입력 무시 프레임 설정
|
||||
/// </summary>
|
||||
private void OnApplicationFocus(bool hasFocus)
|
||||
{
|
||||
if (hasFocus)
|
||||
{
|
||||
// 포커스 복귀 시 누적된 스크롤 입력 무시
|
||||
focusIgnoreFrames = FOCUS_IGNORE_FRAME_COUNT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 포커스 해제 시 상태 리셋
|
||||
currentMode = InputMode.None;
|
||||
isOrbitActive = false;
|
||||
isZoomActive = false;
|
||||
isCtrlRightZoomActive = false;
|
||||
isRightMouseHeld = false;
|
||||
isMiddleMouseHeld = false;
|
||||
lastScrollValue = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 포커스가 없으면 입력 무시
|
||||
if (!Application.isFocused)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 포커스 복귀 후 무시 프레임 카운트다운
|
||||
if (focusIgnoreFrames > 0)
|
||||
{
|
||||
focusIgnoreFrames--;
|
||||
// 무시 프레임 동안에는 스크롤 값을 계속 소비(버림)
|
||||
_ = Input.mouseScrollDelta;
|
||||
}
|
||||
|
||||
// 마우스 버튼 Raw 상태
|
||||
bool leftMouse = Input.GetMouseButton(0);
|
||||
bool rightMouse = Input.GetMouseButton(1);
|
||||
@ -123,6 +164,9 @@ public class InputHandler : MonoBehaviour
|
||||
|
||||
public Vector2 GetLookDelta()
|
||||
{
|
||||
// 포커스가 없으면 0 반환
|
||||
if (!Application.isFocused) return Vector2.zero;
|
||||
|
||||
return new Vector2(
|
||||
Input.GetAxis("Mouse X"),
|
||||
Input.GetAxis("Mouse Y")
|
||||
@ -131,6 +175,9 @@ public class InputHandler : MonoBehaviour
|
||||
|
||||
public float GetZoomDelta()
|
||||
{
|
||||
// 포커스가 없거나 포커스 복귀 직후면 0 반환 (누적된 휠 값 무시)
|
||||
if (!Application.isFocused || focusIgnoreFrames > 0) return 0f;
|
||||
|
||||
return Input.mouseScrollDelta.y;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 634dec0a07bb460468b7e86f02bbc5b9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,39 @@
|
||||
%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: 1
|
||||
m_Colors: []
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f563d7f0c3fc17647ad56388fb936427
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,56 @@
|
||||
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);
|
||||
|
||||
// 이전 카메라 (캡처된 텍스처)
|
||||
float4 prevColor = SAMPLE_TEXTURE2D(_PrevTex, sampler_PrevTex, uv);
|
||||
|
||||
// 두 카메라를 블렌딩 (BlendAmount: 0 = 이전, 1 = 현재)
|
||||
float4 finalColor = lerp(prevColor, currentColor, _BlendAmount);
|
||||
|
||||
return finalColor;
|
||||
}
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
|
||||
FallBack Off
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76944497fc2bcdd479fc435a5d4d8447
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,198 @@
|
||||
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
|
||||
{
|
||||
// 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<UniversalResourceData>();
|
||||
var cameraData = frameData.Get<UniversalCameraData>();
|
||||
|
||||
// 백버퍼인 경우 스킵 (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<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;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14b4e395bb7cf744d87b2c1885014709
|
||||
Loading…
x
Reference in New Issue
Block a user