happen to have a link? I’ve had a good look around and not found any great info. Though I did see some old posts by galaxyman who seemed to get a great result, but he did not give details.
Thats a great sounding suggestion - and perhaps a better option, but before diving that deep I’m trying out altering scenecapture2d to accept a 2d rect bound so I can minimise render time (though, memory usage would still be fairly extreme, particularly with recursion)
It looks like it should not be so hard - but it seems this is not the reality of the situation
That sounds like an excellent solution for allowing portals to be placed against walls - somewhat disappointed in myself I did not think of it.
Though it does not solve the issue of collisions with a clone at the other end of the portal, I think this is dooable with some low-ish level hooks in physx.
Yeah, the real problem is objects that colliding with itself, character that colliding with itself, and objects that colliding with characters who holds them.
Anyway, I should ask ) @DanielW, maybe you can advise with RayTracing?
will it be possible to modify/extend it (on post process phase) for some specific objects, like recursive mirrors/portals, or some other non-euclidian space?
@redbox Thanks for the links!
I’d seen the forum thread, but not read in detail - contains info I was not aware of.
The answerhub thread I’ve also seen before, but it seems to be a “dead end” broken doc search link - but even doing a fresh search for stencil buffer in the docs yeilds no results?
I’ve also got some interest in raytraced portals, but it really seems like an RTX (or similar hardware) required feature to run with reasonable performance.
Right after the nvidia drivers that allowed my 1080ti to do RT, I had a quick go at setting up two reflective surfaces, and standing a character (mannequin) between them…
Default bounce limit is 1 - and performance is not great, default response to exceeding the bounce limit is to render “black”
Turning up the bounce limit quickly brought my system to its knees.
Though it did look nice, aside from the black square in the distance, which could perhaps be fixed by something along the lines of using pixels from the last frames image…
I suspect on RT hardware, rendering at 1080p with DLSS up to 4k, this may be much more practical - still unsure about how perf would be with recursion, I’d hope it can be better than the raster method.
though you raise a good point, I’d hardly considerd it could be problematic to transform rays through a portal instead of a simple reflection, would still expect it to be easier than fiddling around with captures and buffers though.
I am having some issues with the portal teleportation. Can anybody help me out? More specifically, character rotation is wrong upon teleportation.
I have tried following the teleportation section of the tutorial but instead of using C++ I have attempted to convert the tutorial’s C++ code into Blueprints. My ConvertLocationToActorSpace blueprint implementation works just fine but ConvertRotationToActorSpace does NOT and I have no idea why. My blueprint code is a 1:1 copy of the C++ code and yet, it does not work.
I am fairly certain that the error is in the ConvertRotationActorSpace blueprint function (looks fine to me though) but I added the TeleportActor blueprint function just in case.
Does anyone here have a single clue why I am getting the wrong rotation upon character teleportation?
@Schytheron if your camera rotation is correct, but player rotation is not, it is probably from setting only the actor rotation. You also/instead need to set the control rotation, if your pawn uses controller rotation as in the default Character templates.
The system in my sig does this with a check along the lines of:
if actor == getPlayerPawn: cast to Character : (respond based on if it is a Character)
but really, it should check if the player pawn uses controller rotation directly…
It’s me again… It seems, that I’m not able to follow your tutorial right. I tried it with C++ and Unreal Engines just crashes when I try to test it in PIE. Then I tried to recode it in blueprints and nothing works like it should. Does some have a running solution and maybe can share it with us? That would be really, really nice! Of course only when Froyak allows it.
I am facing the exact same issues as you. Tried to first write it in C++. Everything went fine until I got to the “GeneratePortalTexture()” function in the portal manager class and it all went downhill from there because I could not figure out the structure or what his custom “UExedreScriptedTexture” class looked like because never showed it. I tried to replicate it best I could by making estimated guesses. It compiled, but the editor crashed as soon as I hit “Play”. Great. Eventually I managed to get it to actually run after I changed some things in my custom implementation of the “ScriptedTexture” class.
It played, but now everything was broken. Render texture is blocked by an overlaying default texture, render texture refuses to clear itself and just overwrites itself every tick causing a blurry mess, teleporting is very inconsistent, my mesh deforms incorrectly etc. (video of the issues: https://www.youtube.com/watch?v=0gQSsQ20LHc).
At that point I just said “F*ck it!” and attempted to rewrite the entire thing from scratch using BP (but using a different portal mesh this time). I didn’t get far. Got stuck at the teleport section because my “TeleportActor” function in Blueprints applied the wrong rotation to my player every time I teleported (as detailed in my previous reply). Now I don’t know what to do… this is so frustrating.
//Function to call at runtime when you create the RenderTarget.
void Init()
{
ScriptedTexture = UCanvasRenderTarget2D::CreateCanvasRenderTarget2D( GetWorld(), UCanvasRenderTarget2D::StaticClass(), SizeX, SizeY );
if( ScriptedTexture == nullptr || !ScriptedTexture->IsValidLowLevel() )
{
return;
}
//"Attach" the resource to the class, to avoid garbage collection
ScriptedTexture->AddToRoot();
//Specify here some properties
ScriptedTexture->Filter = TextureFilter::TF_Bilinear;
//Binding of the Update function, where we draw things
ScriptedTexture->OnCanvasRenderTargetUpdate.AddDynamic(this, &UExedreScriptedTexture::OnReceiveUpdate);
//Call the draw function of the RenderTarget
ScriptedTexture->UpdateResource();
}
If you crash, it’s likely because something is not valid, etc. You should look at the callstack to get more information.
Here is the bottom part of my “GeneratePortalTexture()” function in my portal manager:
//Cleanup existing RTT
if (PortalTexture != nullptr && PortalTexture->IsValidLowLevel())
{
PortalTexture->DestroyComponent();
GEngine->ForceGarbageCollection();
}
//Create new RTT
PortalTexture = nullptr;
PortalTexture = NewObject<UPTZRScriptedTexture>(this, UPTZRScriptedTexture::StaticClass(), *FString("PortalRenderTarget"));
PortalTexture->SizeX = CurrentSizeX;
PortalTexture->SizeY = CurrentSizeY;
//Custom properties of the UExedreScriptedTexture class
PortalTexture->Gamma = 1.0f;
PortalTexture->WrapModeX = TA_Wrap; //Clamp
PortalTexture->WrapModeY = TA_Wrap; //Clamp
PortalTexture->bDrawWidgets = false;
PortalTexture->bGenerateMipMaps = false;
PortalTexture->SetClearOnUpdate(false); //Will be cleared by SceneCapture instead
PortalTexture->RTFormat = ETextureRenderTargetFormat::RTF_RGBA16f; //Needs 16b to get >1 for Emissive
PortalTexture->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale);
PortalTexture->RegisterComponent();
PortalTexture->SetOwner(this);
PortalTexture->Init();
PortalTexture->SetFilterMode(TextureFilter::TF_Bilinear);
And here is the code of my own implementation of the custom “ScriptedTexture” class:
.h:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "Engine/CanvasRenderTarget2D.h"
#include "PTZRScriptedTexture.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class PORTALIZER_API UPTZRScriptedTexture : public USceneComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UPTZRScriptedTexture();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UFUNCTION()
void Init();
UFUNCTION()
UTexture* GetTexture();
UFUNCTION()
void SetFilterMode(TextureFilter filter);
UFUNCTION()
void SetClearOnUpdate(bool bShouldClearOnUpdate);
UFUNCTION()
void SetOwner(AActor* NewOwner);
UPROPERTY(Transient)
UCanvasRenderTarget2D* RenderTarget;
UPROPERTY()
float Gamma;
UPROPERTY()
TEnumAsByte<enum TextureAddress> WrapModeX;
UPROPERTY()
TEnumAsByte<enum TextureAddress> WrapModeY;
UPROPERTY()
bool bGenerateMipMaps;
UPROPERTY()
bool bDrawWidgets;
UPROPERTY()
bool bClearOnUpdate;
UPROPERTY()
TEnumAsByte <enum ETextureRenderTargetFormat> RTFormat;
UPROPERTY()
int32 SizeX;
UPROPERTY()
int32 SizeY;
};
.cpp:
// Fill out your copyright notice in the Description page of Project Settings.
#include "PTZRScriptedTexture.h"
// Sets default values for this component's properties
UPTZRScriptedTexture::UPTZRScriptedTexture()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// Called when the game starts
void UPTZRScriptedTexture::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void UPTZRScriptedTexture::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
void UPTZRScriptedTexture::Init()
{
RenderTarget = UCanvasRenderTarget2D::CreateCanvasRenderTarget2D(GetWorld(), UCanvasRenderTarget2D::StaticClass(), SizeX, SizeY);
RenderTarget->TargetGamma = Gamma;
RenderTarget->AddressX = WrapModeX;
RenderTarget->AddressY = WrapModeY;
RenderTarget->bAutoGenerateMips = bGenerateMipMaps;
RenderTarget->RenderTargetFormat = RTFormat;
RenderTarget->SetShouldClearRenderTargetOnReceiveUpdate(bClearOnUpdate);
}
UTexture* UPTZRScriptedTexture::GetTexture()
{
return RenderTarget;
}
void UPTZRScriptedTexture::SetFilterMode(TextureFilter filter)
{
RenderTarget->Filter = filter;
}
void UPTZRScriptedTexture::SetClearOnUpdate(bool bShouldClearOnUpdate)
{
bClearOnUpdate = bShouldClearOnUpdate;
}
void UPTZRScriptedTexture::SetOwner(AActor* NewOwner)
{
}
(bDrawWidgets does nothing and “SetOwner” is empty because I currently have no idea how to use it what they are supposed to do exactly)
I don’t know if this helps since has already posted an answer but there ya go I guess.
EDIT: This code is broken though so don’t expect it to work.
You don’t care about “bDrawWidgets”, it’s a Boolean that I use to know if I need to render a custom Widget class that I use to create my UI in my Render Targets (I don’t use UMG). You can get rid of it. Same for SetOwner, it’s related to my Menu/UI system to query the widgets.
As I said elsewhere : my scripted texture class is really there for my own needs, there is no point in trying to copy/paste it. Dealing with a UCanvasRenderTarget2D directly will be much more efficient.
The .cpp/body code is missing a function name. What function is that code supposed to represent? Is it your “Init” function (the one that gets called in your GeneratePortalTexture function in the portal manager)? Is it the constructor?
There are obviously some things missing here which are being called in the GeneratePortalTexture function in the portal manager class that are not addressed in your body code. For example:
I think I have been able to correctly figure out the implementation behind “SetClearOnUpdate” and “Format” (check my previous post which contains my own implementation of the ‘ScriptedTexture’ class) but I am still pretty unsure about the implementation of “WrapModeX/Y” (how and why does it take an integer? I can’t find a single function or variable in the RenderTexture C++ API that contains the word “Wrap”.The only thing I can find that is even remotely close are the “AddressX” and “AddressY” variables but those take an enum [for example ‘TA_WRAP’], not a number), “SetOwner” (like I understand what it’s supposed to do but I don’t get what code you called/used in order to set the owner. There is a function in the C++ API called “SetOwner” but this function is part of “AActor” and “AActor” does not inherit from “USceneComponent” or “UCanvasRenderTarget2D” so I can’t really use it… I think), “Init” (it’s probably the body code you just posted) and “bDrawWidgets” (where is this used? I can’t find a single function or variable in the RenderTarget C++ API that contains the word “Widget”).
Also, is the “bNeedsTwoCopies” boolean in your body code supposed to be true or false?
Sorry for asking so many question and being all over the place but I guess I just need some sort of context to the code you just posted.
EDIT: Whoops, you already answered some of the questions in the post above which I missed. You probably posted it while I was writing this post.
Aha… ok. It’s a bit clearer now that I understand that the properties you posted previously weren’t necessary for the portal system to work. That was what I was mostly scratching my head over. Couldn’t figure out which properties and settings that were absolutely necessary for the portal setup and which weren’t.
After actually testing this there seems to be a tiny bit of the puzzle missing… my compilation fails because the function “OnReceiveUpdate” does not exist. The USceneComponent does not seem to have that function. I am guessing this is a custom function of yours. I tried creating a custom placeholder function in my “ScriptedTexture” class with the same name (that just prints some text to the screen for debugging, used this post as reference because it was literally the only thing I could find when I googled “OnReceiveUpdate”) just to see if it would at least compile.
It compiles but my portal views are now a buggy mess. What should the “OnRecieveUpdate” function actually look like?
Here is just what my placeholder looks like currently:
void UPTZRScriptedTexture::OnReceiveUpdate(class UCanvas* Canvas, int32 Width, int32 Height)
{
if (GEngine)
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("UPTZRScriptedTexture Received Update!"));
}
You can ignore the call to OnCanvasRenderTargetUpdate.AddDynamic() and remove it. It’s only needed if you plan on drawing manually into the render target. The SceneCapture will take care of the drawing update directly.
Aha ok. I am not that familiar with C++ in Unreal since I usually use Blueprint but I am guessing that line of code basically just says “Whenever render target updates -> Call the custom OnReceiveUpdate function”.