Seamless Portals

This was already discussed somewhere in Rendering section.

Of course you can’t use regular post process stencil. It’s different thing.

I don’t remember, but I think Tim Hobson explained that stencil buffer is used many times in render flow, and there is no easy access to it.

To resolve this, you should write your own Renderer. I mean like Forward or Deffered Renderer, with supported portals

And even with it you can render correctly only 2 portals at the same time, and only through player camera.

But I think in near future it will possible to use RayTracing feature to make correct recursive and inner portals.

Its easy to achieve with collision channels. Just create some “PortalAllowedWall” channel, and toggle it in actors that passing portals.

But, anyway, there are many other physics problems with portals you should resolve to make it nearly equal to portal and portal 2 games.

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 :slight_smile:

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.

Sorry, that was post by Daniel Wright.

https://forums.unrealengine.com/deve…466#post975466

and another one from answerhub:
https://answers.unrealengine.com/que…il-buffer.html

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.

ConvertRotationToActorSpace: ConvertRotationToActorSpace posted by anonymous | blueprintUE | PasteBin For Unreal Engine 4

(the RotToQuat function is a custom blueprint function I implemented that just converts a rotator into a quaternion since blueprints apparently didn’t have a function that does that. Here it is just in case: RotToQuat posted by anonymous | blueprintUE | PasteBin For Unreal Engine 4. The logic for the conversion was taken from this Wiki page: Conversion between quaternions and Euler angles - Wikipedia)

TeleportActor: blueprintUE | PasteBin For Unreal Engine 4

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?

Thanks!

@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…

If you check my “TeleportActor” blueprint function (here https://blueprintue.com/blueprint/-quw-_8r) you can see that I DO in fact set the control rotation.

Hello!

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.

Thank you

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.

It seems, that I’ve found the line, where it comes to crash:



PortalTexture = NewObject<UTextureRenderTarget2D>(this, UTextureRenderTarget2D::StaticClass(), *FString("PortalRenderTarget"));


Someone know a fix for it? Or how did you solved the crash problem?

On my side the RenderTarget is setup like this (inside my ScriptedTexture class, which is inherited from SceneComponent).

Header:


    private:
        UPROPERTY()
        UCanvasRenderTarget2D* ScriptedTexture;

Body:



//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?

Also, what is the “Update()” function here that is being called? Is it the “Update()” function in the portal manager? Or is it any of these function “UpdateResource()”, “UpdateResourceImmediate”, “FastUpdateResource()”?

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:


PortalTexture->WrapModeX = 1;
    PortalTexture->WrapModeY = 1;
    PortalTexture->bDrawWidgets = false;
    PortalTexture->SetClearOnUpdate(false);
    PortalTexture->Format = ERenderTargetFormat::RGBA16;

    PortalTexture->SetOwner( this );
    PortalTexture->Init();

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. :slight_smile:

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.

I updated my previous post, it should be a bit more clear now. :slight_smile:

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”.

Yep, that’s it. :slight_smile: