문제 상황
외부 서버에서 다운로드한 이미지를 UMG에 표시할 때, 텍스처 크기가 제각각이라 고정 크기 위젯에 넣으면 찌부되는 문제가 있다. 이를 해결하기 위해 ScaleBox(Fill 모드)로 감싸는 건 흔한 접근인데, 여기에 라운드 처리까지 해야 하면 문제가 복잡해진다.
일반적으로 떠올리는 구조는 이렇다:
RetainerBox (라운드 처리 Material 적용)
└─ InvalidationBox
└─ ScaleBox (Fill)
└─ Image (다운로드 텍스처)
이 구조에서 스크롤 뷰 안에 넣으면 문제가 발생한다. RetainerBox는 자식 위젯을 RenderTarget에 그리는 방식인데, 스크롤로 visible 영역이 줄어들면 RenderTarget 크기도 같이 변한다. 그 결과 UV가 꼬이면서 텍스처가 검은색으로 말려 올라가는 현상이 생긴다.

해결 방법
ScaleBox, RetainerBox, InvalidationBox를 전부 걷어내고, Material 하나에서 Fill 스케일링과 Rounded Corner를 동시에 처리한다. Image 위젯에 이 Material Instance를 직접 넣으면 래핑 없이 깔끔하게 동작하고, 스크롤 시 RenderTarget 크기 변동 문제도 원천 차단된다.

머티리얼 기본 설정
- Material Domain: User Interface
- Blend Mode: Translucent
- Shading Model: Unlit
파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
Texture |
Texture2D | 다운로드 받은 텍스처 |
TextureAspectRatio |
Scalar | 텍스처의 가로/세로 비율 (Width / Height) |
WidgetAspectRatio |
Scalar | 위젯의 가로/세로 비율 (Width / Height) |
CornerRadius |
Scalar | 라운드 반지름 (0 ~ 0.5, UV 공간 기준) |
TextureAspectRatio는 텍스처 다운로드 완료 시점에 GetSizeX() / GetSizeY()로 계산해서 넘기면 된다.
Custom 노드 1: FillUV
CSS의 object-fit: cover와 동일한 로직이다. 텍스처 비율과 위젯 비율을 비교해서 UV를 재계산한다. 짧은 쪽에 맞추고 긴 쪽은 크롭한다.
Name: FillUV / Return Type: float2
Inputs:
UV(float2) — TexCoord[0]TexAspect(float) — TextureAspectRatio 파라미터WidgetAspect(float) — WidgetAspectRatio 파라미터
float2 Scale;
if (TexAspect > WidgetAspect)
{
// 텍스처가 더 넓음 → 좌우 크롭
Scale = float2(WidgetAspect / TexAspect, 1.0);
}
else
{
// 텍스처가 더 높음 → 상하 크롭
Scale = float2(1.0, TexAspect / WidgetAspect);
}
// 중심 기준으로 스케일 적용
return (UV - 0.5) * Scale + 0.5;
Custom 노드 2: RoundedRect
UV 공간에서 Rounded Rectangle의 SDF(Signed Distance Field)를 계산해서 마스크를 만든다. fwidth 기반 안티앨리어싱으로 가장자리가 부드럽게 처리된다.
Name: RoundedRect / Return Type: float
Inputs:
UV(float2) — TexCoord[0]Radius(float) — CornerRadius 파라미터
// UV를 중심 기준 좌표로 변환 (0~1 → -1~1 → abs로 1사분면)
float2 Pos = abs(UV - 0.5) * 2.0;
// 라운드 SDF 계산
float2 Q = Pos - (1.0 - Radius);
float Dist = length(max(Q, 0.0)) - Radius;
// 안티앨리어싱 (fwidth로 픽셀 크기 기반 부드러운 경계)
float AA = fwidth(Dist) * 1.5;
return 1.0 - smoothstep(-AA, AA, Dist);
노드 연결
[TexCoord] ──┬──→ [FillUV] ──→ UVs ──→ [TextureSample] ──→ Final Color
│
[TexAspect] ─┤
[WidgetAspect]┘
[TexCoord] ──→ [RoundedRect] ──→ Opacity
[CornerRadius]─┘
FillUV의 출력은 TextureSample의 UV 입력에 연결하고, RoundedRect의 출력은 Opacity에 연결한다. TexCoord는 두 Custom 노드에 각각 연결해야 한다.
정리
- ScaleBox + RetainerBox 래핑은 스크롤 환경에서 RenderTarget 크기 변동 문제를 일으킨다
- Material 자체에서 Fill + Round를 처리하면 래핑이 필요 없고 스크롤 문제도 없다
- Fill UV 계산은 텍스처와 위젯의 비율을 비교해서 짧은 축 기준으로 스케일하는 것이 핵심
- Rounded Corner는 SDF 기반으로 구현하면 해상도와 무관하게 깔끔한 결과를 얻을 수 있다
CornerRadius는 0.05 ~ 0.15 정도가 자연스럽다