Returns the correct texture pointer when run in the editor, and everything works fine.
However, when I run my project as a standalone or after packaging, it returns a null pointer. I’m guessing the viewports and rendertargets are set up differently in standalone mode compared to regular mode. How can I achieve what I want, so it works in all cases?
Okay, so I managed to get non-null texture pointer, by moving the code to the Draw() method of a custom UGameViewportClient, but the texture I get is not being rendered to when playing as standalone (I get a completely black texture). The exact same code works fine in PIE-mode.
I did in fact solve this! To convert the pixel format, you can sample the backbuffer using a simple pixel shader and draw it on a new render target with the desired size and format. Seems to have close to zero impact on performance.
VinnyBlood’s method can work,but if you just want to get the texture manually somtimes but not every frame,see about this.
lets go in code.
GetGameViewport()->GetRenderTargetTexture will return the member RenderTargetTextureRHI in client,SceneViewport.UseSeparateRenderTarget() decide whether the RenderTargetTextureRHI is valid or not(FSceneViewport::InitDynamicRHI),wo if you want the texture,you gonna to make SceneViewport.bUseSeparateRenderTarget ture.if this flag value set as false,render result will send to the backbuffer directly.And this value setted in construct function with ViewportWidget(SWiewport) member parameter.
and as a violent way you can simply set GameViewportWidgetRef’s construct paramater RenderDirectlyToWindows to false in UGameEngine::CreateGameViewportWidget(),and every thing about this will be same with the editor.
However,you couldnt access to viewport dynamicrhi(just like editor),and you will get a upset down viewport in android.
It may have a runtime method to change the setting so you will not needed to change the engine source code,I dont have enough time to find it out right now.
Adding to this thread (a couple years and more than a couple versions of Unreal later) with what worked for me in case someone else runs into this problem. In my case, I needed to be able to read from the back buffer on-demand, but not every frame, in both editor and packaged/shipping configurations. Not 100% certain, but I think the reason GEngine->GameViewport->GetGameViewport()->GetRenderTargetTexture().GetReference() doesn’t work without the editor present has to do with the fact that that function is returning an FTextureRHIRef for an FSlateTextureRenderTarget2DResource, which I believe is responsible for the editor viewport widget and nothing else. Again, hard to tell without digging more into the renderer given the state of Unreal’s docs, but I at least know FSlateTextureRenderTarget2DResource::InitRHI is not being called when the editor is not present.
Despite this, there is a way to get a back buffer at runtime:
ENQUEUE_RENDER_COMMAND(CopyBackBuffer)([this](FRHICommandList& RHICmdList)
{
FRHITexture* frameBufferRef = nullptr;
#if WITH_EDITOR
// Grabbing editor viewport here is fine.
if (GEngine)
{
frameBufferRef = GEngine->GameViewport->GetGameViewport()->GetRenderTargetTexture().GetReference();
}
#else
// Get to the swap chain directly and grab a reference to the back buffer.
if (GDynamicRHI && GEngine)
{
FViewport* viewport = GEngine->GameViewport->Viewport;
FViewportRHIRef viewportRHI = viewport->GetViewportRHI();
if (!viewportRHI)
{
// TODO log some warning here
return;
}
frameBufferRef = GDynamicRHI->RHIGetViewportBackBuffer(viewportRHI);
}
#endif //WITH_EDITOR
// Copy the backbuffer down to a color array.
TArray<FLinearColor> backBuffer;
FIntPoint viewportSize = GEngine->GameViewport->Viewport->GetSizeXY();
FIntRect captureRect = FIntRect(0, 0, viewportSize.X, viewportSize.Y);
FRHICommandListImmediate::Get().ReadSurfaceData(frameBufferRef, captureRect, backBuffer, FReadSurfaceDataFlags(RCM_MinMax));
}
This gets us away from the SlateUI interface and any potential editor shenanigans and allows us to talk to the proper RHI. Included as well is an RHI command to read that back buffer down to an array of FLinearColor, which helps to standardize output across the editor/packaged pixel format disparity mentioned here, as FLinearColor can be constructed correctly from both PF_B8G8R8A8 and PF_A2B10G10R10.