저자 : 张氏男子
들어가며
언리얼 엔진의 렌더링 시스템은 상당히 폐쇄적으로 구성되어 있다. 대부분의 경우 머티리얼 에디터에서 노드를 연결하는 방식으로 렌더링 결과를 제어하게 되는데, 셰이더 코드 레벨에서 직접 무언가를 추가하거나 수정하려면 꽤 번거로운 작업이 필요하다. 게다가 머티리얼에서 접근할 수 있는 리소스도 매우 제한적이다.
이번 글에서는 머티리얼에 텍스처를 주입하는 두 가지 방법을 소개한다. 여기서는 평면 반사(Planar Reflection) 텍스처를 예시로 사용했지만, 같은 방법으로 다른 렌더 타겟도 머티리얼에서 접근할 수 있다. 각 방법마다 장단점이 있으므로 상황에 맞게 선택하면 된다.
첫 번째 방법: BasePassUniformParameters 활용
BasePass에 텍스처를 직접 주입하는 방식이다.
먼저 MobileBasePassUniformParameters에서 텍스처를 선언한다.
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FMobileBasePassUniformParameters, )
..........
//----YHRP---- PreviousSceneColor Begin
SHADER_PARAMETER_RDG_TEXTURE(Texture2D , PreviousHistoryTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, PreviousSceneColorSampler)
//----YHRP---- PreviousSceneColor End
..........
END_GLOBAL_SHADER_PARAMETER_STRUCT()
이제 텍스처를 등록해야 한다. PrevViewInfo에는 AO, SSPR, SceneColor 등 이전 프레임의 다양한 PooledRenderTarget이 포함되어 있다. 대부분 필요한 데이터는 여기서 얻을 수 있다.
void SetupMobileBasePassUniformParameters(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
EMobileBasePass BasePass,
EMobileSceneTextureSetupMode SetupMode,
const FMobileBasePassTextures& MobileBasePassTextures,
FMobileBasePassUniformParameters& BasePassParameters)
{
..........
//----YHRP---- Set Shader Parameters Begin
FRDGTextureRef PreviousHistoryTexture = [SystemTextures.Black](http://SystemTextures.Black);
if(View.PrevViewInfo.MobilePixelProjectedReflection.IsValid())
{
PreviousHistoryTexture = GraphBuilder.RegisterExternalTexture(View.PrevViewInfo.MobilePixelProjectedReflection);
}
BasePassParameters.PreviousHistoryTexture = PreviousHistoryTexture;
//----YHRP---- Set Shader Parameters End
..........
}
셰이더 코드도 수정이 필요하다. MobileBasePassPixelShader.usf 파일을 다음과 같이 변경한다.
......
//----YHRP----Previous
#define PreviousHistoryTexture MobileBasePass.PreviousHistoryTexture
#define PreviousSceneColorSampler MobileBasePass.PreviousSceneColorSampler
//----YHRP----Previous
......
......
void Main(
......
......
FPixelMaterialInputs PixelMaterialInputs;
{
float4 ScreenPosition = SvPositionToResolvedScreenPosition(SvPosition);
float3 WorldPosition = [BasePassInterpolants.PixelPosition.xyz](http://BasePassInterpolants.PixelPosition.xyz);
float3 WorldPositionExcludingWPO = [BasePassInterpolants.PixelPosition.xyz](http://BasePassInterpolants.PixelPosition.xyz);
#if USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS
WorldPositionExcludingWPO = BasePassInterpolants.PixelPositionExcludingWPO;
#endif
//----YHRP---- Inject History Ref to materialParameters
MaterialParameters.HistoryReflectionData = Texture2DSample(PreviousHistoryTexture,PreviousSceneColorSampler , SvPositionToBufferUV(SvPosition) );
CalcMaterialParametersEx(MaterialParameters, PixelMaterialInputs, SvPosition, ScreenPosition, bIsFrontFace, WorldPosition, WorldPositionExcludingWPO);
......
}
MaterialTemplate.ush에도 구조체 정의를 추가해준다.
struct FMaterialPixelParameters
{
......
float4 HistoryReflectionData;
......
이렇게 설정하면 머티리얼에서 Parameters.HistoryReflectionData를 통해 SSPR 정보를 샘플링할 수 있다.

두 번째 방법: ViewUniformBuffer 활용 (권장)
개인적으로 더 선호하는 방식이다.
ViewUniformBuffer에 텍스처를 추가하는 방법인데, ViewUniformBuffer는 전역 UniformBuffer에 가까워서 거의 모든 곳에서 접근할 수 있다. 머티리얼에서 사용 가능한 Atmosphere 관련 텍스처나 사전 계산된 BRDF 텍스처 같은 것들도 모두 여기서 정의된다. ViewUniformBuffer는 정말 다양한 데이터를 저장하고 있다.
/** The uniform shader parameters associated with a view. */
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT_WITH_CONSTRUCTOR(FViewUniformShaderParameters, ENGINE_API)
VIEW_UNIFORM_BUFFER_MEMBER_TABLE
..........
// ----YHRP----History PPR
SHADER_PARAMETER_TEXTURE(Texture2D , HistoryPPRTex)
SHADER_PARAMETER_SAMPLER(SamplerState , HistoryPPRTexSampler)
..........
END_GLOBAL_SHADER_PARAMETER_STRUCT()
초기화 코드도 작성한다.
FViewUniformShaderParameters::FViewUniformShaderParameters()
{
......
//----YHRP---- HistoryPPRTex
HistoryPPRTex = GBlackTexture->TextureRHI;
......
}
이제 실제로 텍스처를 주입하는 부분이다.
/** Creates the view's uniform buffers given a set of view transforms. */
void FViewInfo::SetupUniformBufferParameters(
const FViewMatrices& InViewMatrices,
const FViewMatrices& InPrevViewMatrices,
FBox* OutTranslucentCascadeBoundsArray,
int32 NumTranslucentCascades,
FViewUniformShaderParameters& ViewUniformShaderParameters) const
{
......
//----YHRP---- HistoryPPR Texture
FRHITexture* HistoryPPRTextureFound = nullptr;
......
//----YHRP---- HistoryPPR
const TRefCountPtr<IPooledRenderTarget>& PooledHistoryPPRTextureFound = PrevViewInfo.MobilePixelProjectedReflection;
if(PooledHistoryPPRTextureFound.IsValid())
{
HistoryPPRTextureFound = PooledHistoryPPRTextureFound->GetRHI();
}
......
//----YHRP---- History PPR Texture
ViewUniformShaderParameters.HistoryPPRTex = OrBlack2DIfNull(HistoryPPRTextureFound);
ViewUniformShaderParameters.HistoryPPRTexSampler = TStaticSamplerState<SF_Bilinear>::GetRHI();
......
}
이제 머티리얼에서 다음과 같이 텍스처 객체와 샘플러에 접근할 수 있다.
View.HistoryPPRTex
View.HistoryPPRTexSampler
커스텀 머티리얼 노드로 편리하게 사용하기

위 코드를 머티리얼 노드로 만들면 훨씬 편하게 사용할 수 있다. 노드 생성 자체는 그리 복잡하지 않으므로 코드를 직접 보여주겠다.
헤더 파일은 다음과 같다.
UCLASS(collapsecategories, hidecategories = Object, MinimalAPI)
class UMaterialExpressionExponential : public UMaterialExpression
{
GENERATED_UCLASS_BODY()
UPROPERTY()
FExpressionInput Input;
//~ Begin UMaterialExpression Interface
#if WITH_EDITOR
virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override;
virtual void GetCaption(TArray<FString>& OutCaptions) const override;
#endif
//~ End UMaterialExpression Interface
};
CPP 구현 코드는 다음과 같다.
#define LOCTEXT_NAMESPACE "MaterialExpressionHistoryPR"
UMaterialExpressionHistoryPR::UMaterialExpressionHistoryPR(const FObjectInitializer& ObjectInitializer)
:Super(ObjectInitializer)
{
// Structure to hold one-time initialization
struct FConstructorStatics
{
FText NAME_Utility;
FConstructorStatics()
: NAME_Utility(LOCTEXT("Utility", "Utility"))
{}
};
static FConstructorStatics ConstructorStatics;
#if WITH_EDITORONLY_DATA
MenuCategories.Add([ConstructorStatics.NAME](http://ConstructorStatics.NAME)_Utility);
#endif
}
#if WITH_EDITOR
int32 UMaterialExpressionHistoryPR::Compile(FMaterialCompiler* Compiler, int32 OutputIndex)
{
if(!Input.GetTracedInput().Expression)
{
return Compiler->Errorf(TEXT("Missing UV input"));
}
return Compiler->HistoryPR(Input.Compile(Compiler));//TODO:Fix
}
void UMaterialExpressionHistoryPR::GetCaption(TArray<FString>& OutCaptions) const
{
OutCaptions.Add(TEXT("HistoryPR"));
}
FName UMaterialExpressionHistoryPR::GetInputName(int32 InputIndex) const
{
if(InputIndex == 0)
{
return TEXT("ViewportUV");
}
return FName();
}
#endif
#undef LOCTEXT_NAMESPACE
//----YHRP---- HistoryPR
int32 FHLSLMaterialTranslator::HistoryPR(int32 InputUV)
{
FString InputUVCode = *GetParameterCode(InputUV);
return AddCodeChunk(MCT_Float4, TEXT("Texture2DSampleLevel(View.HistoryPPRTex, View.HistoryPPRTexSampler, %s, 0.0f)") , *InputUVCode);
}
정리하며
두 가지 방법 모두 렌더링 파이프라인의 텍스처를 머티리얼에서 접근할 수 있게 해준다. BasePassUniformParameters 방식은 BasePass에서만 동작하지만 구현이 비교적 직관적이다. 반면 ViewUniformBuffer 방식은 더 광범위한 곳에서 접근 가능하고 다른 머티리얼 접근 가능 텍스처들과 같은 방식으로 관리되므로 일관성이 있다. 프로젝트의 요구사항에 맞춰 적절한 방법을 선택하면 된다.
원문
https://zhuanlan.zhihu.com/p/663995419?share_code=1aUw7yS9RAmux&utm_psn=1963577349099487371
'TECH.ART.FLOW.IO' 카테고리의 다른 글
| [번역] UE5. 카툰 렌더링 셰이딩 파트 4: 앰비언트 라이트와 GI (0) | 2025.10.23 |
|---|---|
| [번역] UE5 Shell-Fur 모발 렌더링 구현 (0) | 2025.10.23 |
| [번역] PredictFootIK 구현 (0) | 2025.10.15 |
| [번역] 언리얼엔진5 SceneViewExtension을 사용하여 Gbuffer 데이터 수정하기 (0) | 2025.10.14 |
| "Advanced Horse IK Solution" related of tech for Study lists. (0) | 2025.10.13 |