Your code is pretty solid in terms of functionality, here is how I would approach it though.
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActor(const FObjectInitializer& ObjectInitializer);
// Called when the game starts
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)
USceneCaptureComponent2D* ScopeCam;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)
UTextureRenderTarget2D* RenderTarget;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)
UMaterialInterface* ScopeMat;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)
int32 ScopeMatIndex;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)
FName ScopeMatRTParamName;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)
USkeletalMeshComponent* SkeletalMeshComponent;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Test)
FName ScopeAttachSocketName;
};
C++ Body
AMyActor::AMyActor(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// Set this actor to be ticked every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
SkeletalMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("SkeletalMesh_0"));
ScopeCam = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("ScopeCam_0"));
ScopeCam->FOVAngle = 90.f;
RenderTarget = CreateDefaultSubobject<UTextureRenderTarget2D>(TEXT("RenderTarget_0"));
RenderTarget->InitAutoFormat(1024, 1024);
RenderTarget->AddressX = TA_Wrap;
RenderTarget->AddressY = TA_Wrap;
// ...
RootComponent = SkeletalMeshComponent;
}
// Called when the game starts
void AMyActor::BeginPlay()
{
Super::BeginPlay();
ScopeCam->AttachTo(SkeletalMeshComponent, ScopeAttachSocketName, EAttachLocation::SnapToTarget);
ScopeCam->TextureTarget = RenderTarget;
ScopeCam->bCaptureEveryFrame = true;
ScopeCam->UpdateContent();
if (ScopeMat != nullptr && SkeletalMeshComponent != nullptr)
{
UMaterialInstanceDynamic* MID = SkeletalMeshComponent->CreateAndSetMaterialInstanceDynamicFromMaterial(ScopeMatIndex, ScopeMat);
MID->SetTextureParameterValue(ScopeMatRTParamName, RenderTarget);
}
// ...
}
This code definitely works. If you are still getting a checkerboard gray material, make sure your material has “Use with Skeletal Meshes” enabled.
Thanks for this Allar, I did have something similar to this, but it ends up crashing when I attempt to SetTextureParameterValue. The RenderMaterial and MID are both valid when attempting to set this. I have included the code segment, material and exception I receive.
I’m guessing that the ScopeMat, ScopeMatIndex and ScopeMatRTParamName are set in the Blueprint? Also, if I just create the RenderTarget as above, it doesn’t pass the if (RenderTarget != nullptr) condition. How should it be initialized? (I’m new to UE :P)