대규모 월드 좌표 프로젝트 변환 가이드라인

정밀도 손실을 최소화하면서 UE4 프로젝트를 UE5로 변환하는 방법을 안내하는 문서입니다.

대규모 월드 좌표 (LWC )는 기존 프로젝트에 문제없이 통합되도록 설계되었습니다. 그러나 프로젝트를 언리얼 엔진 5 에 임포트하여 이 가이드라인을 따라 변환할 때 몇 가지 작은 문제가 발생할 수 있습니다. 소스 코드에서 베리언트 타입은 디폴트가 더블 타입인 typedef 에일리어스를 가집니다. 이름의 경우 원본 언리얼 엔진 4 (UE4) 이름과 일치합니다. 기존 코드는 리컴파일되어 자동으로 더블을 지원합니다.

변경된 에일리어스 타입

향후 호환성을 위해 가급적 FVector 에일리어스를 사용할 것을 권장합니다. 특정 타입이 꼭 필요한 경우에만 명시적 FVector3f 또는 FVector3d 타입으로 전환하세요.

아래의 표는 다음 타입의 베리언트 변환을 나타냅니다.

디폴트(더블 베리언트 에일리어스)

플로트 베리언트

더블 베리언트

FVector

FVector3f

FVector3d

FVector2D

FVector2f

FVector2d

FVector4

FVector4f

FVector4d

FMatrix(FScaleMatrix, FQuatRotationTranslationMatrix 등)

FMatrix44f

FMatrix44d

FPlane

FPlane4f

FPlane4d

FQuat

FQuat4f

FQuat4d

FRotator

FRotator3f

FRotator3d

FTransform

FTransform3f

FTransform3d

FBox

FBox3f

FBox3d

FBox2D

FBox2f

FBox2d

FSphere

FSphere3f

FSphere3d

FBoxSphereBounds

FBoxSphereBounds3f

FBoxSphereBounds3d

FCapsuleShape

FCapsuleShape3f

FCapsuleShape3d

FDualQuat

FDualQuat4f

FDualQuat4d

FRay

FRay3f

FRay3d

향후 호환성을 유지하기 위해, 꼭 필요한 경우가 아니면 베리언트 타입을 사용하는 것을 권장하지 않습니다. 대신 위의 에일리어스에 나열된 원본 버전을 사용하고, 특정 타입이 꼭 필요한 경우에만 명시적 플로트 및 더블 타입을 사용하세요.

베리언트 간에 형변환하려면 다음 예시와 같이 명시적으로 수행해야 합니다.

    FVector Vec(1.0, 2.0, 3.0); 

    FVector3f AsFloat = FVector3f(Vec);

    FVector3f AsFloat = Vec;    // 이 경우 컴파일이 실패합니다.

FMath의 더블 지원

FMath 함수가 더블을 지원하도록 확장되었습니다. 여기에는 새로운 타입에서의 표준 수학 연산 지원이 포함됩니다.

UnrealMathSSE.h/UnrealMathNeon.h 디렉터리에 위치한 VectorRegister의 벡터화 지원도 포함됩니다. VectorRegister4f 및 VectorRegister4d 타입을 추가할 수 있습니다.

대규모 월드 실험

UE5의 대규모 월드의 베타 상태의 결과로, 디폴트 WORLD_MAX 크기가 UE4 WORLD_MAX 크기(21km)로 유지되며 월드 바운드에서의 엔진 체크가 활성화 상태로 유지됩니다. 대규모 월드의 스케일을 실험하는 옵션은 다음과 같이 두 가지가 있습니다.

WorldSettings 클래스에 액세스하여 bEnableLargeWorlds 부울을 다음과 같이 true 로 세팅하면 바운드 체크를 비활성화할 수 있습니다.

    AWorldSettings::bEnableLargeWorlds = true

이렇게 하면 WORLD_MAX 의 값이 약 21km로 유지되며, 언리얼 엔진 5.0 초기 출시의 실험 안정성이 향상됩니다.

다른 대안으로는 다음과 같이 UE_USE_UE4_WORLD_MAX 의 글로벌 값을 설정하여 한층 규모가 큰 월드 바운드를 활성화할 수 있습니다.

