Viewport black bars color

Hello, folks!

I’m making custom viewport widget for custom camera via editor extension module.

Everything works fine except the Background color - color of bars with constrained aspect ratio.

For some reason the rendering into the render target overrides whole canvas and kill custom background clear color here (always set to black):

Canvas->Clear(BackgroundColor);
GetRendererModule().BeginRenderingViewFamily(Canvas, &ViewFamily);

View->CameraConstrainedViewRect = View->UnscaledViewRect seems fine and have proper values (constrained rect for aspect ratio).

ViewInitOptions.BackgroundColor = BackgroundColor is set correct

I’m geussing it’s something about ViewInitOptions or ViewFamily properties but can’t find what it could be. Maybe you have an idea. Thank you for the help!

Full source code is below:

#pragma once

#include "CoreMinimal.h"
#include "Components/ContentWidget.h"
#include "Slate/SceneViewport.h"
#include "Widgets/SViewport.h"
#include "UnrealClient.h"
#include "GProject/GCamera.h"
#include "GameView.generated.h"

class GPROJECTEDITOREXTENSION_API FGViewportClient : public FCommonViewportClient, public FViewElementDrawer
{
public:
	FGViewportClient();
	virtual ~FGViewportClient() override;

	using FViewElementDrawer::Draw;

	// FViewportClient interface
	virtual UWorld* GetWorld() const override;
	virtual void Draw(FViewport* InViewport, FCanvas* Canvas) override;
	
	// FGViewportClient
	virtual void Tick(float DeltaTime);	
	void UpdateViewInfo(float DeltaTime);

	virtual FSceneView* CalcSceneView(FSceneViewFamily* ViewFamily);

	/**
	 * @return The scene being rendered in this viewport
	 */
	virtual FSceneInterface* GetScene() const;
	
	bool IsAspectRatioConstrained() const;

	/** Get the near clipping plane for this viewport. */
	float GetNearClipPlane() const;

	void SetBackgroundColor(FLinearColor InBackgroundColor);
	FLinearColor GetBackgroundColor() const;

	void SetEngineShowFlags(FEngineShowFlags InEngineShowFlags)
	{
		EngineShowFlags = InEngineShowFlags;
	}

protected:

	bool IsCameraExist;
	
	TWeakObjectPtr<AGCamera> Camera;

	FMinimalViewInfo ViewInfo;

	FLinearColor BackgroundColor;

	FViewport* Viewport;

	/** The viewport's scene view state. */
	FSceneViewStateReference ViewState;

	/** A set of flags that determines visibility for various scene elements. */
	FEngineShowFlags EngineShowFlags;

	TArray<FPostProcessSettings> ControllingActorExtraPostProcessBlends;
	TArray<float> ControllingActorExtraPostProcessBlendWeights;
};

/////////////////////////////////////////////////////
// SAutoRefreshViewport

class GPROJECTEDITOREXTENSION_API SAutoRefreshViewport : public SViewport
{
	SLATE_BEGIN_ARGS(SAutoRefreshViewport)
	{
	}
	SLATE_END_ARGS()

	SAutoRefreshViewport(){}

	void Construct(const FArguments& InArgs)
	{
		SViewport::FArguments ParentArgs;
		ParentArgs.IgnoreTextureAlpha(false);
		ParentArgs.EnableBlending(false);
		ParentArgs.EnableGammaCorrection(false);
		SViewport::Construct(ParentArgs);

		ViewportClient = MakeShareable(new FGViewportClient());
		Viewport = MakeShareable(new FSceneViewport(ViewportClient.Get(), SharedThis(this)));

		// The viewport widget needs an interface so it knows what should render
		SetViewportInterface(Viewport.ToSharedRef());
	}

	virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override
	{
		Viewport->Invalidate();

		Viewport->Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
		ViewportClient->Tick(InDeltaTime);
	}

public:
	TSharedPtr<FGViewportClient> ViewportClient;
	
	TSharedPtr<FSceneViewport> Viewport;
};

/////////////////////////////////////////////////////
// UGameView

UCLASS()
class GPROJECTEDITOREXTENSION_API UGameView : public UContentWidget
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, Category=Appearance)
	FLinearColor BackgroundColor;

	// UWidget interface
	virtual void SynchronizeProperties() override;
	// End of UWidget interface

	virtual void ReleaseSlateResources(bool bReleaseChildren) override;

#if WITH_EDITOR
	virtual const FText GetPaletteCategory() override;
#endif
	
public:

	UGameView(const FObjectInitializer& ObjectInitializer);

protected:
	// UPanelWidget
	virtual void OnSlotAdded(UPanelSlot* Slot) override;
	virtual void OnSlotRemoved(UPanelSlot* Slot) override;
	// End UPanelWidget

	// UWidget interface
	virtual TSharedRef<SWidget> RebuildWidget() override;
	// End of UWidget interface

	TSharedPtr<class SAutoRefreshViewport> ViewportWidget;
	
	/** Show flags for the engine for this viewport */
	FEngineShowFlags ShowFlags;
};

CPP

#include "GameView.h"

#include "CanvasTypes.h"
#include "EngineModule.h"
#include "EngineUtils.h"
#include "LegacyScreenPercentageDriver.h"
#include "LevelEditor.h"
#include "SceneViewExtension.h"
#include "Camera/CameraComponent.h"
#include "Components/LineBatchComponent.h"
#include "Kismet/GameplayStatics.h"

#define LOCTEXT_NAMESPACE "UMG"

FGViewportClient::FGViewportClient()
	: Viewport(nullptr)
	, EngineShowFlags(ESFIM_Game)
{
	const FSceneInterface* Scene = GetScene();
	ViewState.Allocate(Scene ? Scene->GetFeatureLevel() : GMaxRHIFeatureLevel);

	BackgroundColor = FColor(55, 55, 55);
}

FGViewportClient::~FGViewportClient()
{
}

void FGViewportClient::Tick(float DeltaTime)
{	
	UpdateViewInfo(DeltaTime);
}

void FGViewportClient::UpdateViewInfo(float DeltaTime)
{
	// Update Camera
	if ( Camera == nullptr )
	{
		IsCameraExist = false;
		// Try to fine the camera in current world 
		TArray<AActor*> Cameras;
		UGameplayStatics::GetAllActorsOfClass(GetWorld(), AGCamera::StaticClass(), Cameras);
		if ( !Cameras.IsEmpty() )
		{
			Camera = Cast<AGCamera>(Cameras[0]);
			IsCameraExist = true;
		}
	}
	else
	{
		IsCameraExist = true;
	}

	// UpdateView info
	if ( IsCameraExist )
	{		
		const TObjectPtr<UCameraComponent> CameraComponent = Camera->GetCameraComponent();

		if ( ensure(CameraComponent->GetEditorPreviewInfo(DeltaTime, ViewInfo)) )
		{
			CameraComponent->GetExtraPostProcessBlends(ControllingActorExtraPostProcessBlends, ControllingActorExtraPostProcessBlendWeights);
		}
	}
	else
	{
		ViewInfo = FMinimalViewInfo();
		ControllingActorExtraPostProcessBlends.Empty();
		ControllingActorExtraPostProcessBlendWeights.Empty();
	}
}

void FGViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas)
{
	//Valid SceneCanvas is required.  Make this explicit.
	check(Canvas);
	
	FViewport* ViewportBackup = Viewport;
	Viewport = InViewport ? InViewport : Viewport;

	UWorld* World = GetWorld();
	if (World == nullptr)
	{
		return;
	}

	const FGameTime Time = World->GetTime();

	// Setup a FSceneViewFamily/FSceneView for the viewport.
	FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
		Canvas->GetRenderTarget(),
		GetScene(),
		EngineShowFlags)
		.SetTime(Time)
		.SetRealtimeUpdate(true));

	// Get DPI derived view fraction.
	const float GlobalResolutionFraction = GetDPIDerivedResolutionFraction();
	
	// Force screen percentage show flag for High DPI.
	ViewFamily.EngineShowFlags.ScreenPercentage = true;

	FSceneViewExtensionContext ViewExtensionContext(InViewport);
	ViewFamily.ViewExtensions = GEngine->ViewExtensions->GatherActiveExtensions(ViewExtensionContext);

	for (auto ViewExt : ViewFamily.ViewExtensions)
	{
		ViewExt->SetupViewFamily(ViewFamily);
	}
	
	FSceneView* View = CalcSceneView(&ViewFamily);

	View->CameraConstrainedViewRect = View->UnscaledViewRect;
	
	ViewFamily.SetScreenPercentageInterface(new FLegacyScreenPercentageDriver(
		ViewFamily, GlobalResolutionFraction));

	if ( IsAspectRatioConstrained() )
	{
		Canvas->Clear(BackgroundColor);
	}

	GetRendererModule().BeginRenderingViewFamily(Canvas, &ViewFamily);

	Viewport = ViewportBackup;
}

FSceneView* FGViewportClient::CalcSceneView(FSceneViewFamily* ViewFamily)
{
	FSceneViewInitOptions ViewInitOptions;

	const FIntPoint ViewportSize = Viewport->GetSizeXY();
	ViewInitOptions.SetViewRectangle(FIntRect(0, 0, ViewportSize.X, ViewportSize.Y));

	const FVector ViewLocation = ViewInfo.Location;
	const FRotator ViewRotation = ViewInfo.Rotation;

	AWorldSettings* WorldSettings = nullptr;
	if( GetScene() != nullptr && GetScene()->GetWorld() != nullptr )
	{
		WorldSettings = GetScene()->GetWorld()->GetWorldSettings();
	}
	if( WorldSettings != nullptr )
	{
		ViewInitOptions.WorldToMetersScale = WorldSettings->WorldToMeters;
	}

	ViewInitOptions.ViewRotationMatrix = FInverseRotationMatrix(ViewRotation);
	ViewInitOptions.ViewRotationMatrix = ViewInitOptions.ViewRotationMatrix * FMatrix(
		FPlane(0, 0, 1, 0),
		FPlane(1, 0, 0, 0),
		FPlane(0, 1, 0, 0),
		FPlane(0, 0, 0, 1));
	
	const EAspectRatioAxisConstraint AspectRatioAxisConstraint = GetDefault<ULevelEditorViewportSettings>()->AspectRatioAxisConstraint;

	FMinimalViewInfo::CalculateProjectionMatrixGivenView(ViewInfo, AspectRatioAxisConstraint, Viewport, ViewInitOptions);

	if (!ViewInitOptions.IsValidViewRectangle())
	{
		// Zero sized rects are invalid, so fake to 1x1 to avoid asserts later on
		ViewInitOptions.SetViewRectangle(FIntRect(0, 0, 1, 1));
	}

	ViewInitOptions.ViewOrigin = ViewLocation;
	ViewInitOptions.ViewLocation = ViewLocation;
	ViewInitOptions.ViewRotation = ViewRotation;
	ViewInitOptions.ViewFamily = ViewFamily;
	ViewInitOptions.SceneViewStateInterface = ViewState.GetReference();
	ViewInitOptions.ViewElementDrawer = this;	
	ViewInitOptions.BackgroundColor = BackgroundColor;
	ViewInitOptions.bUseFauxOrthoViewPos = true;
	ViewInitOptions.FOV = ViewInfo.FOV;
	ViewInitOptions.DesiredFOV = ViewInfo.DesiredFOV;
	ViewInitOptions.bUseFieldOfViewForLOD = ViewInfo.bUseFieldOfViewForLOD;	
	if ( IsCameraExist )
		ViewInitOptions.ViewActor = Camera.Get();

	FSceneView* View = new FSceneView(ViewInitOptions);

	ViewFamily->Views.Add(View);

	View->StartFinalPostprocessSettings(ViewLocation);

	if ( IsCameraExist )
	{
		// Pass on the previous view transform of the controlling actor to the view
		View->PreviousViewTransform = ViewInfo.PreviousViewTransform;
		View->OverridePostProcessSettings(ViewInfo.PostProcessSettings, ViewInfo.PostProcessBlendWeight);
		
		for (int32 ExtraPPBlendIdx = 0; ExtraPPBlendIdx < ControllingActorExtraPostProcessBlends.Num(); ++ExtraPPBlendIdx)
		{
			FPostProcessSettings const& PPSettings = ControllingActorExtraPostProcessBlends[ExtraPPBlendIdx];
			float const Weight = ControllingActorExtraPostProcessBlendWeights[ExtraPPBlendIdx];
			View->OverridePostProcessSettings(PPSettings, Weight);
		}
	}

	View->EndFinalPostprocessSettings(ViewInitOptions);

	for (int ViewExt = 0; ViewExt < ViewFamily->ViewExtensions.Num(); ViewExt++)
	{
		ViewFamily->ViewExtensions[ViewExt]->SetupView(*ViewFamily, *View);
	}

	return View;
}

