URP에서의 카메라 오버레이

들어가며

 

얼마 전에 프로젝트를 만들면서 카메라의 Depth Only를 써서 UI를 작업하려 했는데, 아무리 찾아도 없어서 이게 무슨 일인가 싶어서 인터넷에 검색을 해보았다. 나는 처음에는 버전이 달라져서 다른 기능으로 대체된 것인가 했는데, 알고 보니 버전은 상관이 없는거였다.

차이점은 프로젝트의 Pipeline이 빌트인이냐, URP이냐인데 내가 URP라서 Depth Only가 없었던 것이었다.

대신 URP는 카메라 오버레이라는 기능이 새롭게 추가되었다. 

 

아래 사진은 각각 빌트인과 URP 프로젝트의 카메라 Inspector이다.

빌트인의 카메라의 Inspector, Depth 기능이 있는 것을 확인할 수 있다.
URP에서는 Depth가 사라지고 Stack이라는 새로운 프로퍼티가 보인다.


Depth Only란?

https://blog.naver.com/winterwolfs/10180231956

 

[Unity] Camera 카메라

  = Camera 카메라       3차원 정보를 2차원으로 변환해서 화면에 출력을 ...

blog.naver.com

위 블로그는 내가 참고한 블로그로 Depth Only를 모르는 사람들은 참고하는 것이 좋다고 생각한다. 유니티 4를 예시로 하고 있지만, 기능은 달라진 것이 없고 블로그에서는 Depth Only에 대해 자세하게 설명이 되어 있다.

 

여기서도 요약을 해보자면,


Clear Flags 옵션을 바꿔서 Depth Only로 하면 여백을 렌더링하지 않고(?), 카메라가 비추는 두 화면을 겹칠 수 있다는 것

Depth의 값을 조절하여 뭐가 앞에 올 것인지 위계관계를 정할 수 있음

-100~100까지가 Depth의 범위이며 숫자가 높을 수록 앞에 옴 (헷갈리면 층 수를 생각)

 

정도가 될 것 같다.

 


URP 카메라 오버레이 사용

 

이번에는 "그래서 어떻게 씀?" 을 얘기해보도록 하겠다.

일단 예시를 위해 Scene을 다음과 같이 구성해보았다.

각각 빨간색 이미지를 가지고 있는 캔버스와 파란색 이미지를 가지고 있는 캔버스가 따로 존재하고, 각각의 캔버스마다 렌더링할 카메라도 따로 준비

 

빨간색만을 찍는 카메라와 파란색만을 찍는 카메라를 만들고, 각 카메라는 서로의 이미지가 앞에 보이도록 캔버스를 중심으로 앞뒤로 보고 있다.

Render TypeBase인 메인 카메라는 따로 존재하며, 각 이미지를 찍고 있는 카메라의 Render TypeOverlay이다.
(Base 카메라 1개, Overlay 카메라 2개로 총 3개의 카메라가 씬에 존재)

파란색을 렌더링하는 카메라
빨간색을 렌더링 하는 카메라

 

여기까지 세팅을 했고, 이제 다음은 URP에서 Depth Only 대신 들어온 기능인 카메라 스택(Camera Stack)에 대해서 얘기를 해보겠다.


Camera Stack의 사용 순서

1. 캔버스의 RederModeScreen Space - Camera로 변경한다.
2. 해당 캔버스를 렌더링할 카메라의 Render TypeOverlay로 변경한다.
3. Reder Camera가 밑에 생기면 그곳에 해당 캔버스를 렌더링할 Render Type이 Overlay 카메라를 연결한다.

캔버스의 Reder Mode / 그 밑에 Render Camera도 확인 가능하다.
Camera 컴포넌트의 Inspector창, Reder Type의 위치와 Overlay로 바꾼 모습

4. Reder Type이 Base인 카메라(메인 카메라)의 Inspector 창에서 Stack을 찾는다.
5. Stack에 캔버스에 연결한 Overlay 카메라를 넣는다.

Stack의 우측 하단을 보면 있는 +를 눌러서 해당 카메라를 선택하면 추가된다.

 

주의 : 카메라 스택의 렌더링의 순서

기존에 Depth Only는 Depth를 통해서 렌더링할 순서를 정할 수 있었다.
그렇다면 카메라 스택에서는 어떨까?

