โป r-e-g-i-s-t-e-r ๊ธ์ง์ด์ฌ์ ํ๊ธ๋ก ์ ์์๋ฑํ๊ฒ ๋ฐ๊ฟ์ ์ฐ์ผ
https://gall.dcinside.com/mgallery/board/view/?id=game_dev&no=128264&search_head=30&page=1
[์ธ๋ฆฌ์ผ][์์ด๋] ์ง์ง ์ฌ์ด ์ ์์ด๋ 3๋ถ๋ง์ ๋ณด์ํ๊ธฐ์ ๋ฒ์ ์ผ๋ฉ๋ก ์ ์์ด๋ ์กฐ๋ฌด์ฌ๋ฅผ ๋ง๋ค์๋๋ฐ ์ฌ์ค ๊ฝค ํฐ ๋ฌธ์ ๊ฐ ์์์....์ผํ ๋ณด๊ธฐ์ ๋ฌธ์ ์์ด ๋ณด์ผ ์๋ ์์ง๋ง์ด๋ฐ ๋๋ฐ์ฌ ์ฌ์ง์ ๊ฒฝ์ฐ์๋ ์ ๋๋ก ์์ด๋ฉ ๋์ง ์๋๋ค๋ ๊ฑฐ์ฌ~(๋์ค ์ด๋ ๊ฒ ๋ณด์ด๋๋ก ์์ ํ๋ค๋ ๋ปgall.dcinside.comCelShader.usf์๋ค๊ฐ ์ ๋ฒ์ ๋ง๋ ์ ์์ด๋๋ฅผ ์์ฑํ ๊ฑฐ์
.usf๋ ์ธ๋ฆฌ์ผ ์ ์ฉ hlsl ์์ด๋ ํฌ๋งท์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋จ
#include "/Engine/Public/Platform.ush" #include "/Engine/Private/Common.ush" #include "/Engine/Private/ScreenPass.ush" Texture2D SceneColorTexture; Texture2D DiffuseColorTexture; Texture2D LUTTexture; SamplerState InputSampler; float EmissiveColorDetectingThreshold; SCREEN_PASS_TEXTURE_VIEWPORT(ViewParams) float2 PosToUV(float2 InPos) { const float2 ViewportUV = (InPos - ViewParams_ViewportMin.xy) * ViewParams_ViewportSizeInverse.xy; return ViewportUV * ViewParams_UVViewportSize + ViewParams_UVViewportMin; } float3 Desaturate(float3 InColor) { return dot(InColor, float3(0.3f, 0.59f, 0.11f)); } float4 MainPS(float4 InPos : SV_POSITION) : SV_Target0 { const float2 UV = PosToUV(InPos.xy); float4 SceneColor = Texture2DSample(SceneColorTexture, InputSampler, UV); float3 LightColor = saturate(Desaturate(SceneColor.rgb) - EmissiveColorDetectingThreshold) * SceneColor.rgb; float LightMask = ceil(saturate(Pow2(LightColor.r - LightColor.g) + Pow2(LightColor.r - LightColor.b))); float4 DiffuseColor = Texture2DSample(DiffuseColorTexture, InputSampler, UV); float DiffuseMask = ceil(saturate(Desaturate(DiffuseColor.rgb).r)); float3 ModifiedDiffuseColor = normalizeFast(lerp(SceneColor.rgb, DiffuseColor.rgb, DiffuseMask - LightMask) * (1 - SceneColor.a)); float3 OutputColor = ModifiedDiffuseColor * Texture2DSample(LUTTexture, InputSampler, float2(saturate(lengthFast(SceneColor.rgb).r), 0)).rgb; OutputColor += SceneColor.rgb * SceneColor.a; return float4(OutputColor, 0); } | cs |
๋์ถฉ ๋ค ์ง ๋ฏ
์ด์ ์ด ์์ด๋์ฝ๋๋ฅผ c++ ์ ๋ฐ์ธ๋ฉ ์์ผ์ค ๊ฑฐ์
๋ชจ๋ Public, Private ํด๋์ ๊ฐ๊ฐ CustomShader.h์ CustomShader.cpp๋ฅผ ์์ฑํด์ฃผ์ผ
//CustomShader.h #pragma once #include "Renderer/Private/ScreenPass.h" #include "SceneViewExtension.h" //๋๋ฒ๊น
์ฉ ๋ก๊ทธ์นดํ
๊ณ ๋ฆฌ ์ ์ธ DECLARE_LOG_CATEGORY_EXTERN(LogCustomShader, All, All); /** * ์์ด๋ ์ ์ญ๋ณ์๋ฅผ ํ๋ฒ์ ๋๊ฒจ์ค ์ ์๋๋ก ๊ตฌ์กฐ์ฒด๋ฅผ ์์ฑํ๋ ๋งคํฌ๋ก * (.usf์์ ์ ์ธํ ์ ์ญ๋ณ์์ ์ด๋ฆ์ด ์ผ์นํด์ผํจ) */ BEGIN_SHADER_PARAMETER_STRUCT(FCelShaderParameters, ) //์ธ์1: ๊ตฌ์กฐ์ฒด ์ด๋ฆ, ์ธ์2: API(ํ์ ์) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColorTexture) //์ฌ๊ธฐ์๋ถํฐ ์ ์ญ๋ณ์ ์ ์ธ SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DiffuseColorTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LUTTexture) SHADER_PARAMETER_SAMPLER(SamplerState, InputSampler) SHADER_PARAMETER(float, EmissiveColorDetectingThreshold) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, ViewParams) RENDER_TARGET_BINDING_SLOTS() //SV_Target์ ๋ฐ์ธ๋ฉํ ์ฌ๋กฏ์ END_SHADER_PARAMETER_STRUCT() //๊ตฌ์กฐ์ฒด ๋ /** * ๊ธ๋ก๋ฒ ์์ด๋ ํด๋์ค * ์ด ํด๋์ค ๋ด์์ RHI๋ฅผ ์ด์ฉํด์ ์ง์ ๋ ๋๋ง ํ์ดํ๋ผ์ธ์ ๋ฑ๋กํ ์ ์์ */ class FCelShaderPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FCelShaderPS); SHADER_USE_PARAMETER_STRUCT(FCelShaderPS, FGlobalShader); using FParameters = FCelShaderParameters; }; /** * ํฌ์คํธํ๋ก์ธ์ค ํ์ฅ ํด๋์ค * PrePostProcessPass_RenderThread ๋ง ์ฌ์ฉํ ๊ฑฐ์ง๋ง * ๋๋จธ์ง ํจ์๊ฐ ์์ ๊ฐ์ ํจ์์ฌ์ ์ผ๋จ ์์ฑ์ ํด์ค์ผํจ */ class FCelShaderViewExtension : public FSceneViewExtensionBase { public: FCelShaderViewExtension(const FAuto๋ ์ง์คํฐ& Auto๋ ์ง์คํฐ, UTexture* LUTTexture, float EmissiveThreshold); virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override {} virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override {} virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override {} virtual void PrePostProcessPass_RenderThread(FRDGBuilder& GraphBuilder, const FSceneView& View, const FPostProcessingInputs& Inputs) override; private: UTexture* LUTTexture; float EmissiveThreshold; public: FORCEINLINE void SetEmissiveThreshold(const float& Value) { EmissiveThreshold = Value; } }; | cs |
.h์๋ ์ด๋ ๊ฒ ์์ฑํ๋ฉด ๋ผ ๋๋ถ๋ถ ์ ์ธ์ด๋ผ ํฌ๊ฒ ํท๊ฐ๋ฆด ๊ฑด ์์๊ฑฐ์ผ
ํ๋ผ๋ฏธํฐ ์ ์ธํ ๋ float2๋ FVector2f, float4๋ FVector4f ์ด๋ฐ ์์ผ๋ก ์ฌ์ฉํด์ผ ํจ
#include "CustomShader.h" #include "PixelShaderUtils.h" #include "PostProcess/PostProcessing.h" //๋ก๊ทธ์นดํ
๊ณ ๋ฆฌ ์ ์ DEFINE_LOG_CATEGORY(LogCustomShader) /* * ๊ธ๋ก๋ฒ ์์ด๋ ํด๋์ค ๋ด์ ๋งคํฌ๋ก ์ ์ธ์ ๋ํ ๊ตฌํ ๋งคํฌ๋ก * ๋ชจ๋ ์ค์ ์ ์ ๊ธ๊ณผ ๋์ผํ๊ฒ ์ค์ ํ๋๋ฉด * ๊ฐ์ ๊ฒฝ๋ก /CustomShaderModule/Private/๊ฐ ๋ํ๋ด๋ ์ค์ ๊ฒฝ๋ก๋ * ../CustomShaderModule/Shaders/Private/์ * * 3๋ฒ์งธ ์ธ์๋ usf์์ ๋ฐ์ธ๋ฉํ ํจ์ ์ด๋ฆ์ ์
๋ ฅํ๋ฉด ๋ผ * 4๋ฒ์งธ ์ธ์๋ ์์ด๋์ ํ์
์ ์ด๋์ผ๋ก ์ง์ ํด์ฃผ๋ฉด๋ SF_Vertex ์ด๋ฐ ์์ผ๋ฃจ */ IMPLEMENT_GLOBAL_SHADER(FCelShaderPS, "/CustomShaderModule/Private/CelShader.usf", "MainPS", SF_Pixel); //๋ก๊ทธ๋ ๊ทธ๋ฅ ๋๋ฒ๊น
์ฉ์ผ ๋ฟ์ธ ๊ฑฐ์ฌ FCelShaderViewExtension::FCelShaderViewExtension(const FAuto๋ ์ง์คํฐ& Auto๋ ์ง์คํฐ, UTexture* LUTTexture, float EmissiveThreshold) : FSceneViewExtensionBase(Auto๋ ์ง์คํฐ), LUTTexture(LUTTexture), EmissiveThreshold(EmissiveThreshold) { UE_LOG(LogCustomShader, Warning, TEXT("Initialize New CelShaderExtension.")) UE_LOG(LogCustomShader, Warning, TEXT("Texture: %s"), *LUTTexture->GetName()) UE_LOG(LogCustomShader, Warning, TEXT("Threshold: %f"), EmissiveThreshold) } /* * ์ด ํจ์๊ฐ ์ด ๊ธ์ ํต์ฌ ์์์ * ์ฌ๊ธฐ์์ ํ๋ผ๋ฏธํฐ ๊ตฌ์กฐ์ฒด ๋ฐ์ธ๋ฉ ๋ฐ * ๋ ๋ ํจ์ค์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ ์ปค์คํ
์์ด๋๋ฅผ ์ฌ๋ฆด๊ฑฐ์ผ */ void FCelShaderViewExtension::PrePostProcessPass_RenderThread(FRDGBuilder& GraphBuilder, const FSceneView& View, const FPostProcessingInputs& Inputs) { //์ด ๋งคํฌ๋ก๋ฅผ ์ ์ธํ๋ฉด ๋ ๋๋
๊ฐ์ ๋๋๋ง ๋๋ฒ๊ฑฐ์์ ์บ์นํ ์ ์์ RDG_EVENT_SCOPE(GraphBuilder, "CelShader"); //์ฌ๊ธฐ์ ์์ด๋ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ RDG๋ฅผ ์ด์ฉํด์ ํ๋ผ๋ฏธํฐ ์ธ์คํด์ค๋ ๋ง๋ค๊ฒ์ FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel); TShaderMapRef<FCelShaderPS> CelShaderPS(GlobalShaderMap); FCelShaderPS::FParameters* CelShaderParameters = GraphBuilder.AllocParameters<FCelShaderPS::FParameters>(); //์ฌ๊ธฐ์ ํฌ์คํธํ๋ก์ธ์ฑ์ ํ ์ฌํ
์ค์ณ๋ฅผ ๋ง๋ค์ด์ฃผ๊ณ checkSlow(View.bIsViewInfo); const FIntRect Viewport = static_cast<const FViewInfo&>(View).ViewRect; FScreenPassTexture SceneColor((*Inputs.SceneTextures)->SceneColorTexture, Viewport); //์์ฑ์๋ฅผ ํตํด ๋ฐ์์จ ํ
์ค์ณ๋ RDG์ ๋ฑ๋กํด์ค FRDGTexture* LUTTextureRDG = ๋ ์ง์คํฐExternalTexture(GraphBuilder, LUTTexture->GetResource()->GetTextureRHI(), TEXT("LUT Texture")); // ์ด๋ถ๋ถ๋ ๋๋ฆ ์ค์ํ๋ฐ ๋ทฐํฌํธ ํฌ๊ธฐ์ ๋ง๊ฒ ์๋์ ์ธ UV๊ฐ์ ์ป๊ธฐ ์ํด ์ธํ
ํ๋ ํ๋ผ๋ฏธํฐ์ const FScreenPassTextureViewport SceneColorTextureViewport(SceneColor); const FScreenPassTextureViewportParameters SceneTextureViewportParams = GetScreenPassTextureViewportParameters(SceneColorTextureViewport); // ์ด ๋ถ๋ถ์ ํ๋ผ๋ฏธํฐ ๊ตฌ์กฐ์ฒด์์ ์ ์ธํ ๋ณ์์ ๋น ์ง์์ด ๊ฐ์ ๋ฃ์ด์ค์ผํจ CelShaderParameters->SceneColorTexture = (*Inputs.SceneTextures)->SceneColorTexture; CelShaderParameters->DiffuseColorTexture = (*Inputs.SceneTextures)->GBufferCTexture; CelShaderParameters->LUTTexture = LUTTextureRDG; CelShaderParameters->InputSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(); CelShaderParameters->EmissiveColorDetectingThreshold = EmissiveThreshold; CelShaderParameters->ViewParams = SceneTextureViewportParams; CelShaderParameters->RenderTargets[0] = FRenderTargetBinding(SceneColor.Texture, ERenderTargetLoadAction::ELoad); //ํ๋ผ๋ฏธํฐ ์ค์ ์ด ์๋ฃ๋ ํ ๋น๋ก์ ๋ ๋ํจ์ค์ ์์ด๋๋ฅผ ์ฌ๋ฆด ์ ์์ FPixelShaderUtils::AddFullscreenPass(GraphBuilder, GlobalShaderMap, FRDGEventName(TEXT("Cel Shading")), CelShaderPS, CelShaderParameters, Viewport); } | cs |
์์ด๋ ๊ด๋ จ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋งคํฌ๋ก๋ก ๊ตฌ์ฑ๋ ์ฝ๋๊ฐ ๊ฝค ๋ง์์ ์ฒ์์ ์ข ์๋ฆฌ๊น๋ฆฌ ํ ์๋ ์์ ใ ใ
์ด์ ์ต์คํ ์ ํด๋์ค ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ฉด ๋ฐ๋ก ์์ด๋๋ฅผ ์ฌ์ฉํ ์ ์์ด
//ShaderTestActor.h #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "ShaderTestActor.generated.h" class FCelShaderViewExtension; UCLASS() class CUSTOMSHADERMODULE_API AShaderTestActor : public AActor { GENERATED_BODY() public: AShaderTestActor(); UFUNCTION(CallInEditor, Category = "Cel Shader") void ApplyCelShader(); UFUNCTION(CallInEditor, Category = "Cel Shader") void CancelCelShader(); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cel Shader") TObjectPtr<UTexture> LUTTexture; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cel Shader") float EmissiveThreshold = 1.2f; float CachedEmissiveThreshold = -1.0f; private: TSharedPtr<FCelShaderViewExtension, ESPMode::ThreadSafe> CelShaderExtension; }; //ShaderTestActor.cpp #include "ShaderTestActor.h" #include "CustomShader.h" AShaderTestActor::AShaderTestActor() { PrimaryActorTick.bCanEverTick = false; } void AShaderTestActor::ApplyCelShader() { if (!LUTTexture || abs(EmissiveThreshold - CachedEmissiveThreshold) < 0.000001) { return; } if (CelShaderExtension.IsValid()) { CelShaderExtension->SetEmissiveThreshold(EmissiveThreshold); return; } CelShaderExtension = FSceneViewExtensions::NewExtension<FCelShaderViewExtension>(LUTTexture, EmissiveThreshold); } void AShaderTestActor::CancelCelShader() { if (CelShaderExtension.IsValid()) { CelShaderExtension.Reset(); } } | cs |
๋์ถฉ ๊ฐ๋จํ๊ฒ ์ต์คํ ์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ์กํฐ๋ฅผ ํ๋ ๋ง๋ค์์ด
์ด์ ํ ์คํธ๋ฅผ ํด๋ณด์
์กํฐ๋ฅผ ๋ฐฐ์นํ๊ณ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ ํ๊ฒ ๋ฃ์ด์ฃผ๊ณ Apply Cel Shader ๋ฒํผ์ ๋๋ฅด๋ฉด
์ ์์ ์ผ๋ก ๋์ํ๋ ๊ฒ ๊ฐ๋ค ^^
๊ถ๊ธํ ๊ฑฐ์์ผ๋ฉด ๋ ์ฃผ์ผ ํผํผ
์๋ํฐ๋ก ๊ตฌํํ๋ ๊ฑฐ์ ๋นํด์ ์ด๋ค ์ฐจ์ด์ ์ด ์๋์? ์ด์จ๋ ์ข ์ด์ฌํ ์ฌ์ จ๋ค์.
์ฌ์ค ์์ง ์์ค๋ฅผ ์์ ํ์ง ์์ ๊ณํ์ด๋ผ๋ฉด ๊ทธ๋ฅ ํฐ ์๋ฏธ๊ฐ ์์ ์๋ ์์ด์ ํ์ง๋ง ๋ ๋ ํ์ดํ๋ผ์ธ์ ๋ฆฌ์์ค์ ๋ง๊ฒ ์์ ํ๋ค๊ฑฐ๋ ํ ๋ ์์๋๋ฉด ์ข์ ๋ฏ ํฉ๋๋ค - dc App
๋ต๋ณ ๊ฐ์ฌํฉ๋๋ค