首页 > 其他分享 >UE5笔记-实现Lumen实时渲染GI下的的类UCanvasRenderTarget实现多场景/自定义分辨率/方位的渲染视口

UE5笔记-实现Lumen实时渲染GI下的的类UCanvasRenderTarget实现多场景/自定义分辨率/方位的渲染视口

时间:2024-06-21 17:43:53浏览次数:12  
标签:const 自定义 渲染 void UCaptureRenderTarget 视口 FinalPostProcessSettings SceneView inc

默认的 Scene Capture 不能用于实时Lumen光照模式下

为了实现实时渲染GI下的的类似于UCanvasRenderTarget2D类.

可以参考GameViewport类的源码尝试使用UE的渲染逻辑和数据多渲染一份视口副本到直接的FSceneView上,封装一份UCaptureRenderTarget出来

从而实现一些例如自定义分辨率的超高清截图/视频输出/全景图合成等功能(太高分辨率了,显存会爆炸就是了.慎用)

 

我已经在代码5.2环境下可跑通并上线

这份代码需要自行使用后期材质反伽马校正一下(大概猜测是UE已经应用了一次gamma矫正我这边又伽马矫正了一次导致的,暂时没找到相关的代码位置)

使用方法参考:

    FIntPoint size{1920, 1080};
    float factor = 2.0;
    UWorld *InWorld = GWorld;
    UCaptureRenderTarget *CaptureRTT = NewObject<UCaptureRenderTarget>(GWorld);
    CaptureRTT->AddToRoot();
    CaptureRTT->OnCaptureCompleted().AddLambda([&](UCaptureRenderTarget *c)
                                               {
        FString SavePath = TEXT("");
        c->SaveToDisk(SavePath);
        c->RemoveFromRoot();
        c = CaptureRTT = nullptr;

        Async(EAsyncExecution::Thread, [factor]()
        {
            // 延迟一下
            FPlatformProcess::Sleep(1.0f * factor);
            GEngine->ForceGarbageCollection();
        });
       });

    FVector location = UGameplayStatics::GetPlayerCameraManager(InWorld, 0)->GetCameraLocation();
    FRotator rot = UGameplayStatics::GetPlayerCameraManager(InWorld, 0)->GetCameraRotation();

    TArray<AActor *> arr_pp;
    UGameplayStatics::GetAllActorsOfClass(InWorld, APostProcessVolume::StaticClass(), arr_pp);
    APostProcessVolume *PP = Cast<APostProcessVolume>(arr_pp[0]); // arr_pp
    CaptureRTT->Initilize(size * factor, location, rot, 90.0f, 200.0f, false, true, PP);

 

P.S:因为我用OPENCV库快速输出webp方便调试,不需要自行删除相关代码和Module引用就好了

参考代码:

            PublicDependencyModuleNames.AddRange(new string[]
            {
                "OpenCVHelper",
                "OpenCV",
                    "Core",
                    "CoreUObject",
                    "Engine",
                    "ImageWrapper",
                    "InputCore",
                    "RenderCore",
                    "Renderer",
            });
// Copyright Epic Games, Inc. All Rights Reserved.

/*=============================================================================
    RendererPrivate.h: Renderer interface private definitions.
=============================================================================*/

#pragma once

#include "SceneView.h"


/**
 * 自定义场景百分比接口...为了更自由的控制Capture的最大最小百分比
 */
class XXX_API FCaptureScreenPercentageDriver : public ISceneViewFamilyScreenPercentage
{
public:
    FORCEINLINE FCaptureScreenPercentageDriver(
        const FSceneViewFamily& InViewFamily,
        float InGlobalResolutionFraction)
        : FCaptureScreenPercentageDriver(InViewFamily, InGlobalResolutionFraction, InGlobalResolutionFraction)
    {
    }

    FCaptureScreenPercentageDriver(
        const FSceneViewFamily& InViewFamily,
        float InGlobalResolutionFraction,
        float InGlobalResolutionFractionUpperBound);

public:
    FORCEINLINE float GetGlobalResolutionFraction() const
    {
        return GlobalResolutionFraction;
    }

    FORCEINLINE bool SetGlobalResolutionFraction(float newFraction) volatile
    {
        float newF = this->GlobalResolutionFraction = FMath::Clamp(newFraction, ISceneViewFamilyScreenPercentage::kMinResolutionFraction, ISceneViewFamilyScreenPercentage::kMaxResolutionFraction);
        if (GlobalResolutionFractionUpperBound < newF)
        {
            //防止上界小于当前
            GlobalResolutionFractionUpperBound = this->GlobalResolutionFraction;
        }
        this->GlobalResolutionFraction = newF;
        return true;
    }

private:
    const FSceneViewFamily& ViewFamily;

    volatile float GlobalResolutionFraction;
    volatile float GlobalResolutionFractionUpperBound;

#pragma region ISceneViewFamilyScreenPercentage虚函数实现
    virtual DynamicRenderScaling::TMap<float> GetResolutionFractionsUpperBound() const override;
    virtual DynamicRenderScaling::TMap<float> GetResolutionFractions_RenderThread() const override;
    virtual ISceneViewFamilyScreenPercentage* Fork_GameThread(const class FSceneViewFamily& ForkedViewFamily) const override;
#pragma endregion
};

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"

#include "ImageFormatEx.generated.h"

UENUM(BlueprintType)
enum class EImageFormatEx : uint8
{
    /** Invalid or unrecognized format. */
    Invalid = 0,

    /** Portable Network Graphics. */
    PNG = 1,

    /** Joint Photographic Experts Group. */
    JPEG,

    /** Single channel JPEG. */
    GrayscaleJPEG,

    /** Windows Bitmap. */
    BMP,

    /** Windows Icon resource. */
    ICO,

    /** OpenEXR (HDR) image file format. */
    EXR,

    /** Mac icon. */
    ICNS,

    /** Truevision TGA / TARGA */
    TGA,

    /** 扩展 */
    WebP
};
// Copyright Epic Games, Inc. All Rights Reserved.

/*=============================================================================
    Renderer.cpp: Renderer module implementation.
=============================================================================*/

#include "CaptureScreenPercentageDriver.h"
#include "UnrealEngine.h"

#include "Misc/ConfigCacheIni.h"
#include "DynamicResolutionState.h"

FCaptureScreenPercentageDriver::FCaptureScreenPercentageDriver(
    const FSceneViewFamily& InViewFamily,
    float InGlobalResolutionFraction,
    float InGlobalResolutionFractionUpperBound)
    : ViewFamily(InViewFamily)
      , GlobalResolutionFraction(InGlobalResolutionFraction)
      , GlobalResolutionFractionUpperBound(InGlobalResolutionFractionUpperBound)
{
}

DynamicRenderScaling::TMap<float> FCaptureScreenPercentageDriver::GetResolutionFractionsUpperBound() const
{
    DynamicRenderScaling::TMap<float> UpperBounds;
    UpperBounds.SetAll(1.0f);

    if (ViewFamily.EngineShowFlags.ScreenPercentage)
    {
        UpperBounds[GDynamicPrimaryResolutionFraction] = FMath::Clamp(
            GlobalResolutionFractionUpperBound,
            ISceneViewFamilyScreenPercentage::kMinResolutionFraction,
            ISceneViewFamilyScreenPercentage::kMaxResolutionFraction);
    }

    return UpperBounds;
}