스택에 가장 먼저 추가된 (가장 위에 표시되는) 카메라가 가장 먼저 렌더링 됨
-- > 그러면 뒷순서에 렌더링하는 카메라에 덮어씌워지게 되서 가려짐
-->가장 위에 있는 카메라가 유저가 보는 화면 기준 맨 뒤, 가장 아래에 있는 카메라맨 앞이 된다.
(이름대로 자료구조의 Stack과 유사한 구조)

 

여기까지 잘 따라했다면 빨간색이 파란색의 앞에 있는 것처럼 화면이 비칠 것이다. 만약 반대로 파란색이 앞에 오게 하고 싶다면 카메라 스택의 BlueUICameraRedUICamera스택 순서를 바꾸면 될 것이다.


런타임 도중의 카메라 스택

 

여기까지가 기본적인 사용법 및 내가 찾은 자료를 통해 내용들이다.

그런데, 정리를 하다가 다른 의문이 떠올랐다.

 

 

그래서 GPT의 힘을 빌렸다. 다음이 챗GPT가 준 코드이다.

using UnityEngine;
using UnityEngine.Rendering.Universal;

public class CameraStackController : MonoBehaviour
{
    [SerializeField] Camera baseCamera;
    [SerializeField] Camera overlayCamera;

    void Start()
    {
        // Base 카메라의 추가 데이터 가져오기
        var baseCamData = baseCamera.GetUniversalAdditionalCameraData();

        // Overlay 카메라의 추가 데이터 가져오기
        var overlayCamData = overlayCamera.GetUniversalAdditionalCameraData();

        // Overlay 카메라로 지정
        overlayCamData.renderType = CameraRenderType.Overlay;

        // Base 스택에 추가
        if (!baseCamData.cameraStack.Contains(overlayCamera))
        {
            baseCamData.cameraStack.Add(overlayCamera);
        }
    }
}

 

물론 이 코드를 그대로 실험하지 않았고, 컴포넌트의 사용법에 대해서 참고만 했다. 다음에 나오는 코드가 더 설명하기 쉽고, 이해하기 편할 것이다.

 

using UnityEngine;
using UnityEngine.Rendering.Universal;

public class CameraController : MonoBehaviour
{

    Camera RedCamera;

    Camera BlueCamera;

    Camera MainCamera;
    
    private void Start()
    {
    	//카메라들을 찾아서 카메라 컴포넌트를 가져오기
        RedCamera = GameObject.Find("RedUICamera").GetComponent<Camera>();
        BlueCamera = GameObject.Find("BlueUICamera").GetComponent <Camera>();
        MainCamera = Camera.main;

        //카메라의 추가 데이터(RenderType이나 Base 카메라인 경우 Stack 등) 가져오기
        var baseCamData = MainCamera.GetUniversalAdditionalCameraData();
        var redCamData = RedCamera.GetUniversalAdditionalCameraData();
        var blueCamData = BlueCamera.GetUniversalAdditionalCameraData();

        Debug.Log($"MainCamera Render Type is {baseCamData.renderType.ToString()}");
        Debug.Log($"RedUICamera Render Type is {redCamData.renderType.ToString()}");
        Debug.Log($"BlueUICamera Render Type is {blueCamData.renderType.ToString()}");
        
        //배열로 카메라를 담아서 반복문이랑 gameobject.name으로 했다면 더 편했을지도...
    }
}

 

프로젝트의 파이프라인이 URP라면 모든 카메라에 이런 스크립트가 붙어 있는 것을 확인할 수 있다. 이는 카메라의 추가 데이터를 저장하는 스크립트로 우리가 위에서 만졌던 프로퍼티인 Render Type이나 Base 카메라인 경우 CameraStack에 접근하려면 이 스크립트에 접근해야만 한다.

URP로 프로젝트의 파이프라인을 바꾸면 카메라에 추가되어 있는 스크립트를 확인할 수 있다.

 

위 코드는 이 스크립트에 접근하고 있다. 하도 길어서 유니티도 전용 메서드를 만들어 놨고, 나도 그냥 변수 타입을 var로 담았다. 다음에는 각 카메라의 타입에 대해서 접근하여 각 카메라의 RederType을 로그로 찍어봤다.

 

메인 카메라만 Base, 나머지는 Overlay

우리가 위에서 설정한 것 Render Type이 잘 나온다. Camera Stack도 비슷하게 접근 가능하다. 다음 예제를 보자.


