언리얼 엔진에 글로벌 셰이더 추가하기

커스텀 글로벌 셰이더의 추가와 사용에 대한 개요입니다.

Choose your operating system:

Windows

macOS

Linux

글로벌 셰이더 는 머티리얼 에디터로 생성되지 않은 셰이더입니다. 그 대신 C++로 생성되어 고정된 지오메트리에서 작동하며 머티리얼 또는 메시와의 상호 작용이 필요하지 않습니다. 원하는 외관을 얻기 위해 때때로 더 고급 기능이 요구되며 이를 위해 커스텀 셰이더 패스가 필요합니다.

글로벌 셰이더의 예로는 포스트 프로세싱 효과 렌더링, 컴퓨팅 셰이더 디스패치, 그리고 화면 지우기 등이 있습니다.

언리얼 셰이더 파일

언리얼 엔진은 사용하는 셰이더에 관한 정보를 읽고 저장하는 데 언리얼 셰이더(.usf) 파일을 사용합니다. 새롭게 생성된 셰이더의 소스 파일은 Engine/Shaders 폴더에 저장해야 합니다. 만약 플러그인 내 셰이더 개요 라면, 대신 Plugin/Shaders 폴더에 저장해야 합니다.

ConsoleVariables.ini 파일의 r.ShaderDevelopmentMode=1 명령을 사용해 셰이더 컴파일에 대한 상세 로그를 확인할 수 있습니다.

자세한 내용은 셰이더 개발 를 참조하십시오.

샘플 글로벌 셰이더

커스텀 컬러를 반환하는 단순 패스 스루 버텍스 셰이더 픽셀 셰이더 를 샘플로 만들어보겠습니다.

새 셰이더 생성 및 추가

Engine/Shaders 폴더에 새 텍스트 파일을 생성해 커스텀 셰이더를 생성합니다. 파일 확장자를 .usf 로 변경하고 이름을 지정합니다. 다음 예시에서는 MyTest.usf 를 사용합니다.

다음으로, MyTest.usf 파일에 다음 코드를 추가합니다.

MyTest.usf

// 단순 패스 스루 버텍스 셰이더

void MainVS(
    in float4 InPosition : ATTRIBUTE0,
    out float4 Output : SV_POSITION
)
{
    Output = InPosition;
}

// 단순 솔리드 컬러 픽셀 셰이더
float4 MyColor;
float4 MainPS() : SV_Target0
{
    return MyColor;
}

클래스 선언

이제 언리얼 엔진이 셰이더를 인식하고 컴파일을 시작하도록 하려면 C++ 클래스를 선언해야 합니다. 이 예시에서는 버텍스 셰이더를 그 클래스로 사용합니다.

MyTestVS.h

#include "GlobalShader.h"

// header 또는 cpp 파일에 추가할 수 있습니다
class FMyTestVS : public FGlobalShader
{
    DECLARE_EXPORTED_SHADER_TYPE(FMyTestVS, Global, /*MYMODULE_API*/);

    FMyTestVS() { }
    FMyTestVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
        : FGlobalShader(Initializer)
    {
    }

    static bool ShouldCache(EShaderPlatform Platform)
    {
        return true;
    }
};

이를 수행할 때는 몇 가지 요구 사항이 있습니다.

  • 이것은 `FGlobalShader`의 서브클래스입니다. 그래서 글로벌 셰이더 맵에서 끝나며, 머티리얼로 찾을 필요가 없습니다.

  • DECLARE_EXPORTED_SHADER_TYPE() 매크로 사용은 셰이더 타입의 직렬화에 필요한 익스포트를 생성합니다. 세 번째 파라미터는 필요할 경우 셰이더 모듈이 존재하는 코드 모듈의 외부 연결 타입입니다. 렌더러 모듈에 존재하지 않는 C++ 코드를 예로 들 수 있습니다.

  • 생성자는 기본 생성자와 직렬화 생성자 두 가지가 있습니다.

  • ShouldCache() 함수는 이 셰이더가 특정 상황에서 컴파일되어야 할지 여부를 결정하기 위해 필요합니다. 예를 들어, 컴퓨팅 셰이더를 비 컴퓨팅 셰이더 지원 RHI에서 컴파일하고 싶지는 않을 것입니다.