UE_USE_UE4_WORLD_MAX=0 

이렇게 하면 WORLD_MAX 값이 약 8,800만 킬로미터로 설정됩니다.

이 값은 언리얼 엔진의 향후 출시에서 변경될 수 있고 안정성 문제가 나타날 수 있습니다. 안정성 문제는 언리얼 엔진 5 개발 과정 전반에서 지속적으로 최적화될 예정입니다.

코드 컴파일 오류

다음은 몇 가지 컴파일 오류 카테고리와 권장 솔루션입니다.

베리언트 타입 전방 선언

전방 선언문을 작성할 때는 매크로 UE_DECLARE_LWC_TYPE을 사용해야 합니다.

예를 들어 다음과 같이 선언해서는 안 됩니다.

struct FVector; 

대신 다음과 같이 선언해야 합니다.

UE_DECLARE_LWC_TYPE (Vector, 3);

타입별 올바른 UE_DECLARE_LWC_TYPE 사용의 예시는 Engine/Source/Runtime/Core/Public/CoreFwd.h 를 참조하세요.

베리언트 타입 전방 선언은 generated.h 또는 CoreMinimal.h를 포함하는 파일에는 필요하지 않습니다.

오류: C2027/C2371

프로젝트가 FVector 또는 다른 베리언트 타입에 C2027/C2371 오류를 생성한다면, 해당 타입을 코드 내 구조체로 전방 선언한 것이 문제의 원인일 가능성이 높습니다.

경고: "아규먼트로 인해 함수 결정 모호성이 발생합니다(Arguments cause function resolution ambiguity)"

베리언트 타입, 부동 소수점 또는 상수 아규먼트가 혼합된 멀티 아규먼트 FMath 함수를 호출할 경우 위와 비슷한 경고가 나타날 수 있습니다.

프로그램에서 발생하는 내재적 정밀도 손실 문제를 가리키는 것일 수 있으므로 함수 결정 모호성 경고를 무시해서는 안 됩니다.

명시적 템플릿 아규먼트를 전달하거나, 일치하지 않는 타입을 형변환하거나, 원하는 타입에 맞도록 상수를 수정하여 정밀도 오류를 수정할 수 있습니다.

예시:

    FMath::Max(MyVector.X, double(MyFloat));

플로트 및 더블 베리언트 타입 간 변환 시 명시적 형변환 필요

코드 내 플로트 및 더블 베리언트 타입 간 변환 시 원치 않는 정밀도 또는 퍼포먼스 문제를 피하기 위해 명시적 형변환이 필요합니다.

예를 들어, FVector4d를 필요로 하는 함수에 FVector3f를 전달할 때 명시적 형변환이 필요할 수 있습니다. FVector3f를 FVector3d로 형변환하면 FVector3d가 FVector4d로 묵시적 형변환되어 변환이 완료됩니다.

명시적 형변환은 내부 시스템 내 유사한 타입에 적용됩니다. 예를 들어 Chaos FVec3을 FVector3f으로 변환할 때가 여기에 해당합니다.

셰이더 파라미터

GPU에서는 더블 파라미터가 지원되지 않습니다. 그러므로 FVector, FVector2D, FVector4, FMatrix는 더 이상 네이티브 코드로 이루어진 SHADER_PARAMETER 선언 내에서 지원되지 않으며, 해당하는 타입의 플로트 베리언트로 전환해야 합니다.

런타임 검사 실패

다음과 같이 런타임 검사 실패가 발생할 수 있습니다.

TArray::BulkSerialize의 예상치 못한 엘리먼트 배열 크기(Unexpected element array size in TArray::BulkSerialize)

이는 더블 베리언트로 자동 변환된 타입이 포함된 구조체를 벌크 시리얼라이징한 결과일 수 있습니다. 다음과 같이 아카이브 버전을 기반으로 bForcePerElementSerialization 파라미터를 추가하여 이 문제를 해결할 수 있습니다.

MyArray.BulkSerialize(Ar, Ar.UEVer() < EUnrealEngineObjectUE5Version::LARGE_WORLD_COORDINATES); 

최신 버전의 아카이브를 사용할 것을 권장합니다.

정밀도 문제