using NUnit.Framework;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class CameraController : MonoBehaviour
{
    Camera redCamera;
    Camera blueCamera;
    UniversalAdditionalCameraData redCamData;
    UniversalAdditionalCameraData blueCameData;
    UniversalAdditionalCameraData mainCamData;

    private void Start()
    {
        redCamera = GameObject.Find("RedUICamera").GetComponent<Camera>();
        blueCamera = GameObject.Find("BlueUICamera").GetComponent<Camera>();

        redCamData = redCamera.GetUniversalAdditionalCameraData();
        blueCameData = blueCamera.GetUniversalAdditionalCameraData();
        mainCamData = Camera.main.GetUniversalAdditionalCameraData();
    }
    public void AddAllOverlayCamera()
    {
        if (!mainCamData.cameraStack.Contains(redCamera))
        {
            mainCamData.cameraStack.Add(redCamera);
        }
        if (!mainCamData.cameraStack.Contains(blueCamera))
        {
            mainCamData.cameraStack.Add(blueCamera);
        }
    }
    public void RemoveAllOverlayCamera()
    {
        mainCamData.cameraStack.Remove(redCamera);
        mainCamData.cameraStack.Remove(blueCamera);
    }
    public void RiseRedCamera()
    {
        if (mainCamData.cameraStack.Contains(redCamera))
        {
            mainCamData.cameraStack.Remove(redCamera);
        }
        mainCamData.cameraStack.Insert(mainCamData.cameraStack.Count, redCamera);
    }

    public void RiseBlueCamera()
    {
        if (mainCamData.cameraStack.Contains(blueCamera))
        {
            mainCamData.cameraStack.Remove(blueCamera);
        }
        mainCamData.cameraStack.Insert(mainCamData.cameraStack.Count, blueCamera);
    }
}

 

그 긴거(카메라에 있는 스크립트)에는 접근하는 것은 똑같고, 함수를 4개를 더 선언하였다. 이는 버튼을 만들어서 테스트를 해보기 위함이다.

AddAllOverlayCamera : 만약에 카메라 스택에 카메라들이 없다면 모두 추가

RemoveAllOverlayCamera : 카메라 스택의 카메라들 삭제

RiseRedCamera : 레드 카메라를 스택에서 제거한 뒤, 스택의 [1] 순서에 추가

RiseBlueCamera : 블루 카메라를 스택에서 제거한 뒤, 스택의 [1] 순서에 추가

 


화면 위에 작아서 잘 안보이지만 버튼이 4개가 생겼고, 함수마다 연결을 해주었다.

 


각 버튼을 누르면서 변화를 확인해보면...

RemoveAll
위 사진에서 RiseRed를 누름
다음은 RiseBlue를 누름

테스트한 영상

 


Camera Stack은 List인가?

 

챗GPT가 처음에 카메라 스택은 List<Camera> 타입이라고 해서 리스트를 그대로 갖다 박는 것이 가능하다고 했는데, 실제로 해보면 되지 않는다.

public void ReverseCameraStack()
{
    Camera a = mainCamData.cameraStack[0];
    Camera b = mainCamData.cameraStack[1];
    mainCamData.cameraStack = new List<Camera> { b, a }; //여기서 오류가 남
}

 

위 코드를 테스트할 때 넣을 생각이었으나, 실제로는 주석에 적힌대로 마지막 줄에서 오류가 난다.

왜 오류가 날까, 컨트롤 눌러서 타고 들어가서 해당 스크립트를 실제로 살펴보니 cameraStack은 get만 가능하게 프로퍼티화를 해놨었다.

public List<Camera> cameraStack { get; private set; }

 

저걸 실제로 수정이 가능한지는 해보지 않았지만, 가능할 거 같긴한데 이미 많이 정리해서 테스트를 할 당시에 건드릴 생각을 하지 못했던 것 같다. 만약 해본 사람이 있다면 알려주세요..

 


정리하며

https://sharp-steamed-bread-for-game.tistory.com/17

 

URP 패키지를 적용한 뒤 Camera의 Depth Only는 어디에 있나요?

당신의 Depth Only는 Stack으로 대체되었다. 그러니까 서브 카메라를 UI처럼 메인 카메라 위에 오버레이한다는 거니까, 서브 카메라의 Render Type를 Overlay로 바꿔준다. 그리고 메인 카메라로 와서 Stack

sharp-steamed-bread-for-game.tistory.com

만약 빌트인을 쓰다가 URP 패키지를 깔았다면 당황하지말자. 나도 당황했지만 이 분의 블로그를 보고 침착하게 대처하였다.

 

당신의 Depth Only, Stack으로 대체되었다면 당황하지말고 이 블로그 글을 보라.