셰이더 타입 등록하기

셰이더 타입 은 피지컬 C++ 클래스에 매핑되며 셰이더 코드에 의해 지정되는 템플릿 또는 클래스입니다. 셰이더 타입은 다음 코드를 이용해 언리얼 엔진의 타입 목록에 등록할 수 있습니다.

// cpp 파일에 추가해야 합니다
IMPLEMENT_SHADER_TYPE(, FMyTestVS, TEXT("MyTest"), TEXT("MainVS"), SF_Vertex);

이 매크로는 ( FMyTestVS ) 타입을 .usf 파일( MyTest.usf ), 셰이더 엔트리 포인트( MainVS ), 프리퀀시/셰이더 스테이지( SF_Vertex )에 매핑합니다. 또한 ShouldCache() 메서드가 true 를 반환하는 한 셰이더가 컴필레이션 목록에 추가되도록 합니다.

`FGlobalShader`를 어느 모듈에 추가하든 엔진이 시작하기 전에 모듈이 *로드되어야* 합니다. 그렇지 않으면 다음과 같은 어서트가 표시됩니다.

> `셰이더 타입이 엔진 시작 후에 로드되었습니다. 먼저 로드되도록 하려면 모듈에 `ELoadingPhase::PostConfigInit`를 사용하십시오.` 

게임이나 에디터를 실행한 후에는 다이내믹 모듈에 셰이더 타입을 추가할 수 없습니다.

픽셀 셰이더 선언하기

다음으로, 픽셀 셰이더는 다음 코드를 사용하여 선언합니다.

class FMyTestPS : public FGlobalShader
{
    DECLARE_EXPORTED_SHADER_TYPE(FMyTestPS, Global, /*MYMODULE_API*/);

    FShaderParameter MyColorParameter;

    FMyTestPS() { }
    FMyTestPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
        : FGlobalShader(Initializer)
    {
        MyColorParameter.Bind(Initializer.ParameterMap, TEXT("MyColor"), SPF_Mandatory);
    }

    static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
    {
        FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
        // 셰이더 코드의 커스텀 정의를 추가합니다
        OutEnvironment.SetDefine(TEXT("MY_DEFINE"), 1);
    }

    static bool ShouldCache(EShaderPlatform Platform)
    {
        // 예를 들어 Platform == SP_METAL 컴파일을 건너뛸 수 있습니다
        return true;
    }

    // FShader 인터페이스
    virtual bool Serialize(FArchive& Ar) override
    {
        bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
        Ar << MyColorParameter;
        return bShaderHasOutdatedParameters;
    }

    void SetColor(FRHICommandList& RHICmdList, const FLinearColor& Color)
    {
        SetShaderValue(RHICmdList, GetPixelShader(), MyColorParameter, Color);
    }
};

// 이전과 동일한 소스 파일이지만 다른 진입점입니다
IMPLEMENT_SHADER_TYPE(, FMyTestPS, TEXT("MyTest"), TEXT("MainPS"), SF_Pixel);

이 클래스에서는 .usf 파일의 셰이더 파라미터 MyColor 가 노출됩니다.

  • 바인딩을 찾을 수 있도록 런타임 정보를 보유한 FShaderParameter MyColorParameter 멤버가 클래스에 추가됩니다. 따라서 런타임에 파라미터 값을 설정할 수 있습니다.

  • 직렬화 생성자에서 Bind() 함수는 이름에 따라 파라미터를 `ParameterMap`에 바인딩합니다. 이는 *반드시* .usf 파일의 이름과 매치되어야 합니다.

  • ModifyCompilationEnvironment() 함수는 같은 C++ 클래스가 다른 동작을 정의할 때, 그리고 셰이더에서 #define 값을 셋업할 수 있도록 하는 데 사용됩니다.

  • Serialize() 메서드는 필수 입니다. 여기서 런타임 중 셰이더의 바인딩(직렬화 생성자에서 매치)으로부터 컴파일 및 쿠킹 시간 정보가 로드되고 저장됩니다.

  • 마지막으로, 커스텀 SetColor() 메서드는 런타임 중 MyColor 파라미터를 지정된 값으로 세팅하는 방법의 예시를 보여줍니다.