LWC 타입 컴포넌트를 더블 타입으로 변환하면 변환된 UE4 프로젝트에서 정밀도 문제가 생길 수 있습니다. 특히 이러한 컴포넌트가 플로트일 것이라고 예상하고 코드를 작성한 경우 더욱 그렇습니다. 업그레이드 후 이 문제에 대해 코드를 검사할 것을 권장합니다.

안전하지 않은 Typecast 경고 활성화

다음의 몇 가지 방법을 사용하면 프로젝트가 안전하지 않은 Typecast 경고를 받도록 활성화할 수 있습니다.

모듈 사용

프로젝트의 build.cs 파일에 다음을 추가할 수 있습니다.

UnsafeTypeCastWarningLevel = WarningLevel.Warning;

이렇게 하면 대규모 코드베이스에 상당수의 경고가 생성되기는 하지만, 정밀도 손실로 이어지는 상황을 감지 및 수정하는 데 매우 유용합니다.

현재 일부 엔진 헤더 파일이 안전하지 않은 Typecast 경고를 생성합니다. 이는 향후 출시에서 수정될 예정입니다.

싱글 파일 또는 코드 블록 사용

다음 매크로를 사용하면 안전하지 않은 Typecast 경고를 토글할 수 있습니다.

매크로

설명

PRAGMA_FORCE_UNSAFE_TYPECAST_WARNINGS

이 지점 밖의 안전하지 않은 Typecast가 모듈 세팅과 관계없이 오류를 생성합니다.

PRAGMA_DISABLE_UNSAFE_TYPECAST_WARNINGS

이 지점 밖의 안전하지 않은 Typecast가 모듈 세팅과 관계없이 무시됩니다.

PRAGMA_RESTORE_UNSAFE_TYPECAST_WARNINGS

FORCE/DISABLE 블록에 대한 종료 마커입니다. 비헤이비어가 모듈 세팅으로 되돌아갑니다. 블록이 올바르게 닫히지 않으면 CheckBalancedMacros 자동화 스크립트가 실패합니다.

타입 컴포넌트 사본 저장 시 정밀도 손실 방지

타입 컴포넌트에 직접 액세스하는 코드는 정밀도 손실을 방지하기 위해 일부 리팩터링이 필요할 수 있습니다. 정밀도 오류는 유효하지 않은 코드 실행으로 이어집니다. 예를 들면 다음과 같습니다.

const float X = MyVector.X; 

// MyVector.X가 현재 예상보다 더 정밀할 수 있습니다.
//SMALL_NUMBER == 1e-8 (0.00000001)
// 더블에서 플로트로 변환할 때 중요 자릿수 6개만큼의 정밀도 오류가 발생할 수 있습니다.

if(FMath::Abs(X - OtherVector.X) > SMALL_NUMBER)
{
 // MyVector.X와 OtherVector.X가 같으면 여기에 포함되지 않을 것으로 예상됩니다.
}

MyVector.X 의 정밀도 더블 값의 경우, XOtherVector.X 델타 간의 차이가 코드가 이 경로를 따르게 하는 큰 원인일 수 있습니다.

기존 UE4 플로트 제한인 10.5km 바운드를 넘어 확장될 예정인 프로젝트의 경우 원점에서 멀어질수록 위험도가 높아집니다.

float X = MyVector.X; 

X += 0.5f;

MyVector.X = X; // 정밀도 손실.

변환된 프로젝트에서 대규모 월드 좌표를 사용할 계획이라면 이러한 정밀도 오류에 대해 코드베이스를 검사해야 합니다.

LWC 타입은 내재된 컴포넌트 타입(플로트 또는 더블)에 FReal 에일리어스를 노출합니다. 기본 타입 위치에 FReal을 사용하면 이러한 타입이 향후 변경되더라도 정밀도 문제를 방지할 수 있습니다. 예를 들면 다음과 같습니다.

FVector::FReal ReallyADoubleNow = FMath::Cos(MyVector.X);

다음과 같이 보다 정교한 접근 방법을 사용하여 코드베이스 전체에서 플로트 타입을 더블로 업데이트할 수도 있습니다.

double ReallyADoubleNow = FMath::Cos(MyVector.X);