ISceneViewFamilyScreenPercentage* FCaptureScreenPercentageDriver::Fork_GameThread(
    const FSceneViewFamily& ForkedViewFamily) const
{
    check(IsInGameThread());
    //return nullptr;
    return new FCaptureScreenPercentageDriver(
        ForkedViewFamily, GlobalResolutionFraction, GlobalResolutionFractionUpperBound);
}

DynamicRenderScaling::TMap<float> FCaptureScreenPercentageDriver::GetResolutionFractions_RenderThread() const
{
    check(IsInRenderingThread());

    DynamicRenderScaling::TMap<float> ResolutionFractions;
    ResolutionFractions.SetAll(1.0f);

    if (ViewFamily.EngineShowFlags.ScreenPercentage)
    {
        ResolutionFractions[GDynamicPrimaryResolutionFraction] = FMath::Clamp(
            GlobalResolutionFraction,
            ISceneViewFamilyScreenPercentage::kMinResolutionFraction,
            ISceneViewFamilyScreenPercentage::kMaxResolutionFraction);
    }

    return ResolutionFractions;
}


//void FCaptureScreenPercentageDriver::ComputePrimaryResolutionFractions_RenderThread(
//    TArray<FSceneViewScreenPercentageConfig>& OutViewScreenPercentageConfigs) const
//{
//    check(IsInRenderingThread());
//    // 如果不支持ScenePercentage功能则直接返回
//    if (!ViewFamily.EngineShowFlags.ScreenPercentage)
//    {
//        return;
//    }
//
//    for (int32 i = 0; i < ViewFamily.Views.Num(); i++)
//    {
//        float ResolutionFraction = GlobalResolutionFraction;
//        //TODO:限制大小,避免卡死..后面可根据硬件等级设置上下界
//        OutViewScreenPercentageConfigs[i].PrimaryResolutionFraction = FMath::Clamp(
//            ResolutionFraction,
//            MinResolutionFraction,
//            MaxResolutionFraction );
//    }
//}

 

/**
 * 
 * 
 */

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "DTO/ImageFormatEx.h"
#include "CaptureRenderTarget.generated.h"


class APostProcessVolume;


UENUM()
enum class ECaptureState : uint8
{
    Initilize = 0,
    Warmup,
    Renderering,
    Renderered,
    Captured,
    Finished
};

UENUM(BlueprintType)
enum class ECaptureCubeFace : uint8
{
    CubeFace_PosX = 0,
    CubeFace_NegX,
    CubeFace_PosY,
    CubeFace_NegY,
    CubeFace_PosZ,
    CubeFace_NegZ,
    CubeFace_MAX
};

/**
 * XXX:维护自己的worldTime用于Vedio
 */
UCLASS(BlueprintType)
class XXX_API UCaptureRenderTarget : public UObject, public FTickableGameObject
{
    GENERATED_UCLASS_BODY()
public:
    //UCaptureRenderTarget();
    ~UCaptureRenderTarget();

    UFUNCTION(BlueprintCallable)
    void Initilize(const FIntPoint& Size,
                   const FVector& Location = FVector::ZeroVector,
                   const FRotator& Rotator = FRotator::ZeroRotator,
                   const float FOV = 90.0f,
                   const float ScenePercentage = 100.0f,
                   const bool bInTile = false,
                   const bool bInAsync = true, //是否是异步捕获模式
                   APostProcessVolume* PP = nullptr);

    UFUNCTION(BlueprintCallable)
    void ResizeRenderTarget(const FIntPoint& Size); 

    UFUNCTION(BlueprintCallable)
    FORCEINLINE bool IsInitilized()
    {
        return GetState() != ECaptureState::Initilize;
    }
    
    UFUNCTION(BlueprintCallable)
    void SetScreenPercentage(float screenPercentage = 100.0f);

    UFUNCTION(BlueprintCallable)
        void SetWorldPause(bool NewPause);

    UFUNCTION(BlueprintCallable)
    FORCEINLINE UTextureRenderTarget2D* GetRTT2D() const
    {
        return this->mRTT2D;
    }

    UFUNCTION(BlueprintCallable)
    void Capture(const uint8 PreCaptureFrameNum = 2);

    UFUNCTION(BlueprintCallable)
    void CaptureFromLocationAndRotation(const FVector& Location = FVector::ZeroVector,
                                        const FRotator& Rotator = FRotator::ZeroRotator,
                                        const uint8 PreCaptureFrameNum = 2
    );


    //UFUNCTION(BlueprintCallable)
    void SetupCustomPassMass(FSceneView* InSceneView);

    //UFUNCTION(BlueprintCallable)
    //void AddWeightedBlendables(TArray<FWeightedBlendable>& arr);

    UFUNCTION(BlueprintCallable)
    void SaveToDisk(FString path = TEXT(""), ECaptureCubeFace CubeFace = ECaptureCubeFace::CubeFace_MAX, EImageFormatEx ImageFormat = EImageFormatEx::PNG, int Quility = 100);


    UFUNCTION(BlueprintCallable)
    void SaveToDiskAsync(FString path = TEXT(""), ECaptureCubeFace CubeFace = ECaptureCubeFace::CubeFace_MAX, EImageFormatEx ImageFormat = EImageFormatEx::PNG, int Quility = 100);

    ECaptureState GetState();
#pragma region event
public:
    DECLARE_MULTICAST_DELEGATE_OneParam(FOnCaptureCompleted, UCaptureRenderTarget*)

    FOnCaptureCompleted& OnCaptureCompleted()
    {
        return Event_OnCaptureCompleted;
    }

    FOnCaptureCompleted& OnCaptureCompleted_RenderThread()
    {
        return Event_OnCaptureCompleted_RenderThread;
    }

    //FOnCaptureCompleted& OnCaptureWarmupCompleted()
    //{
    //    return Event_OnCaptureWarmupCompleted;
    //}

    FOnCaptureCompleted& OnCaptureWarmupCompleted_RenderThread()
    {
        return Event_OnCaptureWarmupCompleted_RenderThread;
    }

    DECLARE_MULTICAST_DELEGATE_OneParam(FOnSaveToDiskFinished, const FString &)
    FOnSaveToDiskFinished& OnSaveToDiskFinished()
    {
        check(IsInGameThread());
        return Event_OnSaveToDiskFinished;
    }

    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSaveToDiskFinished_BP,const FString&, SavePath);
    UPROPERTY(BlueprintAssignable)
    FOnSaveToDiskFinished_BP OnSaveToDiskFinished_BP;
protected:
    FOnCaptureCompleted Event_OnCaptureCompleted;
    FOnCaptureCompleted Event_OnCaptureCompleted_RenderThread;
    //FOnCaptureCompleted Event_OnCaptureWarmupCompleted;
    FOnCaptureCompleted Event_OnCaptureWarmupCompleted_RenderThread;
    FOnSaveToDiskFinished Event_OnSaveToDiskFinished;
#pragma endregion event
protected:
    void InitRenderTarget(const FIntPoint& Size);
    void SetState(ECaptureState NewState);
    TSharedPtr<FSceneViewFamilyContext> SetupViewFamily();
    FSceneView* SetupSceneView(TSharedPtr<FSceneViewFamilyContext> InViewFamily);

    void UpdateSceneViewPP(FSceneView* SceneView);
    void CalculateProjectionMatrix();

    //ref: UMovieScenePipeline::FlushAsyncEngineSystems 同步等待UE完成渲染每一帧前的预处理..
    void FlushAsyncEngineSystems();
    void RenderFrame();
    void PostRendererSubmission();
    void HandleFrame_OnRenderThread();