단순 함수 작성하기

다음 코드는 지정된 셰이더 타입을 사용해 전체 화면 쿼드를 그리는 단순 함수를 작성합니다.

void RenderMyTest(FRHICommandList& RHICmdList, ERHIFeatureLevel::Type FeatureLevel, const FLinearColor& Color)
{
    // 글로벌 셰이더 컬렉션을 가져옵니다
    auto ShaderMap = GetGlobalShaderMap(FeatureLevel);

    // ShaderMap의 실제 셰이더 인스턴스를 가져옵니다
    TShaderMapRef MyVS(ShaderMap);
    TShaderMapRef MyPS(ShaderMap);

    // 해당 셰이더를 통해 바운드 셰이더 스테이트를 선언하고

명령 목록에 적용합니다

    static FGlobalBoundShaderState MyTestBoundShaderState;
    SetGlobalBoundShaderState(RHICmdList, FeatureLevel, MyTestBoundShaderState, GetVertexDeclarationFVector4(), *MyVS, *MyPS);

    // 함수를 호출하여 파라미터를 설정합니다
    MyPS->SetColor(RHICmdList, Color);

    // 완전한 사각형을 그리기 위해 GPU를 설정합니다
    RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI());
    RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
    RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI(), 0);

    // 버텍스를 설정합니다
    FVector4 Vertices[4];
    Vertices[0].Set(-1.0f, 1.0f, 0, 1.0f);
    Vertices[1].Set(1.0f, 1.0f, 0, 1.0f);
    Vertices[2].Set(-1.0f, -1.0f, 0, 1.0f);
    Vertices[3].Set(1.0f, -1.0f, 0, 1.0f);

    // 사각형을 그립니다
    DrawPrimitiveUP(RHICmdList, PT_TriangleStrip, 2, Vertices, sizeof(Vertices[0]));
}

코드베이스에서 런타임에 토글할 수 있는 콘솔 변수를 지워 이를 테스트해보세요. 이를 위해서 다음 코드를 사용합니다.

static TAutoConsoleVariable CVarMyTest(
    TEXT("r.MyTest"),
    0,
    TEXT("Test My Global Shader, set it to 0 to disable, or to 1, 2 or 3 for fun!"),
    ECVF_RenderThreadSafe
);

void FDeferredShadingSceneRenderer::RenderFinish(FRHICommandListImmediate& RHICmdList)
{
    [...]
    // ***
    // 렌더링을 완료하기 직전에 코드를 입력하여 화면 내 콘텐츠를 덮어씁니다
    int32 MyTestValue = CVarMyTest.GetValueOnAnyThread();
    if (MyTestValue != 0)
    {
        FLinearColor Color(MyTestValue == 1, MyTestValue == 2, MyTestValue == 3, 1);
        RenderMyTest(RHICmdList, FeatureLevel, Color);
    }
    // 삽입된 코드 종료
    // ***
    FSceneRenderer::RenderFinish(RHICmdList);
    [...]
}

물결(~) 키를 사용하여 프로젝트를 실행하고 콘솔 창을 열어 추가된 콘솔 변수의 기능을 테스트해보세요. 그리고 다음 명령 중 하나를 입력하여 변수를 설정하세요.

  • 컬러를 변경하려면 1, 2, 또는 3 값과 함께 r.MyTest 를 입력합니다.

  • 셰이더 패스를 비활성화하려면 r.MyTest 0 을 입력합니다.

추가 참고 사항

  • .usf 파일의 컴파일 디버깅에 대한 자세한 정보나 처리된 파일을 보려면 Debugging the Shader Compiling Process 를 참조하세요.

  • 쿠킹되지 않은 게임 또는 에디터가 빠른 반복처리를 위해 실행 중일 때 .usf 파일을 수정할 수 있습니다. 키보드 단축키 Ctrl + Shift + .(마침표) 를 사용하거나, 콘솔 창을 열고 recompileshaders changed 명령을 입력해 셰이더를 선택하고 리빌드하세요.

언리얼 엔진 문서의 미래를 함께 만들어주세요! 더 나은 서비스를 제공할 수 있도록 문서 사용에 대한 피드백을 주세요.
설문조사에 참여해 주세요
취소