添加摄像机捕捉的统一处理
This commit is contained in:
parent
f682b3b460
commit
c8aa3652ff
Binary file not shown.
@ -1,21 +1,14 @@
|
|||||||
#include "CameraCaptureActor.h"
|
#include "CameraCaptureActor.h"
|
||||||
|
|
||||||
#include "Components/SceneCaptureComponent2D.h"
|
#include "Components/SceneCaptureComponent2D.h"
|
||||||
#include "Camera/CameraComponent.h"
|
#include "Camera/CameraComponent.h"
|
||||||
#include "Engine/TextureRenderTarget2D.h"
|
#include "Engine/TextureRenderTarget2D.h"
|
||||||
#include "Engine/World.h"
|
|
||||||
#include "HAL/PlatformFilemanager.h"
|
|
||||||
#include "Misc/Paths.h"
|
|
||||||
#include "Misc/FileHelper.h"
|
|
||||||
|
|
||||||
#include "IImageWrapper.h"
|
|
||||||
#include "IImageWrapperModule.h"
|
|
||||||
#include "Modules/ModuleManager.h"
|
|
||||||
#include "Async/Async.h"
|
|
||||||
|
|
||||||
|
|
||||||
ACameraCaptureActor::ACameraCaptureActor()
|
ACameraCaptureActor::ACameraCaptureActor()
|
||||||
{
|
{
|
||||||
PrimaryActorTick.bCanEverTick = true;
|
// PrimaryActorTick.bCanEverTick = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ACameraCaptureActor::BeginPlay()
|
void ACameraCaptureActor::BeginPlay()
|
||||||
@ -24,21 +17,6 @@ void ACameraCaptureActor::BeginPlay()
|
|||||||
BuildViewCaptures();
|
BuildViewCaptures();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ACameraCaptureActor::Tick(float DeltaSeconds)
|
|
||||||
{
|
|
||||||
Super::Tick(DeltaSeconds);
|
|
||||||
|
|
||||||
if (!bCaptureEveryFrame) return;
|
|
||||||
|
|
||||||
UpdateViewTransforms();
|
|
||||||
CaptureViews();
|
|
||||||
|
|
||||||
if (bGenerateAtlas)
|
|
||||||
{
|
|
||||||
GenerateAtlas();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if WITH_EDITOR
|
#if WITH_EDITOR
|
||||||
void ACameraCaptureActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
void ACameraCaptureActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||||
{
|
{
|
||||||
@ -51,59 +29,44 @@ void ACameraCaptureActor::PostEditChangeProperty(FPropertyChangedEvent& Property
|
|||||||
|
|
||||||
void ACameraCaptureActor::BuildViewCaptures()
|
void ACameraCaptureActor::BuildViewCaptures()
|
||||||
{
|
{
|
||||||
// Destroy old components
|
if (ViewCapture)
|
||||||
for (USceneCaptureComponent2D* Comp : ViewCaptures)
|
|
||||||
{
|
{
|
||||||
if (Comp)
|
ViewCapture->DestroyComponent();
|
||||||
{
|
ViewCapture = nullptr;
|
||||||
Comp->DestroyComponent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ViewCaptures.Empty();
|
|
||||||
ViewRTs.Empty();
|
|
||||||
AtlasRT = nullptr;
|
|
||||||
|
|
||||||
if (ViewCount < 1) ViewCount = 1;
|
|
||||||
if (ViewWidth < 16) ViewWidth = 16;
|
|
||||||
if (ViewHeight < 16) ViewHeight = 16;
|
|
||||||
|
|
||||||
// Recreate view captures
|
|
||||||
for (int32 i = 0; i < ViewCount; ++i)
|
|
||||||
{
|
|
||||||
UTextureRenderTarget2D* RT = NewObject<UTextureRenderTarget2D>(this);
|
|
||||||
RT->RenderTargetFormat = RTF_RGBA8;
|
|
||||||
RT->ClearColor = FLinearColor::Black;
|
|
||||||
RT->InitAutoFormat(ViewWidth, ViewHeight);
|
|
||||||
RT->UpdateResourceImmediate(true);
|
|
||||||
|
|
||||||
USceneCaptureComponent2D* Capture = NewObject<USceneCaptureComponent2D>(this);
|
|
||||||
Capture->SetupAttachment(GetRootComponent());
|
|
||||||
Capture->RegisterComponent();
|
|
||||||
|
|
||||||
Capture->TextureTarget = RT;
|
|
||||||
Capture->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
|
|
||||||
Capture->bCaptureEveryFrame = false; // 手动触发
|
|
||||||
Capture->bCaptureOnMovement = false;
|
|
||||||
|
|
||||||
ViewCaptures.Add(Capture);
|
|
||||||
ViewRTs.Add(RT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atlas RT (optional)
|
if (ViewRT)
|
||||||
if (bGenerateAtlas)
|
|
||||||
{
|
{
|
||||||
if (AtlasGridX < 1) AtlasGridX = 1;
|
ViewRT = nullptr;
|
||||||
if (AtlasGridY < 1) AtlasGridY = 1;
|
|
||||||
|
|
||||||
AtlasRT = NewObject<UTextureRenderTarget2D>(this);
|
|
||||||
AtlasRT->RenderTargetFormat = RTF_RGBA8;
|
|
||||||
AtlasRT->ClearColor = FLinearColor::Black;
|
|
||||||
AtlasRT->InitAutoFormat(ViewWidth * AtlasGridX, ViewHeight * AtlasGridY);
|
|
||||||
AtlasRT->UpdateResourceImmediate(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ViewRT = NewObject<UTextureRenderTarget2D>(this);
|
||||||
|
ViewRT->ClearColor = FLinearColor::Black;
|
||||||
|
ViewRT->InitAutoFormat(ViewWidth, ViewHeight);
|
||||||
|
ViewRT->UpdateResourceImmediate(true);
|
||||||
|
|
||||||
|
ViewCapture = NewObject<USceneCaptureComponent2D>(this, TEXT("ViewCapture"));
|
||||||
|
ViewCapture->SetupAttachment(GetRootComponent());
|
||||||
|
ViewCapture->RegisterComponent();
|
||||||
|
|
||||||
|
ViewCapture->TextureTarget = ViewRT;
|
||||||
|
ViewCapture->CaptureSource = ESceneCaptureSource::SCS_FinalColorHDR;
|
||||||
|
ViewCapture->bCaptureEveryFrame = false;
|
||||||
|
ViewCapture->bCaptureOnMovement = false;
|
||||||
|
|
||||||
|
ViewCapture->PostProcessBlendWeight = 1.0f;
|
||||||
|
|
||||||
|
ViewCapture->PostProcessSettings.bOverride_AutoExposureMethod = true;
|
||||||
|
ViewCapture->PostProcessSettings.AutoExposureMethod = EAutoExposureMethod::AEM_Manual;
|
||||||
|
|
||||||
|
ViewCapture->PostProcessSettings.bOverride_AutoExposureBias = true;
|
||||||
|
ViewCapture->PostProcessSettings.AutoExposureBias = 1.0f;
|
||||||
|
|
||||||
|
ViewCapture->SetRelativeLocation(FVector::ZeroVector);
|
||||||
|
ViewCapture->SetRelativeRotation(FRotator::ZeroRotator);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ACameraCaptureActor::UpdateViewTransforms()
|
void ACameraCaptureActor::UpdateViewTransforms(const int32 Index)
|
||||||
{
|
{
|
||||||
// 以本 Actor(CameraActor)的 transform 为中心
|
// 以本 Actor(CameraActor)的 transform 为中心
|
||||||
const FVector Center = GetActorLocation();
|
const FVector Center = GetActorLocation();
|
||||||
@ -117,129 +80,18 @@ void ACameraCaptureActor::UpdateViewTransforms()
|
|||||||
FOV = Cam->FieldOfView;
|
FOV = Cam->FieldOfView;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32 i = 0; i < ViewCaptures.Num(); ++i)
|
if (ViewCapture)
|
||||||
{
|
{
|
||||||
if (!ViewCaptures[i]) continue;
|
|
||||||
|
|
||||||
// 线性水平分布:居中对称
|
// 线性水平分布:居中对称
|
||||||
const float Offset = (i - (ViewCount - 1) * 0.5f) * Baseline;
|
const float Offset = (Index - (ViewCount - 1) * 0.5f) * Baseline;
|
||||||
const FVector Pos = Center + Right * Offset;
|
const FVector Pos = Center + Right * Offset;
|
||||||
|
|
||||||
ViewCaptures[i]->SetWorldLocationAndRotation(Pos, Rot);
|
ViewCapture->SetWorldLocationAndRotation(Pos, Rot);
|
||||||
ViewCaptures[i]->FOVAngle = FOV;
|
ViewCapture->FOVAngle = FOV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ACameraCaptureActor::CaptureViews()
|
void ACameraCaptureActor::CaptureViews()
|
||||||
{
|
{
|
||||||
for (USceneCaptureComponent2D* Capture : ViewCaptures)
|
ViewCapture->CaptureScene();
|
||||||
{
|
|
||||||
if (Capture)
|
|
||||||
{
|
|
||||||
Capture->CaptureScene();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FlipVertical(TArray<FColor>& Pixels, int32 W, int32 H)
|
|
||||||
{
|
|
||||||
for (int32 Row = 0; Row < H / 2; ++Row)
|
|
||||||
{
|
|
||||||
const int32 A = Row * W;
|
|
||||||
const int32 B = (H - 1 - Row) * W;
|
|
||||||
for (int32 Col = 0; Col < W; ++Col)
|
|
||||||
{
|
|
||||||
Swap(Pixels[A + Col], Pixels[B + Col]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ACameraCaptureActor::GenerateAtlas()
|
|
||||||
{
|
|
||||||
if (!AtlasRT) return;
|
|
||||||
|
|
||||||
// 这里做一个“可编译的基础版”:CPU 拼合 AtlasPixels,并(可选)保存到磁盘
|
|
||||||
// 注意:把像素写回 AtlasRT 需要更底层的 RHI/渲染线程操作;高性能版我建议改成 GPU Blit。
|
|
||||||
const int32 AtlasW = AtlasRT->SizeX;
|
|
||||||
const int32 AtlasH = AtlasRT->SizeY;
|
|
||||||
|
|
||||||
TArray<FColor> AtlasPixels;
|
|
||||||
AtlasPixels.SetNumZeroed(AtlasW * AtlasH);
|
|
||||||
|
|
||||||
const int32 MaxViews = FMath::Min(ViewCount, AtlasGridX * AtlasGridY);
|
|
||||||
|
|
||||||
for (int32 i = 0; i < MaxViews; ++i)
|
|
||||||
{
|
|
||||||
UTextureRenderTarget2D* RT = ViewRTs.IsValidIndex(i) ? ViewRTs[i] : nullptr;
|
|
||||||
if (!RT) continue;
|
|
||||||
|
|
||||||
FTextureRenderTargetResource* Res = RT->GameThread_GetRenderTargetResource();
|
|
||||||
if (!Res) continue;
|
|
||||||
|
|
||||||
TArray<FColor> Pixels;
|
|
||||||
Pixels.SetNumUninitialized(ViewWidth * ViewHeight);
|
|
||||||
|
|
||||||
FReadSurfaceDataFlags ReadFlags(RCM_UNorm);
|
|
||||||
ReadFlags.SetLinearToGamma(true);
|
|
||||||
|
|
||||||
if (!Res->ReadPixels(Pixels, ReadFlags)) continue;
|
|
||||||
FlipVertical(Pixels, ViewWidth, ViewHeight);
|
|
||||||
|
|
||||||
const int32 GX = i % AtlasGridX;
|
|
||||||
const int32 GY = i / AtlasGridX;
|
|
||||||
|
|
||||||
const int32 DstX0 = GX * ViewWidth;
|
|
||||||
const int32 DstY0 = GY * ViewHeight;
|
|
||||||
|
|
||||||
for (int32 y = 0; y < ViewHeight; ++y)
|
|
||||||
{
|
|
||||||
const int32 DstY = DstY0 + y;
|
|
||||||
if (DstY < 0 || DstY >= AtlasH) continue;
|
|
||||||
|
|
||||||
const int32 SrcRow = y * ViewWidth;
|
|
||||||
const int32 DstRow = DstY * AtlasW;
|
|
||||||
|
|
||||||
for (int32 x = 0; x < ViewWidth; ++x)
|
|
||||||
{
|
|
||||||
const int32 DstX = DstX0 + x;
|
|
||||||
if (DstX < 0 || DstX >= AtlasW) continue;
|
|
||||||
|
|
||||||
AtlasPixels[DstRow + DstX] = Pixels[SrcRow + x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ 如果你需要“输出到目录”,这里就直接把 AtlasPixels 编码写盘
|
|
||||||
// (你也可以加开关:bDumpAtlasToDisk)
|
|
||||||
const FString OutDir = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("CameraCapture"));
|
|
||||||
IPlatformFile& PF = FPlatformFileManager::Get().GetPlatformFile();
|
|
||||||
if (!PF.DirectoryExists(*OutDir)) PF.CreateDirectoryTree(*OutDir);
|
|
||||||
|
|
||||||
const FString FullPath = FPaths::Combine(
|
|
||||||
OutDir,
|
|
||||||
FString::Printf(TEXT("atlas_%010lld.png"), (int64)GFrameCounter)
|
|
||||||
);
|
|
||||||
|
|
||||||
auto EncodeAndWrite = [AtlasPixels = MoveTemp(AtlasPixels), AtlasW, AtlasH, FullPath]()
|
|
||||||
{
|
|
||||||
IImageWrapperModule& ImageWrapperModule =
|
|
||||||
FModuleManager::LoadModuleChecked<IImageWrapperModule>(TEXT("ImageWrapper"));
|
|
||||||
|
|
||||||
TSharedPtr<IImageWrapper> Wrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
|
|
||||||
if (!Wrapper.IsValid()) return;
|
|
||||||
|
|
||||||
if (!Wrapper->SetRaw(AtlasPixels.GetData(), AtlasPixels.Num() * sizeof(FColor),
|
|
||||||
AtlasW, AtlasH, ERGBFormat::BGRA, 8))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TArray64<uint8>& Compressed = Wrapper->GetCompressed();
|
|
||||||
TArray<uint8> Bytes;
|
|
||||||
Bytes.Append(Compressed.GetData(), (int32)Compressed.Num());
|
|
||||||
FFileHelper::SaveArrayToFile(Bytes, *FullPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 基础版默认异步写盘,避免卡
|
|
||||||
Async(EAsyncExecution::ThreadPool, MoveTemp(EncodeAndWrite));
|
|
||||||
}
|
}
|
||||||
@ -4,6 +4,17 @@
|
|||||||
#include "Camera/CameraActor.h"
|
#include "Camera/CameraActor.h"
|
||||||
#include "CameraCaptureActor.generated.h"
|
#include "CameraCaptureActor.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define CAMERA_WIDTH_NUMS 3
|
||||||
|
#define CAMERA_HEIGHTH_NUMS 3
|
||||||
|
|
||||||
|
const int32 AtlasGridX = 3;
|
||||||
|
const int32 AtlasGridY = 3;
|
||||||
|
const int32 ViewWidth = 1024;
|
||||||
|
const int32 ViewHeight = 1024;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UENUM(BlueprintType)
|
UENUM(BlueprintType)
|
||||||
enum class EViewDistribution : uint8
|
enum class EViewDistribution : uint8
|
||||||
{
|
{
|
||||||
@ -11,6 +22,12 @@ enum class EViewDistribution : uint8
|
|||||||
Circular UMETA(DisplayName = "Circular")
|
Circular UMETA(DisplayName = "Circular")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class USceneCaptureComponent2D;
|
||||||
|
class UTextureRenderTarget2D;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class CAMERACAPTURE_API ACameraCaptureActor : public ACameraActor
|
class CAMERACAPTURE_API ACameraCaptureActor : public ACameraActor
|
||||||
{
|
{
|
||||||
@ -19,6 +36,10 @@ class CAMERACAPTURE_API ACameraCaptureActor : public ACameraActor
|
|||||||
public:
|
public:
|
||||||
ACameraCaptureActor();
|
ACameraCaptureActor();
|
||||||
|
|
||||||
|
USceneCaptureComponent2D* GetViewCapture() { return ViewCapture; }
|
||||||
|
UTextureRenderTarget2D* GetViewRT() { return ViewRT; }
|
||||||
|
|
||||||
|
|
||||||
// ===== 多视图参数 =====
|
// ===== 多视图参数 =====
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiView")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiView")
|
||||||
@ -30,28 +51,11 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiView")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiView")
|
||||||
EViewDistribution Distribution = EViewDistribution::LinearHorizontal;
|
EViewDistribution Distribution = EViewDistribution::LinearHorizontal;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiView")
|
|
||||||
int32 ViewWidth = 1024;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiView")
|
|
||||||
int32 ViewHeight = 1024;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiView")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiView")
|
||||||
bool bCaptureEveryFrame = true;
|
bool bCaptureEveryFrame = true;
|
||||||
|
|
||||||
// Atlas
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Atlas")
|
|
||||||
bool bGenerateAtlas = true;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Atlas")
|
|
||||||
int32 AtlasGridX = 3;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Atlas")
|
|
||||||
int32 AtlasGridY = 3;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void BeginPlay() override;
|
virtual void BeginPlay() override;
|
||||||
virtual void Tick(float DeltaSeconds) override;
|
|
||||||
|
|
||||||
#if WITH_EDITOR
|
#if WITH_EDITOR
|
||||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||||
@ -62,18 +66,15 @@ protected:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
void BuildViewCaptures();
|
void BuildViewCaptures();
|
||||||
void UpdateViewTransforms();
|
void UpdateViewTransforms(const int32 Index);
|
||||||
void CaptureViews();
|
void CaptureViews();
|
||||||
void GenerateAtlas();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
UPROPERTY(Transient)
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
|
||||||
TArray<class USceneCaptureComponent2D*> ViewCaptures;
|
TObjectPtr<USceneCaptureComponent2D> ViewCapture;
|
||||||
|
|
||||||
UPROPERTY(Transient)
|
UPROPERTY(Transient)
|
||||||
TArray<class UTextureRenderTarget2D*> ViewRTs;
|
TObjectPtr<UTextureRenderTarget2D> ViewRT;
|
||||||
|
|
||||||
UPROPERTY(Transient)
|
|
||||||
class UTextureRenderTarget2D* AtlasRT;
|
|
||||||
};
|
};
|
||||||
@ -4,15 +4,17 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Subsystems/WorldSubsystem.h"
|
#include "Subsystems/WorldSubsystem.h"
|
||||||
|
#include "Tickable.h"
|
||||||
#include "CameraCaptureSubSystem.generated.h"
|
#include "CameraCaptureSubSystem.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ACameraCaptureActor;
|
class ACameraCaptureActor;
|
||||||
|
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class CAMERACAPTURE_API UCameraCaptureSubSystem : public UWorldSubsystem
|
class CAMERACAPTURE_API UCameraCaptureSubSystem : public UWorldSubsystem, public FTickableGameObject
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
@ -22,10 +24,25 @@ public:
|
|||||||
virtual void Deinitialize() override;
|
virtual void Deinitialize() override;
|
||||||
virtual void OnWorldBeginPlay(UWorld& InWorld) override;
|
virtual void OnWorldBeginPlay(UWorld& InWorld) override;
|
||||||
|
|
||||||
|
virtual void Tick(float DeltaTime) override;
|
||||||
|
|
||||||
|
virtual TStatId GetStatId() const override;
|
||||||
|
|
||||||
|
virtual bool IsTickable() const override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void CaptureViewToImageTick();
|
||||||
|
|
||||||
|
void AsyncWriteImageToDisk(TArray<FColor>& AtlasPixels, const int32 AtlasW, const int32 AtlasH);
|
||||||
|
|
||||||
TArray<TWeakObjectPtr<ACameraCaptureActor>> AllCaptureCameras;
|
TArray<TWeakObjectPtr<ACameraCaptureActor>> AllCaptureCameras;
|
||||||
|
|
||||||
|
// 输出的目录
|
||||||
|
UPROPERTY()
|
||||||
|
FString OutPutDirectoryPath;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,13 +2,25 @@
|
|||||||
|
|
||||||
#include "CameraCaptureSubSystem.h"
|
#include "CameraCaptureSubSystem.h"
|
||||||
#include "CameraCaptureActor.h"
|
#include "CameraCaptureActor.h"
|
||||||
|
#include "Components/SceneCaptureComponent2D.h"
|
||||||
|
#include "Camera/CameraComponent.h"
|
||||||
|
|
||||||
#include "EngineUtils.h"
|
#include "EngineUtils.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
|
#include "Engine/TextureRenderTarget2D.h"
|
||||||
|
#include "IImageWrapper.h"
|
||||||
|
#include "IImageWrapperModule.h"
|
||||||
|
#include "HAL/PlatformFilemanager.h"
|
||||||
|
#include "Misc/Paths.h"
|
||||||
|
#include "Misc/FileHelper.h"
|
||||||
|
#include "Modules/ModuleManager.h"
|
||||||
|
#include "Async/Async.h"
|
||||||
|
|
||||||
void UCameraCaptureSubSystem::Initialize(FSubsystemCollectionBase& Collection)
|
void UCameraCaptureSubSystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||||
{
|
{
|
||||||
Super::Initialize(Collection);
|
Super::Initialize(Collection);
|
||||||
|
|
||||||
|
OutPutDirectoryPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("CameraCapture"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UCameraCaptureSubSystem::Deinitialize()
|
void UCameraCaptureSubSystem::Deinitialize()
|
||||||
@ -30,4 +42,129 @@ void UCameraCaptureSubSystem::OnWorldBeginPlay(UWorld& InWorld)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (AllCaptureCameras.Num() != CAMERA_WIDTH_NUMS * CAMERA_HEIGHTH_NUMS)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FlipVertical(TArray<FColor>& Pixels, int32 W, int32 H)
|
||||||
|
{
|
||||||
|
for (int32 Row = 0; Row < H / 2; ++Row)
|
||||||
|
{
|
||||||
|
const int32 A = Row * W;
|
||||||
|
const int32 B = (H - 1 - Row) * W;
|
||||||
|
for (int32 Col = 0; Col < W; ++Col)
|
||||||
|
{
|
||||||
|
Swap(Pixels[A + Col], Pixels[B + Col]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UCameraCaptureSubSystem::Tick(float DeltaTime)
|
||||||
|
{
|
||||||
|
CaptureViewToImageTick();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TStatId UCameraCaptureSubSystem::GetStatId() const
|
||||||
|
{
|
||||||
|
RETURN_QUICK_DECLARE_CYCLE_STAT(UCameraCaptureSubSystem, STATGROUP_Tickables);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UCameraCaptureSubSystem::CaptureViewToImageTick()
|
||||||
|
{
|
||||||
|
if (AllCaptureCameras.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FColor> AtlasPixels;
|
||||||
|
const int32 AtlasW = ViewWidth * AtlasGridX;
|
||||||
|
const int32 AtlasH = ViewHeight * AtlasGridY;
|
||||||
|
AtlasPixels.SetNumZeroed(AtlasW * AtlasH);
|
||||||
|
|
||||||
|
for (int32 CameraIndex = 0; CameraIndex < AllCaptureCameras.Num(); CameraIndex++)
|
||||||
|
{
|
||||||
|
if (!AllCaptureCameras[CameraIndex].IsValid())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTextureRenderTarget2D* RenderTarget = AllCaptureCameras[CameraIndex]->GetViewRT();
|
||||||
|
if (!RenderTarget)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FTextureRenderTargetResource* RenderTargetRes = RenderTarget->GameThread_GetRenderTargetResource();
|
||||||
|
if (!RenderTargetRes)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FColor> Pixels;
|
||||||
|
Pixels.SetNumUninitialized(ViewWidth * ViewHeight);
|
||||||
|
|
||||||
|
FReadSurfaceDataFlags ReadFlags(RCM_UNorm);
|
||||||
|
ReadFlags.SetLinearToGamma(true);
|
||||||
|
|
||||||
|
if (!RenderTargetRes->ReadPixels(Pixels, ReadFlags)) continue;
|
||||||
|
FlipVertical(Pixels, ViewWidth, ViewHeight);
|
||||||
|
|
||||||
|
const int32 GX = CameraIndex % AtlasGridX;
|
||||||
|
const int32 GY = CameraIndex / AtlasGridX;
|
||||||
|
|
||||||
|
const int32 DstX0 = GX * ViewWidth;
|
||||||
|
const int32 DstY0 = GY * ViewHeight;
|
||||||
|
|
||||||
|
for (int32 y = 0; y < ViewHeight; ++y)
|
||||||
|
{
|
||||||
|
const int32 DstY = DstY0 + y;
|
||||||
|
if (DstY < 0 || DstY >= AtlasH) continue;
|
||||||
|
|
||||||
|
const int32 SrcRow = y * ViewWidth;
|
||||||
|
const int32 DstRow = DstY * AtlasW;
|
||||||
|
|
||||||
|
for (int32 x = 0; x < ViewWidth; ++x)
|
||||||
|
{
|
||||||
|
const int32 DstX = DstX0 + x;
|
||||||
|
if (DstX < 0 || DstX >= AtlasW) continue;
|
||||||
|
|
||||||
|
AtlasPixels[DstRow + DstX] = Pixels[SrcRow + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWriteImageToDisk(AtlasPixels, AtlasW, AtlasH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UCameraCaptureSubSystem::AsyncWriteImageToDisk(TArray<FColor>& AtlasPixels, const int32 AtlasW, const int32 AtlasH)
|
||||||
|
{
|
||||||
|
const FString FullPath = FPaths::Combine( OutPutDirectoryPath, FString::Printf(TEXT("atlas_%010lld.png"), (int64)GFrameCounter) );
|
||||||
|
|
||||||
|
auto EncodeAndWrite = [AtlasPixels = MoveTemp(AtlasPixels), AtlasW, AtlasH, FullPath]()
|
||||||
|
{
|
||||||
|
IImageWrapperModule& ImageWrapperModule =
|
||||||
|
FModuleManager::LoadModuleChecked<IImageWrapperModule>(TEXT("ImageWrapper"));
|
||||||
|
|
||||||
|
TSharedPtr<IImageWrapper> Wrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
|
||||||
|
if (!Wrapper.IsValid()) return;
|
||||||
|
|
||||||
|
if (!Wrapper->SetRaw(AtlasPixels.GetData(), AtlasPixels.Num() * sizeof(FColor),
|
||||||
|
AtlasW, AtlasH, ERGBFormat::BGRA, 8))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TArray64<uint8>& Compressed = Wrapper->GetCompressed();
|
||||||
|
TArray<uint8> Bytes;
|
||||||
|
Bytes.Append(Compressed.GetData(), (int32)Compressed.Num());
|
||||||
|
FFileHelper::SaveArrayToFile(Bytes, *FullPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 基础版默认异步写盘,避免卡
|
||||||
|
Async(EAsyncExecution::ThreadPool, MoveTemp(EncodeAndWrite));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user