protected:
    #pragma region tickable impl
    virtual void Tick(float DeltaTime) override;
    virtual bool IsTickable() const override;
    virtual TStatId GetStatId() const override { return TStatId(); }
    #pragma endregion tickable impl

public:
    /** Read RTT pixel Data, from FloatRGBA to R8G8B8A */
    static bool GetRawData(UTextureRenderTarget2D* RTT, TArray64<uint8>& OutRawData, ECaptureCubeFace CubeFace = ECaptureCubeFace::CubeFace_MAX);
private:
    bool IOToDisk(const TArray64<uint8> & rawData,const FString& SavePath, const FIntPoint &Size, EImageFormatEx ImageFormat, int Quility);
protected:
#pragma region View
    FMatrix mProjectionMatrix = FMatrix::Identity;
    FVector mViewLocation = FVector::ZeroVector;
    FRotator mViewRotation = FRotator::ZeroRotator;
    float mViewFOV = 90.0f;
    float mScenePercentage = 100.0f; // important 
    bool bWorldPause = false;
    //bool bFixedExposure = false;

#pragma endregion
    bool bNotInverseGamma = false;
    bool bAsync = true;

    //Only Sync Mode
    bool bStartWarmup = false;
    bool bStartRender = false;

    /**
     * 
     */
    FSceneViewStateReference SceneViewState;

    UPROPERTY()
    UTextureRenderTarget2D* mRTT2D = nullptr;

    ECaptureState CaptureState = ECaptureState::Initilize;

    //预渲染帧数
    uint8 PreRenderingFrameNum = 2;
    uint8 CurPreFrame = 0;

    UPROPERTY()
    APostProcessVolume* mPP = nullptr;

    UPROPERTY()
    class UMaterial* Mat_PPGamma;
    
    UPROPERTY()
    class UMaterialInstanceDynamic* MatInsDyn_PPGamma;
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "Pipeline/CaptureRenderTarget.h"

#include "Serialization/BufferArchive.h"
#include "CaptureScreenPercentageDriver.h"
#include "EngineModule.h"
#include "Misc/FileHelper.h"
#include "EngineUtils.h"
#include "IImageWrapper.h"
#include "IImageWrapperModule.h"
#include "ImageUtils.h"
#include "Engine/PostProcessVolume.h"
#include "Components/PostProcessComponent.h"
#include "CanvasTypes.h"
#include "LegacyScreenPercentageDriver.h"
#if WITH_EDITOR
#include "AssetCompilingManager.h"
#endif

#include "OpenColorIODisplayExtension.h"
#include "Camera/PlayerCameraManager.h"
#include "Engine/TextureRenderTarget2D.h"
#include "GameFramework/PlayerController.h"
#pragma region warmup
#include "LandscapeProxy.h"
#include "DistanceFieldAtlas.h"
// #include "MeshCardRepresentation.h"
#define IS_ARTISTRENDERER 0
#if IS_ARTISTRENDERER
#include "DistanceFieldAtlas2.h"
#include "MeshCardRepresentation2.h"
#endif
#include "ShaderCompiler.h"
#pragma endregion warmup


#if WITH_OPENCV
#include "OpenCVHelper.h"
//OPENCV_INCLUDES_START
#include "PreOpenCVHeaders.h"
//#undef check // the check macro causes problems with opencv headers
#include "opencv2/opencv.hpp"
#include "PostOpenCVHeaders.h"
//OPENCV_INCLUDES_END
#endif

#include "Async/Async.h"

#include "ScreenRendering.h"
#include <Engine/World.h>


#include "Materials/Material.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialInstanceDynamic.h"


namespace CF_CaptureRenderTarget
{
    //默认的暖帧数
    static const uint8 DefaultWarmupFrameNum = 30;
    //默认预渲染帧数,Note:最低要求2帧...防止变换时模糊问题
    static const uint8 DefaultPreRenderingNum = 2;
}


UCaptureRenderTarget::UCaptureRenderTarget(const FObjectInitializer& ObjectInitializer)
    :Super(ObjectInitializer)
{
    SceneViewState.Allocate();
    {
    }
}

UCaptureRenderTarget::~UCaptureRenderTarget()
{
    FSceneViewStateInterface* Ref = SceneViewState.GetReference();
    if (Ref)
    {
        Ref->ClearMIDPool();
    }
    SceneViewState.Destroy();

    UE_LOG(LogTemp, Log, TEXT("UCaptureRenderTarget destroyed....."));
}

void UCaptureRenderTarget::Initilize(const FIntPoint& Size,
                                     const FVector& Location,
                                     const FRotator& Rotator,
                                     const float FOV,
                                     const float ScenePercentage,
                                     const bool InbNotInverseGamma,
                                     const bool bInAsync,
                                     APostProcessVolume* PP)
{
#if WITH_EDITOR
    check(Size.Size() > 0);
    check(FOV != 0);
#endif

    if(!InbNotInverseGamma)
    {
        this->Mat_PPGamma = LoadObject<UMaterial>(this, TEXT("/PanoramicCaptureExtend/Materials/mat_inverseGammaSpace.Mat_InverseGammaSpace"));
        //this->MatInsDyn_PPGamma = UMaterialInstanceDynamic::Create(Mat_PPGamma,this);
        //this->Mat_PPGamma = UMaterialInstanceDynamic::Create(Mat, this, TEXT("InverseGamma"));
    }

    this->mViewFOV = FOV;
    this->mViewLocation = Location;
    this->mViewRotation = Rotator;
    this->mScenePercentage = ScenePercentage;
    this->bNotInverseGamma = InbNotInverseGamma;
    this->mPP = PP;
    this->bAsync = bInAsync;

    if (this->mRTT2D != nullptr)
    {
        this->mRTT2D->ReleaseResource();
        this->mRTT2D = nullptr;
    }

    const FName TargetName = MakeUniqueObjectName(this, UTextureRenderTarget2D::StaticClass(), TEXT("SceneCaptureTextureTarget"));
    this->mRTT2D = NewObject<UTextureRenderTarget2D>(this, TargetName);

    check(this->mRTT2D != nullptr);
    InitRenderTarget(Size);

    this->CalculateProjectionMatrix();

    //this->SetupViewFamily();
    //this->SetupSceneView();
    this->SetState(ECaptureState::Warmup);
}

void UCaptureRenderTarget::SetScreenPercentage(float screenPercentage)
{
    this->mScenePercentage = screenPercentage;
}

void UCaptureRenderTarget::SetWorldPause(bool NewPause)
{
    if (this->bWorldPause != NewPause)
    {
        //this->UpdateSceneViewTime();
    }
    this->bWorldPause = NewPause;
}

void UCaptureRenderTarget::InitRenderTarget(const FIntPoint& Size)
{
    //TODO: rtt2d可以换成rhiTexture在renderthread处理.
    check(this->mRTT2D != nullptr);
    this->mRTT2D->ClearColor = FLinearColor::Transparent; //FLinearColor(0.0f, 0.0f, 0.0f, 1.0f);
    this->mRTT2D->InitCustomFormat(Size.X, Size.Y, EPixelFormat::PF_FloatRGBA, true);
    this->mRTT2D->TargetGamma = FOpenColorIODisplayExtension::DefaultDisplayGamma; // default 2.2
    this->mRTT2D->bAutoGenerateMips = false;
    this->mRTT2D->Filter = TF_Default;//TF_Nearest;//TF_Trilinear;
    this->mRTT2D->AddressX = TA_Clamp;
    this->mRTT2D->AddressY = TA_Clamp;
    this->mRTT2D->MipsAddressU = TA_Clamp;
    this->mRTT2D->MipsAddressV = TA_Clamp;
    this->mRTT2D->UpdateResourceImmediate(true);
    this->mRTT2D->SRGB = true;
}

