Download

How to write to UCanvasRenderTarget2D in C++

I have a pretty simple setup for a RenderCanvasTarget2D in Blueprint which I am trying to recreate in C++, but it’s not working for some reason.

Here is the BP version:

In a BP derived from RenderCanvasTarget2D, this is the ‘Event Receive Update’ delegate function:

And then in a BP actor, I have this in BeginPlay:

This successfully creates a CanvasRenderTarget2D of 128x128 pixels, writes a diagonal line across it, and saves it to the hard disk. All working perfectly.

In the C++ version, I am doing everything in a single actor called ‘HDRWriter’. Here is the full relevant code:

HDRWriter.h


#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Runtime/Engine/Classes/Engine/CanvasRenderTarget2D.h"
#include "Runtime/Core/Public/Delegates/Delegate.h"
#include "Runtime/Engine/Classes/Engine/Canvas.h"
#include "Runtime/Engine/Classes/Kismet/KismetRenderingLibrary.h"
#include "HDRWriter.generated.h"

UCLASS()
class DRAWLINEBP_API AHDRWriter : public AActor
{
    GENERATED_BODY()

public:    
    // Sets default values for this actor's properties
    AHDRWriter();

    // The Canvas Render Target 2D
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    UCanvasRenderTarget2D* CanvasRenderTarget;

    // Update Function
    UFUNCTION()
    void WhenCanvasUpdated(UCanvas* Canvas, int32 Width, int32 Height);

    UFUNCTION(BlueprintCallable)
    void CallUpdate();

    UFUNCTION(BlueprintCallable)
    void SetupCanvas();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

};

HDRWriter.cpp


#include "HDRWriter.h"

// Sets default values
AHDRWriter::AHDRWriter()
{
     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = false;
}

void AHDRWriter::WhenCanvasUpdated(UCanvas* Canvas, int32 Width, int32 Height)
{
    Canvas->K2_DrawLine(FVector2D(0.0f, 0.0f), FVector2D(Width, Height));

    UKismetRenderingLibrary::ExportRenderTarget(GEngine->GetWorld(), CanvasRenderTarget, "D:\\", "CPPLine.hdr");
}

void AHDRWriter::CallUpdate()
{
    CanvasRenderTarget->UpdateResource();
}

void AHDRWriter::SetupCanvas()
{
    CanvasRenderTarget = UCanvasRenderTarget2D::CreateCanvasRenderTarget2D(GEngine->GetWorld(), UCanvasRenderTarget2D::StaticClass(), 128, 128);

    CanvasRenderTarget->OnCanvasRenderTargetUpdate.AddDynamic(this, &AHDRWriter::WhenCanvasUpdated);
}

// Called when the game starts or when spawned
void AHDRWriter::BeginPlay()
{
    Super::BeginPlay();
}

// Called every frame
void AHDRWriter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

So I run SetupCanvas to create the CanvasRenderTarget2D and bind a function (WhenCanvasUpdated) to the delegate OnCanvasRenderTargetUpdate. When I then call CallUpdate() it calls UpdateResource() on the CanvasRenderTarget.

When the function WhenCanvasUpdated() is then run, it should draw a line and save the file to the hard disk, as with the Blueprint version.

However, a file is indeed saved, but it is totally black - no line is drawn. I feel like I’m missing some important connection between the RenderCanvasTarget and the UCanvas that is passed in the delegate function, but I can’t see how they should be connected.

I tried nativizing the BP into C++ code, but found it to be completely illegible.

Does anyone know what I’m doing wrong here?

Thanks!

No responses for two years??? I’m also looking into this topic, and found an article that looks very useful:

https://www.ue4community.wiki/Legacy…RenderTarget2D