[URP Custom Renderer Feature 만들기- Grayscale]
프로젝트/Color Lim 개발일지

[URP Custom Renderer Feature 만들기- Grayscale]

구현 목표

  • 특정 Shader만 제외하고 전체 화면에 Grayscale 효과 적용시키기

구현을 위해 필요했던 지식들

  • 유니티 렌더 파이프라인의 변화
  • SRP 구조
  • Custom Renderer Feature 작성법

URP를 사용하면서 기존에 있던걸 사용할 수 없거나 교체된 부분들이 있어 여러 자료를 찾아봐야 했다

 

Custom Renderer Feature를 만들기 위해선 두 클래스를 사용하면 된다

  • ScriptableRenderPass
  • ScriptableRendererFeature

 

기본적인 부분은 Unity Manual에 나와있다

Unity Manual: How to create a custom Renderer Feature

 

최종 결과물

using UnityEngine;
using UnityEngine.Rendering.Universal;

public class GrayscaleWithOriginColorRendererFeature : ScriptableRendererFeature
{
    [System.Serializable]
    public class GrayRenderSettings
    {
        public RenderPassEvent Event = RenderPassEvent.AfterRenderingOpaques;
        public Material fullScreenMaterial;
    }

    public GrayRenderSettings settings = new GrayRenderSettings();
    private GrayscaleWithOriginColorRenderPass grayPass;

    public override void Create()
    {     
        // 초기 실행시 호출, 렌더 패스 생성 및 초기값 설정을 여기서 한다
        grayPass = new GrayscaleWithOriginColorRenderPass(settings.fullScreenMaterial, settings.Event);
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        var src = renderer.cameraColorTarget;

        grayPass.SetSource(src);
        renderer.EnqueuePass(grayPass);
    }
}
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

internal class GrayscaleWithOriginColorRenderPass : ScriptableRenderPass
{
    // 원하는 이름으로 짓기, Frame Debugger에서 사용될 이름임
    private const string ProfilerName = "Grayscale With Origin Color";
    // 원하는 이름으로 짓기, 임시 렌더 텍스쳐 이름으로 쓰임
    private const string TempRTName = "_TempRT";

    private ProfilingSampler m_ProfilingSampler = new ProfilingSampler(ProfilerName);
    private RenderTargetIdentifier source;
    // RenderTargetHandle 대신 Shader ID를 얻어와 사용
    private int destinationID = Shader.PropertyToID(TempRTName);
    private Material overrideMaterial;


    public GrayscaleWithOriginColorRenderPass(Material overrideMaterial, RenderPassEvent renderPassEvent)
    {
        this.overrideMaterial = overrideMaterial;
        this.renderPassEvent = renderPassEvent;
    }

    public void SetSource(RenderTargetIdentifier source)
    {
        this.source = source;
    }

    public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
    {
        cmd.GetTemporaryRT(destinationID, cameraTextureDescriptor, FilterMode.Point);
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        CommandBuffer cmd = CommandBufferPool.Get();

        using (new ProfilingScope(cmd, m_ProfilingSampler))
        {
            cmd.CopyTexture(source, destinationID);         
            Blit(cmd, destinationID, source, overrideMaterial);
        }

        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

    public override void FrameCleanup(CommandBuffer cmd)
    {
        cmd.ReleaseTemporaryRT(destinationID);
    }

 

cmd.GetTemporaryRT를 하면 임시로 사용할 렌더 텍스쳐가 생성된다

 

ProfilingScope를 사용하면 해당 name으로 아래와 같이 토글이 생성돼서 Frame Debug를 보기 용이해진다

 

현재 cameraColorTarget을 source에 할당하고

source를 임시 렌더 텍스쳐인 destinationID에 복사해준다

그 후 Blit을 통해 destinationID를 현재 카메라가 render하고 있는 텍스쳐인 source에 overrideMaterial를 적용시켜 복사해준다

 

Blit

주로 사용자 지정 셰이더를 사용해 렌더 텍스쳐에서 다른 텍스쳐로 복사하는데 사용된다

 

Stencil

위 화면처럼 전체 화면은 OverrideMaterial (Grayscale 효과)로 그려지고 몇몇 오브젝트는 원래 색으로 그려지고 있다

이 효과는 Stencil을 사용해서 구현했다

[Color Shader]
Stencil
{
    Ref 1
    Comp Always
    Pass Replace
}
[Grayscale Shader Code]
Stencil
{
    Ref 1
    Comp NotEqual
}

1. 원래 색으로 표현되어야 할 오브젝트들은

Ref 1

Comp Always

Pass Replace로 함으로써

화면에 그려질 때 먼저 쓰여있는 Stencil 값과 상관없이 항상 Render하고

먼저 쓰여있는 값을 1로 대체해놓는다

 

2. Grayscale로 전체 화면을 그릴 때

Ref 1

Comp NotEqual로 둬서

화면에 있는 Stencil 값이 1이 아닌 부분만 Render하게해서

원래 색으로 표현되어야 할 오브젝트부분을 제외한 곳만 Grayscale로 Render되게 만들었다

 

 

어려웠던 점

Shader Graph에 Stencil 값을 적용시킬 수 없다는 것이었는데

일단 Shader Graph의 Generated Shader를 통해 Shader Code를 복사하여

새로운 Shader를 만든 뒤 붙여넣고 Stencil 값만 추가하여 사용하고있다

 

 

첨부

Scriptable Renderer Feature를 만들기 위해 URP의 기본 개념부터 익혀야했는데

그 때 많은 도움이 되었던 영상과 글들을 첨부해둔다

Dev Weeks: URP 기본 구성과 흐름 (Unity 유튜브 채널)

https://mathmakeworld.tistory.com/59

 

Unity SRP 처음부터 시작하기 2

이번에는 저번 시간에 이어서 Unity SRP를 이용해 Object를 그리는 방법에 대해서 알아보겠습니다. 저번 시간과 마찬가지로 결과 화면 먼저 보겠습니다. 역시나 저번 포스팅과 비슷하게 그래픽스 수

mathmakeworld.tistory.com

https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/

 

Unity Scriptable Render Pipeline Tutorials

A collection of tutorials that cover the scriptable render pipeline of Unity.

catlikecoding.com