void UCaptureRenderTarget::ResizeRenderTarget(const FIntPoint& Size)
{
    this->mRTT2D->ResizeTarget(Size.X, Size.Y);
}

void UCaptureRenderTarget::SetState(ECaptureState NewState)
{
    if (this->CaptureState == NewState)
    {
        //UE_LOG(LogTemp, Error, TEXT("当前状态不应该等于新状态"));
        return;
    }

    //处理状态变换前的判断,断言确保状态从指定的状态切换过来.
    switch (NewState)
    {
        case ECaptureState::Warmup:
            {
                check(this->CaptureState == ECaptureState::Initilize);
                FlushAsyncEngineSystems();
                break;
            }
        case ECaptureState::Renderering:
            {
                check(this->CaptureState != ECaptureState::Initilize);
                break;
            }
        case ECaptureState::Renderered:
            {
                check(this->CaptureState == ECaptureState::Renderering);
                PreRenderingFrameNum = CF_CaptureRenderTarget::DefaultPreRenderingNum;
                this->bStartRender = false;
                break;
            }
        case ECaptureState::Captured:
            {
                check(this->CaptureState == ECaptureState::Renderered);
                break;
            }
        default:
            {
                break;
            }
    }

    this->CurPreFrame = 0;
    this->CaptureState = NewState;
}

TSharedPtr<FSceneViewFamilyContext> UCaptureRenderTarget::SetupViewFamily()
{
    check(GetRTT2D() != nullptr);
    check(GetWorld());

    UWorld* InWorld = GetWorld();
    FSceneInterface* Scene = InWorld->Scene;

    FTextureRenderTargetResource* RenderTarget = this->mRTT2D->GameThread_GetRenderTargetResource();
    EViewModeIndex ViewLightMode = VMI_Lit;
    FEngineShowFlags ShowFlags = GEngine->GameViewport->EngineShowFlags; ///**/FEngineShowFlags(ESFIM_Game);
    ShowFlags.SetScreenPercentage(true);
    TSharedPtr<FSceneViewFamilyContext> InViewFamily = MakeShared<FSceneViewFamilyContext>(
        (
            FSceneViewFamily::ConstructionValues(RenderTarget, Scene, ShowFlags)
        )
        .SetResolveScene(true)
        .SetWorldTimes( InWorld->GetTimeSeconds(),
                        InWorld->GetDeltaSeconds(),
                        InWorld->GetRealTimeSeconds())
        .SetRealtimeUpdate(true)
    );

#pragma region ViewFamily配置
    //mViewFamily->SceneCaptureSource = SCS_FinalToneCurveHDR; // SCS_FinalColorLDR; //SCS_FinalToneCurveHDR;//
    InViewFamily->SceneCaptureSource = SCS_FinalColorLDR;

    InViewFamily->SetScreenPercentageInterface(new FCaptureScreenPercentageDriver(
            InViewFamily.ToSharedRef().Get(),
            this->mScenePercentage / 100.00f)
    );

    InViewFamily->ViewExtensions = GEngine->ViewExtensions->GatherActiveExtensions(FSceneViewExtensionContext(Scene));
    for (auto ViewExt : InViewFamily->ViewExtensions)
    {
        ViewExt->SetupViewFamily(*InViewFamily);
    }

    EngineShowFlagOverride(ESFIM_Game, InViewFamily->ViewMode, InViewFamily->EngineShowFlags, false);

    InViewFamily->bWorldIsPaused = this->bWorldPause;
    InViewFamily->ViewMode = ViewLightMode;
    InViewFamily->GammaCorrection = 2.2f;
    InViewFamily->EngineShowFlags = ShowFlags;
    //InViewFamily->EngineShowFlags.SetScreenPercentage(true);
    // Force enable view family show flag for HighDPI derived's screen percentage.
    InViewFamily->EngineShowFlags.ScreenPercentage = this->mScenePercentage;
    InViewFamily->EngineShowFlags.SetMotionBlur(false);
    InViewFamily->bIsHDR = false;
    InViewFamily->bRequireMultiView = false;

#pragma endregion viewfamily配置
    return InViewFamily;
}

FSceneView* UCaptureRenderTarget::SetupSceneView(TSharedPtr<FSceneViewFamilyContext> InViewFamily)
{
    FIntRect Rect(0, 0, GetRTT2D()->SizeX, GetRTT2D()->SizeY);
    EAntiAliasingMethod AAMethod = EAntiAliasingMethod::AAM_TemporalAA;

    FSceneViewInitOptions opt;
    opt.bUseFieldOfViewForLOD = true;
    opt.BackgroundColor = FColor::Transparent; //FColor::Black;
    opt.FOV = opt.DesiredFOV = this->mViewFOV;

    opt.SetViewRectangle(Rect);
    opt.ViewFamily = InViewFamily.Get();
    //计算默认视口位置
    {
        opt.ViewOrigin = this->mViewLocation;
        //refer FSceneView::UpdateViewMatrix
        FMatrix mat = FMatrix(FPlane(0, 0, 1, 0),
                              FPlane(1, 0, 0, 0),
                              FPlane(0, 1, 0, 0),
                              FPlane(0, 0, 0, 1));
        opt.ViewRotationMatrix = FInverseRotationMatrix(this->mViewRotation) * mat;
        opt.ProjectionMatrix = this->mProjectionMatrix;
    }

    opt.SceneViewStateInterface = SceneViewState.GetReference();

    FSceneView* SceneView = new FSceneView(opt);
    InViewFamily->Views.Add(SceneView);

#pragma region SceneView配置
//#if RHI_RAYTRACING
//    SceneView->SetupRayTracedRendering();
//#endif
    SceneView->bCameraCut = false;
    SceneView->bIsOfflineRender = true;
    SceneView->bIsGameView = true;
    SceneView->bIsSceneCapture = false;
    SceneView->bIsMultiViewEnabled = false;
    SceneView->AntiAliasingMethod = AAMethod;
    SceneView->SetupAntiAliasingMethod();

    // process setting
    {
        SceneView->StartFinalPostprocessSettings(opt.ViewOrigin);
        {
            this->UpdateSceneViewPP(SceneView);

            SetupCustomPassMass(SceneView);
        }
        SceneView->EndFinalPostprocessSettings(opt);
    }

#pragma endregion SceneView配置
    return SceneView;
}