FSceneInterface* FGViewportClient::GetScene() const
{
	UWorld* World = GetWorld();
	if ( World )
	{
		return World->Scene;
	}

	return NULL;
}

UWorld* FGViewportClient::GetWorld() const
{
	const FWorldContext* World = GEngine->GetWorldContextFromGameViewport(GEngine->GameViewport);

	return World->World();
}

bool FGViewportClient::IsAspectRatioConstrained() const
{
	return ViewInfo.bConstrainAspectRatio;
}

void FGViewportClient::SetBackgroundColor(FLinearColor InBackgroundColor)
{
	BackgroundColor = InBackgroundColor;
}

FLinearColor FGViewportClient::GetBackgroundColor() const
{
	return BackgroundColor;
}

float FGViewportClient::GetNearClipPlane() const
{
	return (ViewInfo.PerspectiveNearClipPlane < 0.0f) ? GNearClippingPlane : ViewInfo.PerspectiveNearClipPlane;
}

/////////////////////////////////////////////////////
// UGameView

UGameView::UGameView(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, ShowFlags(ESFIM_Game)
{
	bIsVariable = true;

	BackgroundColor = FLinearColor::Black;
}

void UGameView::ReleaseSlateResources(bool bReleaseChildren)
{
	Super::ReleaseSlateResources(bReleaseChildren);

	ViewportWidget.Reset();
}

TSharedRef<SWidget> UGameView::RebuildWidget()
{
	if ( IsDesignTime() )
	{
		return SNew(SBox)
			.HAlign(HAlign_Center)
			.VAlign(VAlign_Center)
			[
				SNew(STextBlock)
				.Text(LOCTEXT("GameView", "GameView"))
			];
	}
	else
	{
		ViewportWidget = SNew(SAutoRefreshViewport);

		if ( GetChildrenCount() > 0 )
		{
			ViewportWidget->SetContent(GetContentSlot()->Content ? GetContentSlot()->Content->TakeWidget() : SNullWidget::NullWidget);
		}

		return ViewportWidget.ToSharedRef();
	}
}

void UGameView::SynchronizeProperties()
{
	Super::SynchronizeProperties();

	if ( ViewportWidget.IsValid() )
	{
		ViewportWidget->ViewportClient->SetBackgroundColor(BackgroundColor);
		ViewportWidget->ViewportClient->SetEngineShowFlags(ShowFlags);
	}
}

void UGameView::OnSlotAdded(UPanelSlot* InSlot)
{
	// Add the child to the live canvas if it already exists
	if ( ViewportWidget.IsValid() )
	{
		ViewportWidget->SetContent(InSlot->Content ? InSlot->Content->TakeWidget() : SNullWidget::NullWidget);
	}
}

void UGameView::OnSlotRemoved(UPanelSlot* InSlot)
{
	// Remove the widget from the live slot if it exists.
	if ( ViewportWidget.IsValid() )
	{
		ViewportWidget->SetContent(SNullWidget::NullWidget);
	}
}

#if WITH_EDITOR

const FText UGameView::GetPaletteCategory()
{
	return LOCTEXT("Primitive", "Primitive");
}

#endif

#undef LOCTEXT_NAMESPACE