대규모 월드 좌표(Large World Coordinates, LWC)는 이름이 DoubleFloat 인 수학 라이브러리를 사용하는데, 이 라이브러리는 단정밀도 부동 소수점 수치 쌍으로 배정밀도 부동 소수점 산술을 에뮬레이트합니다.
이 라이브러리의 C++/HLSL 타입 및 연산자는 Math/DoubleFloat.h 및 DoubleFloat.ush 소스 파일에 있습니다.
사용할 수 있는 HLSL 타입은 다음과 같습니다.
| HLSL 타입 | 참고 사항 |
|---|---|
FDFScalar FDFVector2/3/4 |
이러한 타입은 float1/2/3/4에 대한 정밀도가 더 높은 카운터파트입니다. 내부적으로는 두 개의 floatN 벡터, High와 Low로 구성되어 있습니다. |
FDFMatrix |
float4x4와 비슷하지만, 추가 float3 PreTranslation 좌표가 포함되어 있습니다. 이 타입에서 벡터를 곱하면, float4x4 매트릭스가 적용되기에 앞서 벡터가 먼저 WorldPosition으로 이동됩니다. 이 타입은 월드 스페이스로 트랜스폼할 때 유용합니다(LocalToWorld). |
FDFInverseMatrix |
float4x4와 비슷하지만, 추가 float3 PostTranslation 좌표가 포함되어 있습니다. 이 타입에서 벡터를 곱하면, float4x4 매트릭스가 적용된 후 벡터가 PostTranslation으로 변환됩니다. 이 타입은 월드 스페이스로 트랜스폼할 때 유용합니다(LocalToWorld). |
연산자에는 접두사 'DF'가 붙으며, 정밀도와 퍼포먼스 사이에서 절충할 수 있는 몇 가지 베리언트가 제공됩니다. 베리언트는 함수 이름에 접두사나 접미사로 표시되어 있습니다. 예를 들어 다음과 같이 할 수 있습니다.
DFFast*는 접미사입니다. 한 예로DFFastAdd는 접미사 베리언트가 됩니다. 이 함수는 빠르지만 정밀도가 떨어집니다. 이 베리언트를 사용하면 정밀도가 낮은 대신 최대 속도를 얻을 수 있습니다. 베이스 연산자의 경우, 각 함수에는 정밀도 바운드에 대한 세부정보를 제공하는 문서에 대한 참조가 나와 있습니다.DF*Demote는 접두사입니다. 한 예로 DFFastAddDemote는 접두사 베리언트가 됩니다. 이 베리언트는 배정밀도 대신 32비트 결과를 반환합니다.DF*Demote함수를 사용할 수 있으며, 이는LWCToFloat함수와 유사합니다.DF*Demote를 사용하면 불필요한 연산이 줄어들고 효율성이 높아집니다. 자르기에 선호되는 방법입니다.
DoubleFloat 수학 라이브러리의 많은 수학 함수는 상당한 퍼포먼스 비용이 발생합니다. 최적의 퍼포먼스와 정밀도를 달성하는 방법에 대한 안내는 이 페이지의 이동된 월드 스페이스 섹션을 참조하세요.
FLWCVector 및 유사 타입
언리얼 엔진에는 대규모 월드 좌표에서 사용할 수 있도록 마련된 다양한 기반 수학 타입 구조체를 활용하는 몇 가지 복합 타입이 있습니다. 이러한 타입에는 접두사 'FLWC'가 붙으며, LargeWorldCoordinates.ush 소스 파일에서 찾을 수 있습니다. 현재 대부분의 셰이더는 DoubleFloat 타입을 사용합니다. 하지만 일부 시스템에서는 FLWC 타입도 여전히 사용하고 있습니다.
사용할 수 있는 HLSL 타입은 다음과 같습니다.
| HLSL 타입 | 참고 사항 |
|---|---|
FLWCScalar FLWCVector2/3/4 |
이 타입은 float1/2/3/4와 비슷하지만, 추가적인 타일 좌표가 포함되어 있습니다. 따라서 FLWCVector2는 float2 Tile과 float2 Offset으로 이루어집니다. 표시되는 값은 다음과 같은 공식으로 계산됩니다. Tile * TileSize + Offset, 여기에서 TileSize는 256k로 정의된 상수 값입니다. |
FLWCMatrix FLWCInverseMatrix |
이 타입은 float4x4와 비슷하지만, 둘 다 추가적인 float3 타일 좌표가 포함되어 있습니다. |
FLWCMatrix |
매트릭스를 곱한 후 결과에 타일 좌표를 추가합니다. |
FLWCInverseMatrix |
매트릭스를 곱하기 전 타일 좌표를 추가합니다. |
FLWCMatrix |
이 타입은 월드 스페이스로 트랜스폼 시 유용합니다(LocalToWorld). |
FLWCInverseMatrix |
이 타입은 월드 스페이스에서 트랜스폼 시 유용합니다(WorldToLocal). |
LWCAdd 또는 LWCRsqrt 와 같은 연산을 이러한 타입에서 수행할 수 있습니다. LWC 입력 값을 가져오는 연산은 LWC 출력을 반환하고( LWCAdd ), 그 외의 경우 일반 플로트 출력을 반환합니다( LWCRsqrt ). 좌표를 작게 만드는 연산은 일반 플로트로 되돌리기를 제공합니다.
FLWC 타입은 DoubleFloat 라이브러리에 비교하면 떨어지는 정밀도를 제공합니다. 타일 에지에 접근하는 벡터에서, 고정 타일 크기는 Tile 컴포넌트의 비트가 사용되지 않고 Offset 은 잘린 좌표만 저장할 수 있다는 것을 의미합니다. 언리얼 엔진 5.4 이전 버전에서 이로 인해 오브제트가 월드를 이동할 때 정밀도가 정해지지 않는 결과가 발생했고, 주로 오브젝트 지터와 흔들림이 드러났습니다. DoubleFloat 수학 타입은 고정된 크기를 제거하여 이 문제를 해결합니다. 고정된 크기가 제거되면 두 컴포넌트 모두 값의 규모에 관계없이 항상 가능한 한 높은 정밀도를 저장합니다. 이는 고정 소수점과 부동 소수점 산술 간 차이와 개념적으로 유사합니다.
머티리얼
머티리얼 트랜슬레이터는 LWC 값으로 작동합니다. 머티리얼 내 일부 노드는 플로트가 아닌 LWC 값을 출력합니다. 특히 WorldPosition, 오브젝트, 카메라, 액터, 파티클과 같은 월드 스페이스 위치에 대한 좌표 기반 노드는 TransformPosition 노드와 함께 LWC 값을 출력합니다. 또한 입력이 LWC 타입 변수인 경우, 대부분의 수학 노드는 LWC 타입을 출력합니다.
머티리얼에서 LWC를 사용하는 경우 퍼포먼스 비용이 추가됩니다. 절대 월드 스페이스는 LWC 스케일에서 작동하지만, 그 대가로 퍼포먼스는 낮아지게 됩니다. LWC를 사용하는 수학 노드는 계산에 더 오랜 시간이 걸리며, 따라서 더 많은 비용이 소요됩니다.
월드 스페이스 값을 받거나 생성하는 대부분의 머티리얼 노드에서 월드 포지션 원점 타입(World Position Origin Type) 프로퍼티를 설정하여 카메라 상대 월드 포지션(Camera Relative World Position) (또는 이동된 월드 스페이스)를 사용하면 최적의 정밀도와 퍼포먼스를 구현할 수 있습니다.
머티리얼 그래프는 퍼포먼스 제약으로 인해 계속 타일 오프셋 시스템을 사용하지만, 버텍스 팩토리와 같은 백엔드 시스템이 DoubleFloat를 사용하도록 업데이트되었으므로 이동된 월드 스페이스를 사용할 때 정밀도의 향상을 확인할 수 있습니다.
머티리얼에 LWC 사용 시 추가적인 주의 사항은 다음과 같습니다.
- 절대 월드 스페이스는 LWC 스케일에서 작동하지만, 그 대가로 퍼포먼스는 하락합니다.
- LWC를 사용하는 수학 노드는 계산에 더 오랜 시간이 걸리며, 따라서 많은 비용이 소요됩니다.
- 접두사 'FWS'가 붙은 에일리어스 타입은
WorldSpaceMath.ush헤더에서 찾을 수 있습니다. 이는 추상화를 위해 사용되며,FLWC*또는FDF타입 중 하나로 구현되어 있습니다. 머티리얼 템플릿 헤더와 같은 머티리얼 그래프와 상호작용하는 HLSL 코드를 작성할 때 이를 사용할 수 있습니다.
UE4~UE5의 변환 가이드
언리얼 엔진 4 이하 버전에서 프로젝트를 이주하는 경우, 기존 HLSL 코드베이스를 언리얼 엔진 5로 포팅할 수 있습니다. 성공적인 변환을 위한 몇 가지 제안이 아래에 나와 있습니다.
- 월드 스페이스에서 값/매트릭스 액세스 시 View 를 PrimaryView 로 변경(LocalToWorld, PreViewTranslation)
- 월드 스페이스에서 작동하는 코드가 컴파일되지 않고(예: 누락된 함수 오버로드 또는 타입 변환인 경우) 코드베이스를 리팩터링할 리소스가 없으면, 필요시 LWCHackToFloat 함수를 사용할 수 있습니다. 예를 들어
LWCHackToFloat(PrimaryView.PreViewTranslation)를 사용할 수 있습니다.
그래도 이벤트에서 코드가 컴파일되지 않으면 다음을 시도해 볼 수 있습니다.
- WorldSpace 대신 TranslatedWorldSpace에서 작동하도록 셰이더 코드 변경을 고려해 보세요.
- 월드 스페이스 값은 LWC 타입(float3 대신
FLWSVector3필요) 및 해당 LWC 함수를 필요로 합니다.
LWC 타입은 유니폼 버퍼 내에 직접 포함되어 있지 않습니다. FLWC* 타입이 사용되는 경우 변수는 주로 베이스 컴포넌트(Tile, Offset 등)로 저장됩니다. 보통 엔티티는 모두 동일한 타일 좌표를 사용할 수 있는 몇 가지 데이터 조각을 포함하고 있습니다(예: WorldToLocal, LocalToWorld, AbsoluteWorldPosition). 따라서 일반적으로 오프셋 값(예: RelativeWorldToLocal, LocalToRelativeWorld, RelativeWorldPosition)을 단일 공유 float3 TilePosition 값과 함께 유니폼 버퍼에 저장합니다. 이 유니폼 데이터는 실제 LWC 값을 포함하는 새로운 구조체 타입으로 '언패킹'되고, 여기에서 여러 값이 셰이더 내 단일 TilePosition과 함께 초기화됩니다.
FDF* 타입에 대해 공유될 수 있는 타일 좌표는 없습니다. 메모리 비용이나 대역폭이 우려되는 경우 float3 레퍼런스 포인트와 이 포인트를 기준으로 한 몇몇 비 LWC 값을 저장하는 것이 가능합니다. 이는 DFTwoSum(Offset, Base) 를 사용하여 셰이더에서 DoubleFloat 값으로 재결합할 수 있습니다. 그런 다음 |Offset|이 |Base|보다 큰 것을 확신할 수 있는 경우, 연산 횟수가 절반인 DFFastTwoSum(Offset, Base)을 사용할 수 있습니다.
많은 변수가 셰이더 코드 내에서 LWC 베리언트로 전환되었습니다. 몇 가지 주목할 만한 베리언트는 다음과 같습니다.
SceneData.ush,FPrimitiveSceneData,FInstanceSceneData는 다양한 최신 매트릭스 및 위치 벡터를 가지고 있습니다.- 라이트 위치 및 리플렉션 캡처 위치
- 글로벌 카메라 유니폼에서 다양한 매트릭스와 오프셋이 변경되었습니다(PreViewTranslation 등). SceneView와 FNaniteView가 이에 해당합니다.
언리얼 엔진 5.0 이전에 글로벌 카메라 유니폼은 인스턴스화된 스테레오 렌더링을 처리하는 메시 렌더링 시 ResolvedView 를 통해 액세스되거나 포스트 프로세싱 및 기타 글로벌 패스 렌더링 시 View 를 통해 액세스되었습니다. View 는 이러한 버전의 언리얼 엔진 내 글로벌 유니폼 버퍼를 직접 참조합니다. PrimaryView 는 언패킹된 글로벌 뷰 구조체를 참조하는 새 에일리어스로 추가되었습니다. 따라서 View.PreViewTranslation 과 같은 이름이 PrimaryView.PreViewTranslation 으로 변경되었습니다. ResolvedView.PreViewTranslation 은 적용 가능한 경우 계속 작동하며, 일반 View 에일리어스에서 비 LWC 양에 액세스할 수 있습니다.
이러한 글로벌 타입을 전환하는 경우 LWC를 지원하기 위해 셰이더 코드를 리팩터링해야 하는 위치를 일반적으로 표시해 줍니다. 예를 들어 다음과 같이 할 수 있습니다.
float3 WorldPosition = mul(Input.LocalPosition, View.LocalToWorld);
//위의 줄은 더 이상 컴파일되지 않으므로, 다음과 같이 리팩터링되어야 합니다.
FDFVector3 WorldPosition = DFMultiply(Input.LocalPosition, PrimaryView.LocalToWorld);
초기 LWC 패스는 모든 셰이더가 LWC와 함께 제대로 작동하는지 확인하지 않습니다. 글로벌 함수 LWCHackToFloat 를 사용하여 LWC 타입을 비 LWC 타입으로 변환할 수 있습니다.
LWCHackToFloat 는 DFDemote 및 LWCToFloat 함수의 래퍼처럼 작동합니다. 래핑되지 않은 버전은 LWC 스케일에서 변환이 안전함을 확신할 수 있는 경우에 사용할 수 있습니다. LWCHackToFloat 는 검색 가능한 마커입니다. 코드베이스를 리팩터링하여 WorldPosition 을 float3 에서 FDFVector3 으로 전환할 시간이 없으면 대신 다음 코드를 사용할 수 있습니다.
float3 WorldPosition = mul(Input.LocalPosition, LWCHackToFloat(PrimaryView.LocalToWorld));
언리얼 엔진 5.0 이상 버전에서 프로젝트를 업그레이드하고 FLWC* 타입을 사용하는 경우, DFFromTileOffset 및 DFToTileOffset 함수로 이러한 타입과 새 DoubleFloat 타입 간에 변환할 수 있습니다. 또한 LWCHackToFloat 와 비슷하게, 마커로 사용되는 에일리어스인 DFFromTileOffset_Hack 및 DFToTileOffset_Hack 도 있습니다. 타입 간에 변환하는 경우 퍼포먼스와 정밀도가 손실되므로, 꼭 필요할 때만 사용해야 합니다.
TranslatedWorldSpace는 언리얼 엔진 셰이더에서 사용되는 기존 엔진 개념입니다. 이전에는 카메라 원점을 기준으로 작동하여 정밀도를 높이기 위해 사용되었습니다. TranslatedWorldSpace에서 작업 시 플로트를 사용하는 것이 안전하므로, 이러한 비헤이비어는 LWC에도 유용합니다. WorldPosition을 배정밀도 값으로 변환하는 것보다는, 함수를 리팩터링하여 이동된 월드 스페이스를 사용할 것을 권장합니다. 이 방법을 사용하면 높은 정밀도를 유지하면서도 뛰어난 퍼포먼스 결과를 얻을 수 있습니다. 예를 들어 다음과 같이 할 수 있습니다.
float3 TranslatedWorldPosition = mul(Input.LocalPosition, PrimaryView.LocalToTranslatedWorld);
이동된 월드 스페이스
최적의 퍼포먼스와 정밀도를 위해 절대 월드 스페이스(왼쪽)를 카메라 상대 월드 스페이스 와 같은 이동된 월드 스페이스(오른쪽)로 변환할 수 있습니다. 이점을 활용할 수 있도록 프로젝트에서 가능한 한 빠르게 이동된 월드 스페이스로 변환하는 것이 가장 좋습니다.
아래에는 절대 월드 스페이스(왼쪽), 카메라 스페이스(중앙), 이동된 월드 스페이스(오른쪽)의 예시가 나와 있습니다.
![]() |
![]() |
![]() |
|---|---|---|
| 절대 월드 스페이스 | 카메라 스페이스 | 이동된 월드 스페이스(카메라 상대 월드 스페이스) |
LWCAdd(WorldPosition, PreViewTranslation) 함수를 타일 오프셋 타입과 함께 사용하여 월드 스페이스를 변환할 수 있습니다. DoubleFloat 라이브러리에서 이를 위한 가장 빠른 메서드는 DFFastToTranslatedWorld() 입니다. 이 메서드는 XToWorld 또는 WorldToX 매트릭스를 XToTranslatedWorld 및 TranslatedWorldToX 로 변환하는 데에도 사용할 수 있습니다. LocalToWorld 매트릭스를 통해 로컬 위치를 이동된 월드 스페이스로 변환하는 가장 빠른 방법은 DFTransformToTranslatedWorld() 를 사용하는 것입니다.
이러한 함수는 특정 최적화를 적용하여 오버헤드를 줄여 주며, DFFastAdd 를 비롯한 다른 유사한 함수를 사용하는 것보다 빠릅니다.
DoubleFloat의 수학적 배경
FDFVector 타입은 High 및 Low 단정밀도 벡터로 구성되어 있습니다. High 컴포넌트는 가장 가까운 32비트 플로트로 반올림된 값과 같으며, Low 값은 반올림 시 발생하는 (대부분의) 오류를 캡처합니다.
64비트 더블에서 FDFScalar 로의 변환은 다음과 같이 수행됩니다.
FDFScalar(double Input)
{
float High = (float)Input;
float Low = (float)(Input - High);
}
아래 표는 숫자 1.1이 17자리 소수점으로 반올림되는 다양한 바이너리 표현의 예를 보여줍니다.
(double)1.1 |
1.1000000000000001 |
(float)1.1 |
1.1000000238418579 |
FDFScalar.High |
1.1000000238418579 |
FDFScalar.Low |
-2.3841858265427618e-08 |
다음은 이 주제에 대한 몇 가지 자료입니다.
- 학술적 개론은 Extended-Precision Floating-Point Numbers for GPU Computation by Andrew Thall (2006) 논문을 참조하세요.
- 보다 포괄적이고 정확한 자료는 Formalization of Double-Word Arithmetic, and Comments on “Tight and Rigorous Error Bounds for Basic Building Blocks of Double-Word Arithmetic” by Jean-Michel Muller and Laurence Rideau (2022)를 참조하세요.