void UCaptureRenderTarget::UpdateSceneViewPP(FSceneView* SceneView)
{
    //SceneView->FinalPostProcessSettings.ScreenPercentage_DEPRECATED
    SceneView->FinalPostProcessSettings.ScreenPercentage_DEPRECATED = this->mScenePercentage;
    if (this->mPP)
    {
        const FPostProcessSettings& setting = this->mPP->Settings;
        //TODO:可改用TPropertyIterator反射迭代赋值

        SceneView->FinalPostProcessSettings.ColorGammaHighlights = setting.ColorGammaHighlights;
        SceneView->FinalPostProcessSettings.ColorGainMidtones = setting.ColorGainMidtones;
        SceneView->FinalPostProcessSettings.ColorGammaShadows = setting.ColorGammaShadows;
        SceneView->FinalPostProcessSettings.ColorGamma = setting.ColorGamma;

        SceneView->FinalPostProcessSettings.ColorGainHighlights = setting.ColorGainHighlights;
        SceneView->FinalPostProcessSettings.ColorGainMidtones = setting.ColorGainMidtones;
        SceneView->FinalPostProcessSettings.ColorGainShadows = setting.ColorGainShadows;
        SceneView->FinalPostProcessSettings.ColorGain = setting.ColorGain;


        SceneView->FinalPostProcessSettings.BloomMethod = setting.BloomMethod;
        SceneView->FinalPostProcessSettings.BloomIntensity = setting.BloomIntensity;
        SceneView->FinalPostProcessSettings.BloomThreshold = setting.BloomThreshold;

        SceneView->FinalPostProcessSettings.WhiteTemp = setting.WhiteTemp;
        SceneView->FinalPostProcessSettings.WhiteTint = setting.WhiteTint;
        SceneView->FinalPostProcessSettings.AutoExposureMethod = setting.AutoExposureMethod;
        SceneView->FinalPostProcessSettings.bOverride_AutoExposureApplyPhysicalCameraExposure = setting.bOverride_AutoExposureApplyPhysicalCameraExposure;
        SceneView->FinalPostProcessSettings.AutoExposureApplyPhysicalCameraExposure = setting.AutoExposureApplyPhysicalCameraExposure;
        SceneView->FinalPostProcessSettings.bOverride_AutoExposureBias = setting.bOverride_AutoExposureBias; // true;
        SceneView->FinalPostProcessSettings.AutoExposureBias = setting.AutoExposureBias; //1.0f;
        SceneView->FinalPostProcessSettings.bOverride_AutoExposureMinBrightness = setting.bOverride_AutoExposureMinBrightness; //true;
        SceneView->FinalPostProcessSettings.bOverride_AutoExposureMaxBrightness = setting.bOverride_AutoExposureMaxBrightness; //true;
        SceneView->FinalPostProcessSettings.AutoExposureMinBrightness = setting.AutoExposureMinBrightness; //1.0f;
        SceneView->FinalPostProcessSettings.AutoExposureMaxBrightness = setting.AutoExposureMaxBrightness; //1.0f;

        SceneView->FinalPostProcessSettings.bOverride_AutoExposureSpeedUp = setting.bOverride_AutoExposureSpeedUp; //true;
        SceneView->FinalPostProcessSettings.bOverride_AutoExposureSpeedDown = setting.bOverride_AutoExposureSpeedDown; //true;

        SceneView->FinalPostProcessSettings.AutoExposureSpeedUp = setting.AutoExposureSpeedUp; // 20.0f;
        SceneView->FinalPostProcessSettings.AutoExposureSpeedDown = setting.AutoExposureSpeedDown; // 20.0f;
        SceneView->FinalPostProcessSettings.WeightedBlendables = setting.WeightedBlendables;

#pragma region lumen
        SceneView->FinalPostProcessSettings.bOverride_LumenReflectionQuality = setting.bOverride_LumenReflectionQuality;
        SceneView->FinalPostProcessSettings.LumenReflectionQuality = setting.LumenReflectionQuality;
        SceneView->FinalPostProcessSettings.bOverride_LumenFinalGatherQuality = setting.bOverride_LumenFinalGatherQuality;
        SceneView->FinalPostProcessSettings.LumenFinalGatherQuality = FMath::Clamp(setting.LumenFinalGatherQuality, 2.0f, 100.0f);
#pragma endregion lumen
    }

}

void UCaptureRenderTarget::Capture(const uint8 PreCaptureFrameNum/* = 1*/)
{
    //check(GetState() == ECaptureState::Finished);
    this->PreRenderingFrameNum = PreCaptureFrameNum;
    this->SetState(ECaptureState::Renderering);
}

void UCaptureRenderTarget::CaptureFromLocationAndRotation(const FVector& Location, const FRotator& Rotator, const uint8 PreCaptureFrameNum)
{
    this->mViewLocation = Location;
    this->mViewRotation = Rotator;
    this->Capture(PreCaptureFrameNum);
}

bool UCaptureRenderTarget::GetRawData(UTextureRenderTarget2D* RTT, TArray64<uint8>& OutRawData, ECaptureCubeFace CubeFace)
{
    FRenderTarget* RenderTarget = RTT->GameThread_GetRenderTargetResource();
    EPixelFormat Format = PF_B8G8R8A8; //RTT->GetFormat();

    int32 ImageBytes = CalculateImageBytes(RTT->SizeX, RTT->SizeY, 0, Format);
    OutRawData.AddUninitialized(ImageBytes);
    bool bReadSuccess = false;

    {
        TArray<FColor> Colors;
        FReadSurfaceDataFlags RSDF(ERangeCompressionMode::RCM_UNorm, (ECubeFace)CubeFace);
        bReadSuccess = RenderTarget->ReadPixels(Colors);

        for (int i = 0; i < Colors.Num(); i++)
        {
            Colors[i].A = 255;
        }
        {
            /* 保留,FFloat16Color 转 FColor */
            //TArray<FFloat16Color> FloatColors;
            //bReadSuccess = RenderTarget->ReadFloat16Pixels(FloatColors);

            //for (int i = 0; i < FloatColors.Num(); i++)
            //{
            //    FColor TempColor;
            //    TempColor.R = FMath::Clamp<uint8>(FloatColors[i].R.GetFloat() * 255, 0, 255);
            //    TempColor.G = FMath::Clamp<uint8>(FloatColors[i].G.GetFloat() * 255, 0, 255);
            //    TempColor.B = FMath::Clamp<uint8>(FloatColors[i].B.GetFloat() * 255, 0, 255);
            //    TempColor.A = 255;
            //    Colors.Add(TempColor);
            //}
        }

        FMemory::Memcpy(OutRawData.GetData(), Colors.GetData(), ImageBytes);
    }

    if (bReadSuccess == false)
    {
        OutRawData.Empty();
    }

    return bReadSuccess;
}

