Here’s a new breakdown for portal rendering, as requested by @MetricZero.
A few words though:
This breakdown won’t cover everything, but hopefully it should get people started on the right direction to building their own portals and rendering them correctly. This same rendering method could also be used to build non-euclidian worlds (eg, a hallway that looks long on the inside, but outside looks short!). It could probably (and has been, although not really shared) be done better in C++, but this has been currently accomplished completely in blueprints.
Here is an example of what you can expect:
The first thing we should do is create the following assets:
- A new blueprint actor class for our portal (BP_Portal)
- A new blueprint actor class for the portals exit (BP_PortalExitNode)
- A new material for the portal’s mesh (MAT_Portal_Master). In addition, you’ll need a simple plane mesh (rotated upwards to stand vertically) to apply the portal material to.
Lets begin with going over how the material will work.
We plug in a texture 2d parameter into the emissive color. In our BP_Portal class, we will inject a dynamically created render target into this parameter, and that is why we don’t need an actual render target asset. We also plug in a screen aligned UV’s into the UV input of the parameter, as we want the texture to align to the screen.
This material right here is fairly simple. I also do some effects with the opacity mask to create more of a wormhole effect, which just uses some basic masks and noise textures that rotate and blend together.
The last thing I do, which is still a WIP is fade out the portal material (lerping it with the render target parameter). I don’t like this, and will most likely get rid of it. In most cases, its not necessary either, since in our BP_Portal class we will stop updating our render target if the player is far enough away.
Next up is the BP_PortalExitNode class. This class serves as the “exit” for the player and also where the portal renders to as it contains our scene capture component. There isn’t much going on here except for components, variables, and a function to update our scene capture:
Lastly is the meat of it all, the BP_Portal class. There is a lot of stuff going on in this class and I’m not going to cover it all, but I’ll try to cover the jist of it.
On Begin Play: The big thing here is creating the dynamic render target 2d. I’m using the Victory Plugin courtesy of Rama to create the dynamic render target, but with 4.11 or 4.12 there was a new node added which made it possible without the plugin to do this. The big thing here is when creating it is to get the correct screen aspect ratio. If it’s off, our portal will be distorted and look incorrect. To maintain a decent FPS, we get the player controllers viewport resolution, divide both X and Y by 2 (so half of the current screen resolution) and use those numbers for our render targets. Something to add in the future would be a scalability setting for this. Obviously if we do not divide the resolution we get a clearer result, but it tends to murder FPS.
We then take that returned value of the render target, and plug it into a reference of our BP_PortalExitNode’s scene capture component so it has something to render onto. Next we create a material instance of the MAT_Portal_Master and plug the render target texture into the PortalRenderTarget parameter we created inside of it.
Next is the camera math. With everything else setup correctly, we don’t have to do much to get something realistic now.
Essentially, all we need to do is set the scene capture component’s rotation (from out BP_PortalExitNode reference) to the player camera’s rotation. It’s slightly hacky, but I have to actually modify the rotation by 180 degrees to get the correct results. This is probably an area that could receive some slight improvements in the future.
In addition to this all is also where I call the Update function from the BP_PortalExitNode reference. If this function isn’t called (which it isn’t if we are more than 1000uu away) then the portal doesn’t update. The UpdatePortalCapture event is called on tick every 0.03 seconds. Reducing this number comes with more realistic rendering with the sacrifice of performance. Really, scene captures murder FPS, at least for me.
Last thing we need to do is throw in a BP_Portal into our scene and link a BP_PortalExitNode to the Exit Node Reference!
Final Result:
Other things to do are teleport the player to the exit node reference. The easiest way to do this is to setup a trigger volume on the portal which simply transports the player to the exit node references location. This is easier said then done, which is why I didn’t show my code, as its still a WIP and quite messy. The same thing can be done to transport other objects. Another room to improve is to use custom stencil to the give the effect of objects passing into the portal (same thing should be done if you have first person objects and don’t want them to clip into the portal mesh).
I hope this helps people who are looking to build portals. I know its not a complete guide but hopefully it starts people on the right direction to building their own. Even these systems have a ton of room for improvement and hopefully in the future I can expand on this same tutorial with more knowledge gained on the subject matter.