타입 컴포넌트에 직접 액세스하는 코드는 정밀도 손실을 방지하기 위해 리팩터링이 필요할 수 있습니다.

플로트 바운드(원점으로부터 10.5km) 내로 유지될 예정인 변환된 UE4 프로젝트의 경우 정밀도 손실이 발생할 수 있지만, 프로젝트에 부정적인 영향을 끼치지는 않습니다.

베리언트 타입 벌크 시리얼라이제이션

기본적으로 디폴트 코어 타입(FVector) 배열의 벌크 시리얼라이제이션은 변환된 프로젝트에 대해 비활성화되어 있습니다. 엔진은 각 벡터 컴포넌트를 플로트로 로드한 후 더블로 변환해야 합니다. 영향을 받은 에셋을 다시 저장할 때는 더블로 작성되며, 벌크 시리얼라이제이션이 다시 작동합니다. 베리언트 타입을 포함한 구조체를 벌크 시리얼라이징할 때는 플로트 베리언트를 사용하도록 변환하거나 BulkSerialize 호출에서 인스턴스당 시리얼라이제이션을 강제 수행 / TCanBulkSerialize 지원을 비활성화해야 합니다.

코어 타입의 메모리 사용 감소

프로퍼티 지정자로 표시된 시리얼라이즈된 코어 타입은 더블과 플로트 베리언트 간의 전환을 자동으로 인식합니다. 이로써 언제든 플로트 베리언트로의 전환으로 인해 낭비되는 메모리를 소급적으로 회수할 수 있습니다.

블루프린트

UE5에서 모든 블루프린트 플로트 타입은 싱글 및 더블 정밀도에서 모두 작동 가능한 변경된 플로트 타입으로 표현됩니다. 이는 블루프린트가 자동으로 LWC 스케일을 지원한다는 것을 의미합니다. 블루프린트의 명시적 플로트/더블 지원을 제거했으므로 임포트된 UE4 블루프린트 프로젝트의 모든 플로트 또는 더블 타입은 새로운 타입으로 승격됩니다.

소스 코드 인터페이스

이제 소스 코드가 플로트 및 더블 타입을 모두 노출할 수 있습니다. 언리얼 헤더 툴 (UHT )은 코드로 이루어진 블루프린트 액세스 가능 부동 소수점 타입을 적절한 싱글(C++ 플로트) 또는 더블(C++ 더블) 정밀도 서브타입의 블루프린트 플로트로 해석하므로, 모든 블루프린트 노드가 제공하는 싱글 또는 더블 정밀도의 플로트 값을 자동으로 변환할 수 있습니다. 부동 소수점 용어를 사용할 때는 주의하는 것이 좋습니다.

블루프린트의 플로트는 싱글 정밀도 또는 더블 정밀도 플로트 중 하나를 가리킬 수 있습니다. 그러나 C++에서는 원하는 정밀도 플로트를 명시적으로 나타내야 합니다. 구체적으로 용어 '플로트'는 싱글 정밀도 플로트를, '더블'은 더블 정밀도 플로트를 나타냅니다. 언리얼 헤더 툴은 두 타입 모두 프로퍼티 또는 함수 파라미터로 유효하게 인식합니다. 타입이 '플로트' 또는 '더블'인 경우 블루프린트는 두 타입을 모두 싱글 '플로트' 타입으로 처리하지만, 플로트가 싱글 또는 더블 정밀도 중 무엇인지 나타내게 됩니다.

single-float-precision-node

블루프린트 사용자의 관점에서는 싱글 또는 더블 정밀도 타입 간에 차이점이 없으며, 이러한 핀은 명시적 형변환 노드 없이도 연결할 수 있습니다. 그렇더라도 언리얼 엔진은 이러한 인스턴스에서 축소 또는 확대 변환 중 하나를 수행해야 할 수 있습니다. 블루프린트 컴파일러는 잠재적인 부동 소수점 변환에 대한 블루프린트 그래프를 자동으로 분석합니다. 감지된 것이 있으면 컴파일러는 내재 코드에 자동으로 형변환 연산을 삽입합니다. 이는 컨테이너에 대해서도 똑같이 작동합니다.