bool UCaptureRenderTarget::IOToDisk(const TArray64<uint8>& rawData, const FString& SavePath, const FIntPoint& Size, EImageFormatEx ImageFormat, int Quility)
{
    bool bResult = false;

    {
        FArchive* Ar = IFileManager::Get().CreateFileWriter(*SavePath);
        if (Ar)
        {
            bool bSuceess = false;
            FBufferArchive Buffer;
            {
                int32 BitsPerPixel = 8;
                ERGBFormat RGBFormat = ERGBFormat::BGRA;
                if (ImageFormat == EImageFormatEx::WebP)
                {
                    //按WebP格式走OpenCV进行编码
                    cv::Mat tmp_mat(Size.Y, Size.X, CV_8UC4, const_cast<uint8*>(rawData.GetData()));

                    static std::vector<uint8> CompressedData;

                    std::vector<int> compress_params;
                    compress_params.push_back(cv::IMWRITE_WEBP_QUALITY);
                    compress_params.push_back(Quility);

                    cv::imencode(".webp", tmp_mat, CompressedData, compress_params);

                    Buffer.Serialize(CompressedData.data(), CompressedData.size());
                    tmp_mat.empty();
                    CompressedData.clear();
                }
                else
                {
                    //png,jpg等
                    static IImageWrapperModule& ImageWrapperModule = FModuleManager::Get().LoadModuleChecked<IImageWrapperModule>(TEXT("ImageWrapper"));
                    //强转换
                    TSharedPtr<IImageWrapper> PNGImageWrapper = ImageWrapperModule.CreateImageWrapper((EImageFormat)((int8)(ImageFormat)-1));

                    PNGImageWrapper->SetRaw(rawData.GetData(), rawData.GetAllocatedSize(), Size.X, Size.Y, RGBFormat, BitsPerPixel);

                    const TArray64<uint8>& Data = PNGImageWrapper->GetCompressed(Quility);

                    Buffer.Serialize((void*)Data.GetData(), Data.GetAllocatedSize());
                }

                bSuceess = true;
            }

            if (bSuceess)
            {
                Ar->Serialize(Buffer.GetData(), Buffer.Num());
                if (IsInGameThread())
                {
                    this->OnSaveToDiskFinished().Broadcast(*SavePath);
                    this->OnSaveToDiskFinished_BP.Broadcast(*SavePath);
                }
                else 
                {
                    FGraphEventRef Task = FFunctionGraphTask::CreateAndDispatchWhenReady([this, SavePath]()
                        {
                            //从TaskGrap传递到gamethread进行事件分发
                            this->OnSaveToDiskFinished().Broadcast(*SavePath);
                            this->OnSaveToDiskFinished_BP.Broadcast(*SavePath);
                        }, TStatId(), nullptr, ENamedThreads::GameThread);
                }
            }
            delete Ar;
        }
    }

    return bResult;
}

void UCaptureRenderTarget::SetupCustomPassMass(FSceneView* InSceneView)
{
    if (InSceneView)
    {
        if (!this->bNotInverseGamma)
        {
            {
                //Fixup:视乎这里被GC了..
                //Mat_PPGamma->AddToRoot();
                //UMaterialInstanceDynamic* MatInsDyn_PPGamma = InSceneView->State->GetReusableMID(Mat_PPGamma);
            }
            //将生命周期改变为当前UObject防止被Engine GC...导致RHI线程错误.
            if(MatInsDyn_PPGamma == nullptr || !MatInsDyn_PPGamma->IsValidLowLevel())
            {
                MatInsDyn_PPGamma = UMaterialInstanceDynamic::Create(Mat_PPGamma, this);
            }
            FPostProcessMaterialNode InitialNode(MatInsDyn_PPGamma, Mat_PPGamma->BlendableLocation, Mat_PPGamma->BlendablePriority, Mat_PPGamma->bIsBlendable);
            InSceneView->FinalPostProcessSettings.BlendableManager.PushBlendableData(1.0f, InitialNode);
        }
    }
}

void UCaptureRenderTarget::SaveToDisk(FString path, ECaptureCubeFace CubeFace, EImageFormatEx ImageFormat, int Quility)
{
    FString SavePath;
    if (path.IsEmpty())
    {
        FString Name = FString::Printf(TEXT("test_%s.png"), *FDateTime::Now().ToString());
        SavePath = FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectDir(), TEXT("textures"), TEXT("test"), *Name));
    }
    else
    {
        SavePath = path;
    }

    bool bSuccess = true;
    {
        check(this->GetRTT2D() != nullptr);
        FRenderTarget* RenderTarget = this->GetRTT2D()->GameThread_GetRenderTargetResource();

        FIntPoint Size = RenderTarget->GetSizeXY();

        TArray64<uint8> RawData;
        bSuccess = GetRawData(this->GetRTT2D(), RawData, CubeFace);

        this->IOToDisk(RawData, SavePath, Size, ImageFormat, Quility);
    }
}

void UCaptureRenderTarget::SaveToDiskAsync( FString path /*= TEXT("")*/, 
                                            ECaptureCubeFace CubeFace /*= ECaptureCubeFace::CubeFace_MAX*/, 
                                            EImageFormatEx ImageFormat /*= EImageFormatEx::PNG*/, 
                                            int Quility /*= 100*/)
{
    FString SavePath;
    if (path.IsEmpty())
    {
        FString Name = FString::Printf(TEXT("test_%s.png"), *FDateTime::Now().ToString());
        SavePath = FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectDir(), TEXT("textures"), TEXT("test"), *Name));
    }
    else
    {
        SavePath = path;
    }

    bool bSuceess = false;
    {
        check(this->GetRTT2D() != nullptr);
        FRenderTarget* RenderTarget = this->GetRTT2D()->GameThread_GetRenderTargetResource();

        FIntPoint Size = RenderTarget->GetSizeXY();

        TArray64<uint8> RawData;
        bSuceess = GetRawData(this->GetRTT2D(), RawData, CubeFace);
    
        //拷贝到logic线程...
        Async(EAsyncExecution::Thread, [this, SavePath, ImageFormat, Size, Quility, rawData = MoveTemp(RawData)]()
        {
            this->IOToDisk(rawData,SavePath, Size,ImageFormat, Quility);
        });
    }
}

ECaptureState UCaptureRenderTarget::GetState()
{
    return this->CaptureState;
}


void UCaptureRenderTarget::CalculateProjectionMatrix()
{
    // Calculate a Projection Matrix
    float XAxisFactor = 1.0f;
    float YAxisFactor = 1.0f;

    {
        //相机横纵比
        XAxisFactor = 1.0f;
        YAxisFactor = GetRTT2D()->SizeX / (float)GetRTT2D()->SizeY;
    }

    const float MinZ = GNearClippingPlane;
    const float MaxZ = MinZ;
    const float Radians_FOV = FMath::Max(0.00001f, this->mViewFOV) * (float)PI / 360.0f; //转弧度,并防止FOV为0度/0弧的情况

    //透视矩阵
    this->mProjectionMatrix = FReversedZPerspectiveMatrix(Radians_FOV,
                                                          Radians_FOV,
                                                          XAxisFactor,
                                                          YAxisFactor,
                                                          MinZ,
                                                          MaxZ);
}

