- 목차
각 상황에 대해 테스트가 필요함
Test version : UE 5.3.2
Texture2D → TArray<FColor>
(런타임에서 잘 실행되지 않는 것 같음...)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | // Header UFUNCTION(BlueprintCallable) static bool Texture2d_To_ColorArray(UTexture2D* SrcTexture2D, TArray<FColor>& ResultColorArray, int32& ResultTextureWidth, int32& ResultTextureHeight); // Cpp bool "클래스 이름"::Texture2d_To_ColorArray(UTexture2D* SrcTexture2D, TArray<FColor>& ResultColorArray, int32& ResultTextureWidth, int32& ResultTextureHeight) { if (SrcTexture2D == nullptr) { //(TEXT("Convert texture error : Texture2D is not defined")); ResultTextureWidth = 0; ResultTextureHeight = 0; ResultColorArray.Empty(); return false; } //Backup texture option const TextureCompressionSettings BackUp_CompressionSettings = SrcTexture2D->CompressionSettings; const bool BackUp_SRGB = SrcTexture2D->SRGB; //const ETextureMipLoadOptions BackUp_MipLoadOptions = SrcTexture2D->MipLoadOptions; //const TEnumAsByte BackUp_TextureGroup = SrcTexture2D->LODGroup; #if WITH_EDITOR || WITH_EDITORONLY_DATA const TextureMipGenSettings BackUp_MipGenSettings = SrcTexture2D->MipGenSettings; SrcTexture2D->MipGenSettings = TMGS_NoMipmaps; #endif //Set texture for read pixel SrcTexture2D->CompressionSettings = TC_VectorDisplacementmap; SrcTexture2D->SRGB = false; //SrcTexture2D->MipLoadOptions = ETextureMipLoadOptions::OnlyFirstMip; //SrcTexture2D->LODGroup = TEXTUREGROUP_UI; SrcTexture2D->UpdateResource(); bool IsRunSuccess; const FColor* FormattedImageData = static_cast<const FColor*>(SrcTexture2D->GetPlatformData()->Mips[0] .BulkData.Lock(LOCK_READ_ONLY)); if (FormattedImageData != nullptr) { ResultTextureWidth = SrcTexture2D->GetPlatformData()->SizeX; ResultTextureHeight = SrcTexture2D->GetPlatformData()->SizeY; //(TEXT("Texture width : %d height : %d"), ResultTextureWidth, ResultTextureHeight) const int ResultTextureLength = ResultTextureWidth * ResultTextureHeight; ResultColorArray.AddUninitialized(ResultTextureLength); FMemory::Memcpy(ResultColorArray.GetData(), FormattedImageData, sizeof(FColor) * ResultTextureLength); // for (int32 Y = 0; Y < ResultTextureHeight; Y++) // { // for (int32 X = 0; X < ResultTextureWidth; X++) // { // FColor PixelColor = FormattedImageData[Y * ResultTextureWidth + X]; // ResultColorArray.Add(PixelColor); // } // } SrcTexture2D->GetPlatformData()->Mips[0].BulkData.Unlock(); IsRunSuccess = true; } else { //(TEXT("Convert texture error : Failed to convert \"BulkData\" to \"FColor\"")); ResultTextureWidth = 0; ResultTextureHeight = 0; ResultColorArray.Empty(); IsRunSuccess = false; } //Rollback texture option #if WITH_EDITOR || WITH_EDITORONLY_DATA SrcTexture2D->MipGenSettings = BackUp_MipGenSettings; #endif SrcTexture2D->CompressionSettings = BackUp_CompressionSettings; SrcTexture2D->SRGB = BackUp_SRGB; //SrcTexture2D->MipLoadOptions = BackUp_MipLoadOptions; //SrcTexture2D->LODGroup = BackUp_TextureGroup; SrcTexture2D->UpdateResource(); return IsRunSuccess; } | cs |
주의!
일부 이미지에 대해서 작동하지 않는 경우가 존재하니 테스트 필요.
예를 들면, uasset으로 변환한 Texture2D는 동작하지 않음. 오히려 FImageUtils::ImportFileAsTexture2D 함수를 이용하여 로드한 실제 (Png) 이미지는 작동을 함.
추가 사항
FImageUtils::CreateTexture2D
FImageUtils::CreateTexture2D 함수는 에디터 전용으로 예상됨.
참고자료 링크에 의하면 Texture2D 내부에 MipGenSettings 변수를 건드리는 경우 #WITH_EDITOR 또는 #WITH_EDITORONLY_DATA 매크로로 감싸라고 되어 있다.
FImageUtils::CreateTexture2D 함수 파라미터에 들어가는 FCreateTexture2DParameters 구조체에서 MipGenSettings을 받고 있는 점도 그렇고, 실제로 Shipping으로 빌드를 하여 실행을 하면 "ConstructTexture2D not supported on console" 메시지를 띄우며 Crush가 뜬다.
참고 자료
TArray<FColor> ↔ TArray<uint8>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //Header UFUNCTION(BlueprintCallable) static TArray<FColor> ByteArray_To_Color(UPARAM(ref) TArray<uint8>& SrcByteArray); UFUNCTION(BlueprintCallable) static TArray<uint8> Color_To_ByteArray(UPARAM(ref) TArray<FColor>& SrcColorArray); //Cpp TArray<FColor> "클래스 이름"::ByteArray_To_Color(TArray<uint8>& SrcByteArray) { const int32 Length = SrcByteArray.Num(); TArray<FColor> ResultColorArray; ResultColorArray.AddUninitialized(Length / 4); FMemory::Memcpy(ResultColorArray.GetData(), SrcByteArray.GetData(), Length); return ResultColorArray; } TArray<uint8> "클래스 이름"::Color_To_ByteArray(TArray<FColor>& SrcColorArray) { const int32 Length = SrcColorArray.Num() * 4; TArray<uint8> ResultByteArray; ResultByteArray.AddUninitialized(Length); FMemory::Memcpy(ResultByteArray.GetData(), SrcColorArray.GetData(), Length); return ResultByteArray; } | cs |
주의
※ 언리얼 (blueprint) 에서는 byte array를 TArray<uint8>로 표현한다.
FColor 구조체는 알파를 포함하여 각 색상 요소를 uint8로 표현한다. 따라서 자료 크기는 4byte가 된다 (uint8 * 4, uint32). 리틀 엔디안이면 BGRA, 빅 엔디안이면 ARGB로 표현.
물론 이미지 포맷마다 나타내는 크기는 다름. (예 : FLinearColor의 경우 각 색상 요소를 float로 표현)
텍스쳐 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //Header UFUNCTION(BlueprintCallable) static UTexture2D* CreateTexture2D_BpAble(const int32 Width, const int32 Height, UPARAM(ref) TArray<FColor>& SrcColorArray); //Cpp UTexture2D* "클래스 이름"::CreateTexture2D_BpAble(const int32 Width, const int32 Height, TArray<FColor>& SrcColorArray) { const int32 SrcColorArrayNum = SrcColorArray.Num(); UTexture2D* ResultTexture = UTexture2D::CreateTransient(Width, Height); FColor* P_Pixels = static_cast<FColor*>(ResultTexture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE)); FMemory::Memcpy(P_Pixels, SrcColorArray.GetData(), sizeof(FColor) * SrcColorArrayNum); ResultTexture->GetPlatformData()->Mips[0].BulkData.Unlock(); ResultTexture->UpdateResource(); return ResultTexture; } | cs |
참고 자료
FColor
, FFloat16Color
, FLinearColor
-
FColor
-
정수 기반의 색상 클래스
-
8비트 채널(
R
,G
,B
,A
)을 가지며, 각 채널은0~255
범위의 값을 가짐. -
주로 메모리 사용이 적고, 단순한 색상 표현이 필요한 경우 사용.
-
예
FColor RedColor = FColor(255, 0, 0, 255); // 빨간색 (R=255, G=0, B=0, A=255)
-
-
FFloat16Color
-
16비트 부동소수점 기반의 색상 클래스
-
더 큰 색상 범위와 정확도를 제공하며, HDR(High Dynamic Range) 데이터나 고정밀 색상 처리를 위해 사용.
-
R
,G
,B
,A
채널이 각각FFloat16
타입을 가짐. -
예
FFloat16Color HDRColor(0.5f, 0.5f, 0.5f, 1.0f); // 중간 회색
-
-
FLinearColor
-
32비트 부동소수점 기반의 색상 클래스
-
채널 값이 0.0~1.0의 범위를 가지며, 선형 색상 공간에서 계산.
-
고정밀도 색상 표현 및 렌더링 작업에 적합.
-
FColor
와 변환 가능하며,SRGB
색상 공간과 선형 색상 공간 간의 변환에 사용. -
예
FLinearColor BlueColor = FLinearColor(0.0f, 0.0f, 1.0f, 1.0f); // 파란색
-
sRGB와 Linear Color의 차이
1. sRGB (Standard RGB)
-
감마 보정된 색상 공간
- 인간의 시각 시스템이 빛의 밝기를 비선형적으로 인식하는 점을 반영하여 설계된 색상 공간.
- 어두운 색은 더 세밀하게, 밝은 색은 덜 세밀하게 표현.
- 일반적으로 UI 작업, 이미지 파일 포맷(JPEG, PNG) 등에서 사용.
-
특징
- 각 채널 값은
0~255
범위를 가짐. - 색상이 비선형적이며, 감마 값(보통 2.2)을 적용하여 처리.
- 각 채널 값은
-
예제
FColor sRGBColor(128, 64, 255, 255); // sRGB 공간에서 표현된 색상
2. Linear Color (선형 색상)
-
선형적으로 처리된 색상 공간
- 감마 보정을 적용하지 않은 상태로, 색상 값이 빛의 물리적 강도를 직접적으로 나타냄.
- 렌더링 파이프라인에서 색상 연산(예: 블렌딩, 조명 계산)에 사용.
- 더 높은 정확도로 작업 가능.
-
특징
- 각 채널 값은 보통
0.0~1.0
범위를 가짐. - 선형적이므로 색상 연산에 왜곡이 적음.
- 각 채널 값은 보통
-
예제
FLinearColor LinearColor(0.5f, 0.25f, 1.0f, 1.0f); // 선형 색상 공간에서 표현된 값
USceneCaptureComponent2D, UTextureRenderTarget2D
USceneCaptureComponent2D의 CaptureSource와 UTextureRenderTarget2D의 ETextureRenderTargetFormat, EPixelFormat 간의 조합이 잘 맞아야한다. 여기서 이미지 저장 (예: PNG)까지 해야한다면, 저장되는 이미지의 포맷 조합 또안 맞춰야하고 데이터 추출이 잘되는지도 확인해야한다.
매칭되는 ETextureRenderTargetFormat, EPixelFormat 조합의 예시는 하단의 참고코드 부분에 있는 "GetPixelFormatFromRenderTargetFormat" 함수를 참조하면 된다.
USceneCaptureComponent2D
BP 및 C++에서 TextureTarget부분에 UTextureRenderTarget2D를 지정할 수 있다.
UTextureRenderTarget2D
FRenderTarget::ReadLinearColorPixels (UnrealClient.h 파일 내부) 함수를 호출하면 (D3D11RenderTarget.cpp 또는 D3D12RenderTarget.cpp 파일 내부의) ConvertRAWSurfaceDataToFLinearColor 함수가 호출되는 것으로 보인다. RenderTarget의 PixelFormat이 지원되지 않는 포맷이라면 ReadLinearColorPixels 함수는 제대로 된 값을 반영하지 않는다. UE5.3버전 기준으로 지원되는 EPixelFormat은 다음과 같다. (정확히는 처리 분기가 있는 녀석들)
- PF_R16F, PF_R16F_FILTER
- PF_R8G8B8A8
- PF_B8G8R8A8
- PF_A2B10G10R10
- PF_FloatRGBA
- PF_FloatRGB, PF_FloatR11G11B10
- PF_A32B32G32R32F
- PF_D24
- PF_DepthStencil
- PF_A16B16G16R16
- PF_G16R16
추천 이미지 옵션
- RGB
- USceneCaptureComponent2D
- CaptureSource : "SCS_SceneColor" 글자가 포함된 것, SCS_FinalColorLDR (Post Processing에서 처리)
- UTextureRenderTarget2D
- Format : RTF_RGBA8
- Pixel을 읽은 후, alpha 채널 값을 수정해야될 수도 있음.
- Depth :
- USceneCaptureComponent2D
- CaptureSource : SCS_SceneDepth
- UTextureRenderTarget2D
- Format : RTF_RGBA32f
- Depth 특성상 R채널에만 이미지 데이터가 저장됨
- TArray<FLinearColor>로 Pixel을 읽어 R채널만 따로 추출하면 됨 (float)
- Normal :
- USceneCaptureComponent2D
- CaptureSource : SCS_Normal
- UTextureRenderTarget2D
- Format : RTF_RGBA16f
- ClearColor 추가 추천
이미지 저장
PNG
하단의 코드는 Rama님께서 제작한 Victory Plugin에 있는 코드이다. 8bit RGB 형태로 캡쳐를 한 후, 8bit PNG 파일로 저장한다. FColor 구조체가 Endian 설정에 따라서 저장하는 순서가 다르기 때문에 PNG 저장 전에 Pixel 순서를 재조정 처리도 한다.
PNG의 경우 ImageWrapper::SetRaw 함수에서 ERGBFormat::RGBA는 지원하지만, ERGBFormat::RGBAF는 지원하지 않는다.
PNG 이미지는 무손실 압축이기 때문에 ImageWrapper::GetCompressed의 파라미터인 Quality부분에 값을 넣어도 무시된다.
댓글
댓글 쓰기