개발자는 대규모 월드 좌표를 활용하기 위해 기존 블루프린트 콘텐츠를 특별히 수정할 필요가 없습니다. 기본적으로 언리얼 엔진은 플로트 핀이 더블 정밀도를 사용하도록 자동 변환합니다. 네이티브 C++ 코드에서 싱글 정밀도 플로트로 사용된 핀이 있다면 해당 핀이 계속해서 싱글 정밀도 플로트를 나타내도록 합니다. 여기에는 네이티브 C++ 프로퍼티, 함수 파라미터, 그리고 네이티브 C++ 델리게이트에 바인딩된 블루프린트 함수의 파라미터가 포함됩니다.

플로트 값으로 예상되는 UFUNCTION 프로퍼티 지정자 노출하기

UFUNCTION 프로퍼티 지정자로 표시되고 플로트 데이터 값을 포함한 모든 메서드의 경우 낮은 정밀도 플로트로 블루프린트 플로트 값이 형변환되므로 부정확할 위험이 있습니다. 기존 UFUNCTION 프로퍼티는 모두 검사해야 합니다. 이는 이후 정밀도 문제를 방지하기 위해 파라미터 또는 반환 값을 더블로 전환하는 게 필요한지 여부를 결정하는 데 도움이 됩니다. 플로트 및 더블 타입 간의 전환은 언제든, 어느 방향으로든 안전합니다.

이는 코드로 생성하거나 노출한 모든 K2 노드에 적용됩니다.

플러그인 - 퍼블리싱된 플러그인 작성자를 위한 가이드라인

기존 플러그인을 대규모 월드를 지원하도록 변환할 경우 플러그인에 정밀도 손실이 발생하지 않도록 주의하세요. 정밀도 문제 섹션에 나열된 일반 가이드라인을 사용하여 플러그인 코어 타입 컴포넌트를 업데이트할 것을 권장합니다.

풀 월드 스페이스 플러그인

특정 플러그인은 풀 월드 스페이스로의 변환이 필요할 수 있습니다. 이는 내재된 타입이 더블을 사용하도록 변환하면 됩니다. 플러그인을 UE5로 변환할 경우 엔진이 이 프로세스의 대부분을 수용하기는 하지만, 결과를 플로트로 저장하여 코드가 정밀도 오류를 생성하지 않도록 해야 합니다.

예시: UE4에서 월드 좌표를 나타내는 데 사용된 FVectors는 UE5에서 이미 더블 정밀도 타입을 사용하도록 업그레이드되었습니다. 그러나 플러그인이 컴포넌트 레벨에서 코어 타입에 액세스할 경우 정밀도가 유지되도록 코드를 일부 리팩터링해야 할 수 있습니다.

월드 스페이스 원점 전용 플러그인

일부 플러그인은 로컬 플로트 스케일 스페이스 내에서만 작동해야 하며, 월드 스페이스 원점에서 결과를 렌더링하는 데 엔진을 사용할 수 있습니다. 이 경우 플러그인 내부에서 사용하는 코어 타입을 플로트 베리언트로 변환하여 더블에 의해 손실된 메모리를 되찾는 방법이 있습니다.

UE4 및 UE5 모두 지원

두 엔진 버전을 모두 지원하는 플러그인은 소스에서 컴파일되는 코드로 배포하거나, 별도의 바이너리 패키지로 배포해야 합니다. UE4는 코어 타입 베리언트를 지원하지 않으므로 UE5 호환 코드가 디폴트 코어 타입만 사용하도록 하거나(UE4에서는 플로트로, UE5에서는 더블로 컴파일) UE4 및 UE5 버전 플러그인용 별도 소스 코드를 제공해야 합니다.

코어 타입 컴포넌트를 사용하는 중간 계산의 올바른 정밀도를 위해 자동 C++ 선언 또는 더블을 사용하여 계산을 처리할 수 있습니다.

LWC 렌더링 개요

대규모 월드 좌표와 함께 도입된 새로운 HLSL 타입은 LargeWorldCoordinates.ush 파일에서 찾을 수 있습니다. 셰이더 코드를 UE5로 변환하는 방법에 대한 자세한 정보는 LWC 렌더링 문서를 참조하세요.

태그