void UCaptureRenderTarget::FlushAsyncEngineSystems()
{
    // Flush Block until Level Streaming completes. This solves the problem where levels that are not controlled
    // by the Sequencer Level Visibility track are marked for Async Load by a gameplay system.
    // This will register any new actors/components that were spawned during this frame. This needs 
    // to be done before the shader compiler is flushed so that we compile shaders for any newly
    // spawned component materials.
    if (GetWorld())
    {
        GetWorld()->BlockTillLevelStreamingCompleted();
    }

#if WITH_EDITOR
    // Flush all assets handled by the asset compiling manager (i.e. textures, static meshes).
    // A progressbar is already in place so the user can get feedback while waiting for everything to settle.
    FAssetCompilingManager::Get().FinishAllCompilation();
#endif

    // Now we can flush the shader compiler. ToDo: This should probably happen right before SendAllEndOfFrameUpdates() is normally called
    if (GShaderCompilingManager)
    {
        bool bDidWork = false;
        int32 NumShadersToCompile = GShaderCompilingManager->GetNumRemainingJobs();
        if (NumShadersToCompile > 0)
        {
            UE_LOG(LogTemp, Log, TEXT("[%d] Starting build for %d shaders."), GFrameCounter, NumShadersToCompile);
        }

        while (GShaderCompilingManager->GetNumRemainingJobs() > 0 || GShaderCompilingManager->HasShaderJobs())
        {
            UE_LOG(LogTemp, Log, TEXT("[%d] Waiting for %d shaders [Has Shader Jobs: %d] to finish compiling..."), GFrameCounter,
                   GShaderCompilingManager->GetNumRemainingJobs(), GShaderCompilingManager->HasShaderJobs());
            GShaderCompilingManager->ProcessAsyncResults(false, true);

            // Sleep for 1 second and then check again. This way we get an indication of progress as this works.
            FPlatformProcess::Sleep(1.f);
            bDidWork = true;
        }

        if (bDidWork)
        {
            UE_LOG(LogTemp, Log, TEXT("[%d] Done building %d shaders."), GFrameCounter, NumShadersToCompile);
        }
    }

    // Flush the Mesh Distance Field builder as well.
    if (GDistanceFieldAsyncQueue)
    {
        bool bDidWork = false;
        int32 NumDistanceFieldsToBuild = GDistanceFieldAsyncQueue->GetNumOutstandingTasks();
        if (NumDistanceFieldsToBuild > 0)
        {
            UE_LOG(LogTemp, Log, TEXT("[%d] Starting build for %d mesh distance fields."), GFrameCounter, NumDistanceFieldsToBuild);
        }

        while (GDistanceFieldAsyncQueue->GetNumOutstandingTasks() > 0)
        {
            UE_LOG(LogTemp, Log, TEXT("[%d] Waiting for %d Mesh Distance Fields to finish building..."), GFrameCounter,
                   GDistanceFieldAsyncQueue->GetNumOutstandingTasks());
            GDistanceFieldAsyncQueue->ProcessAsyncTasks();

            // Sleep for 1 second and then check again. This way we get an indication of progress as this works.
            FPlatformProcess::Sleep(1.f);
            bDidWork = true;
        }

        if (bDidWork)
        {
            UE_LOG(LogTemp, Log, TEXT("[%d] Done building %d Mesh Distance Fields."), GFrameCounter, NumDistanceFieldsToBuild);
        }
    }
    #if IS_ARTISTRENDERER
    // Flush the Mesh Distance Field builder as well.
    if (GDistanceFieldAsyncQueue2)
    {
        bool bDidWork = false;
        int32 NumDistanceFieldsToBuild = GDistanceFieldAsyncQueue2->GetNumOutstandingTasks();
        if (NumDistanceFieldsToBuild > 0)
        {
            UE_LOG(LogTemp, Log, TEXT("cf [%d] Starting build for %d mesh distance fields."), GFrameCounter, NumDistanceFieldsToBuild);
        }

        while (GDistanceFieldAsyncQueue2->GetNumOutstandingTasks() > 0)
        {
            UE_LOG(LogTemp, Log, TEXT("cf [%d] Waiting for %d Mesh Distance Fields to finish building..."), GFrameCounter,
                GDistanceFieldAsyncQueue2->GetNumOutstandingTasks());
            GDistanceFieldAsyncQueue2->ProcessAsyncTasks();

            // Sleep for 1 second and then check again. This way we get an indication of progress as this works.
            FPlatformProcess::Sleep(1.f);
            bDidWork = true;
        }

        if (bDidWork)
        {
            UE_LOG(LogTemp, Log, TEXT("cf [%d] Done building %d Mesh Distance Fields."), GFrameCounter, NumDistanceFieldsToBuild);
        }
    }
    #endif
    // if (GCardRepresentationAsyncQueue)
    // {
    //     bool bDidWork = false;
    //     int32 NumTasks = GCardRepresentationAsyncQueue->GetNumOutstandingTasks();
    //     if (NumTasks > 0)
    //     {
    //         UE_LOG(LogTemp, Log, TEXT("cf [%d] Starting build for %d mesh cards."), GFrameCounter, NumTasks);
    //     }
    //
    //     while (GCardRepresentationAsyncQueue->GetNumOutstandingTasks() > 0)
    //     {
    //         UE_LOG(LogTemp, Log, TEXT("cf [%d] Waiting for %d Mesh Cards to finish building..."), GFrameCounter,
    //                GCardRepresentationAsyncQueue->GetNumOutstandingTasks());
    //         GCardRepresentationAsyncQueue->ProcessAsyncTasks();
    //
    //         // Sleep for 1 second and then check again. This way we get an indication of progress as this works.
    //         FPlatformProcess::Sleep(1.f);
    //         bDidWork = true;
    //     }
    //
    //     if (bDidWork)
    //     {
    //         //UE_LOG(LogMovieRenderPipeline, Log, TEXT("[%d] Done building %d Mesh Cards."), GFrameCounter, NumTasks);
    //     }
    // }
#if IS_ARTISTRENDERER
    if (GCardRepresentationAsyncQueue2)
    {
        bool bDidWork = false;
        int32 NumTasks = GCardRepresentationAsyncQueue2->GetNumOutstandingTasks();
        if (NumTasks > 0)
        {
            UE_LOG(LogTemp, Log, TEXT("cf [%d] Starting build for %d mesh cards."), GFrameCounter, NumTasks);
        }

        while (GCardRepresentationAsyncQueue2->GetNumOutstandingTasks() > 0)
        {
            UE_LOG(LogTemp, Log, TEXT("cf [%d] Waiting for %d Mesh Cards to finish building..."), GFrameCounter,
                GCardRepresentationAsyncQueue2->GetNumOutstandingTasks());
            GCardRepresentationAsyncQueue2->ProcessAsyncTasks();

            // Sleep for 1 second and then check again. This way we get an indication of progress as this works.
            FPlatformProcess::Sleep(1.f);
            bDidWork = true;
        }

        if (bDidWork)
        {
            //UE_LOG(LogMovieRenderPipeline, Log, TEXT("[%d] Done building %d Mesh Cards."), GFrameCounter, NumTasks);
        }
    }
    #endif

    // Flush grass
    {
        for (TActorIterator<ALandscapeProxy> It(GetWorld()); It; ++It)
        {
            ALandscapeProxy* LandscapeProxy = (*It);
            if (LandscapeProxy)
            {
                TArray<FVector> CameraList;
                LandscapeProxy->UpdateGrass(CameraList, true);
            }
        }
    }

    // Flush virtual texture tile calculations
    ERHIFeatureLevel::Type FeatureLevel = GetWorld()->FeatureLevel;
    ENQUEUE_RENDER_COMMAND(VirtualTextureSystemFlushCommand)(
        [FeatureLevel](FRHICommandListImmediate& RHICmdList)
        {
            GetRendererModule().LoadPendingVirtualTextureTiles(RHICmdList, FeatureLevel);
        });
}

void UCaptureRenderTarget::RenderFrame()
{
#pragma region 通知RHI绘制到指定画布中

    //this->UpdateSceneView();
    //mCanvas->Clear(FLinearColor::Transparent);
    
    TSharedPtr<FCanvas> InCanvas = MakeShared<FCanvas>(FCanvas( this->mRTT2D->GameThread_GetRenderTargetResource(),
                                                                nullptr,
                                                                GetWorld(),
                                                                ERHIFeatureLevel::SM5,
                                                                FCanvas::CDM_DeferDrawing, //FCanvas::CDM_ImmediateDrawing, //
                                                                1.0f));

    TSharedPtr<FSceneViewFamilyContext> InViewFamily = SetupViewFamily();
    SetupSceneView(InViewFamily);
    GetRendererModule().BeginRenderingViewFamily(InCanvas.Get(), InViewFamily.Get());
    PostRendererSubmission();
#pragma endregion 通知RHI绘制到指定画布中
    {
        //FlushRenderingCommands();
    }
}

void UCaptureRenderTarget::PostRendererSubmission()
{
    //Note:渲染管线队列保证会按序消费Command
    //FRenderTarget* RenderTarget = this->mCanvas->GetRenderTarget();
    ENQUEUE_RENDER_COMMAND(CaptureRenderFrameCommand)
    (
        [this](FRHICommandListImmediate& RHICmdList)
        {
            this->HandleFrame_OnRenderThread();
        }
    );

    //SaveToDisk();
}

void UCaptureRenderTarget::HandleFrame_OnRenderThread()
{
    check(IsInRenderingThread());

    this->CurPreFrame++;

    if (this->GetState() == ECaptureState::Warmup)
    {
        if (this->CurPreFrame >= CF_CaptureRenderTarget::DefaultWarmupFrameNum)
        {
            OnCaptureWarmupCompleted_RenderThread().Broadcast(this);

            this->SetState(ECaptureState::Renderering);
        }
    }
    else if (this->GetState() == ECaptureState::Renderering)
    {
        if (CurPreFrame >= this->PreRenderingFrameNum)
        {
            SetState(ECaptureState::Renderered);
        }
    }
    else if (this->GetState() == ECaptureState::Renderered)
    {
        SetState(ECaptureState::Captured);
        this->OnCaptureCompleted_RenderThread().Broadcast(this);
    }
    //do something
}

void UCaptureRenderTarget::Tick(float DeltaTime)
{
    switch (this->GetState())
    {
    case ECaptureState::Initilize:
        {
            break;
        }
    case ECaptureState::Warmup:
        {
            if (bAsync)
            {
                if (this->CurPreFrame <= CF_CaptureRenderTarget::DefaultWarmupFrameNum)
                {
                    this->RenderFrame();
                }
            }
            else
            {
                if (!bStartWarmup)
                {
                    bStartWarmup = true;
                    for (int i = 0; i < CF_CaptureRenderTarget::DefaultWarmupFrameNum; i++)
                    {
                        this->RenderFrame();
                    }
                }
            }
            break;
        }
    case ECaptureState::Renderering:
        {
            if (bAsync)
            {
                //XXX:maybe线程处理原因会触发多次问题,后面花时间处理一下
                //if(CurPreFrame == 0)
                //{    
                //    OnCaptureWarmupCompleted().Broadcast(this);
                //}

                if (this->CurPreFrame <= PreRenderingFrameNum)
                {
                    this->RenderFrame();
                }
            }
            else
            {
                if (!bStartRender)
                {
                    bStartRender = true;
                    for (int i = 0; i < PreRenderingFrameNum; i++)
                    {
                        this->RenderFrame();
                    }
                }
            }

            break;
        }
    case ECaptureState::Renderered:
        {
            this->RenderFrame();
            break;
        }
    case ECaptureState::Captured:
        {
            SetState(ECaptureState::Finished);
            this->OnCaptureCompleted().Broadcast(this);
            break;
        }
    case ECaptureState::Finished:
        {
            break;
        }
    default:
        {
            break;
        }
    }
    //this->Capture();
}

bool UCaptureRenderTarget::IsTickable() const
{
    if (GetDefault<UCaptureRenderTarget>() == this)
    {
        return false;
    }

    if (this->GetWorld()->IsPaused())
    {
        return false;
    }

    if (!GetRTT2D())
    {
        return false;
    }

    return true;
}

 

标签:const,自定义,渲染,void,UCaptureRenderTarget,视口,FinalPostProcessSettings,SceneView,inc
From: https://www.cnblogs.com/linqing/p/18260935

相关文章

  • 一文读懂Java线程池之自定义线程池、设置合适的线程数量、线程池阻塞队列、线程拒绝策
    在上篇我们学习了线程池各个参数的含义,线程池任务处理流程,使用线程池的好处等内容,本篇我们学习如何创建一个适合我们业务的线程池。为此,我们有必要先学习一下如何大概确定我们线程池核心线程数、怎么设置阻塞队列的类型与大小、当线程池没有能力处理任务了该如何使用拒绝策略等......
  • React的服务器端渲染(SSR)和客户端渲染(CSR)有什么区别?
    React的服务器端渲染(SSR)和客户端渲染(CSR)是两种不同的页面渲染方式,它们各自有不同的特点和适用场景:服务器端渲染(SSR)页面渲染:页面在服务器上生成,然后将完整的HTML发送给客户端。SEO:由于搜索引擎爬虫可以直接访问到完整的页面内容,因此对搜索引擎优化(SEO)更为友好。首......
  • sqlalchemy根据字典kv自定义表结构
    根据数据的内容自动创建数据库表结构fromsqlalchemyimportcreate_engine,Column,Integer,String,Float,Booleanfromsqlalchemy.ext.declarativeimportdeclarative_basefromsqlalchemy.ormimportsessionmaker,Mapped,mapped_columnBase=declarative_base()......
  • Swagger文档渲染,将Filter过滤器中抛出的异常错误消息返回前端显示
    一、应用场景在swagger通过/v2/api-docs获取到后端接口数据后,使用过滤器对/v2/api-docs请求进行拦截,然后对获取到的数据进行处理,当在处理过程中对Filter过滤器中抛出的异常错误消息进行捕获,并将捕获到的异常错误消息返回到前端二、示例代码@OverridepublicvoiddoFilte......
  • 【React篇】父组件渲染时避免重复渲染子组件的3种处理方法
    在React中,父组件渲染时要避免重复渲染子组件,可以使用以下方法:使用React.memo(仅适用于函数式组件)或PureComponent(适用于类组件):这些方法可以帮助你创建在接收到新的props时仅在有必要的情况下重新渲染的组件。它们通过浅比较新旧props来判断是否需要重新渲染。对于......
  • 自定义组件获取接口数据
    <template><divclass="ting-title"><spanclass="text">{{text}}</span></div></template><script>exportdefault{name:'TingTitle',data:()=>{......
  • 微信小程序学习(七):自定义组件和通信
    1、基本知识开发中常见的组件主要分为公共组件和页面组件两种,因此注册组件的方式也分为两种:全局注册:在app.json文件中配置usingComponents节点进行引用声明,注册后可在任意组件使用局部注册:在页面.json文件中配置usingComponents节点进行引用声明,只可在当前页面......
  • apisix~为自定义插件设计一个configmap脚本
    configMapKubernetes中的ConfigMap是一种用来存储配置数据的API资源,它允许您将配置信息以键值对的形式保存,并在容器中使用这些配置信息。ConfigMap提供了一种将配置数据与应用程序解耦的方式,使得应用程序可以动态地获取配置而无需重新构建镜像。以下是ConfigMap的一些特......
  • cesium中如何高性能渲染3D模型(附水淹分析模拟)
    大家好,我是日拱一卒的攻城师不浪,专注可视化、数字孪生、前端、nodejs、AI学习、GIS等学习沉淀,这是2024年输出的第18/100篇文章;前言之前在参加城市应急数字孪生项目开发过程中,遇到一个场景,就是模拟水淹分析。也就是说,甲方需要根据你这个平台,在下暴雨的时候,精准监测到城......
  • ef core自定义默认的迁移表的名称
    efcore自定义默认的迁移表的名称usingMicrosoft.EntityFrameworkCore;usingMicrosoft.EntityFrameworkCore.Design;namespaceLong.EntityFrameworkCore;classMyDesignTimeDbContextFactory:IDesignTimeDbContextFactory<ApplicationDbContext>{publicApplicat......