Hey there. Yesterday I was playing around with some more advanced UI stuff in Unreal for my game, The Peacenet, that’s entirely UI-based. Basically, I wanted to apply various post-process effects such as bloom and the regular “Add To Viewport” node on a User Widget just wasn’t going to cut it. I needed a way to simulate the behaviour of adding a widget to the viewport while making it render in 3D so I can take advantage of the various effects and post-processing features of the UE4 Camera. Needless to say, I found a way. I decided to post a tutorial in case it’s useful to anyone.
The other day, I made a video showing off the effects working in my game. So, when we’re done, it should look something like this.
The primary limitation with this style of UI is that it’s meant to simulate rendering the UI in 2D space. To accomplish this, the camera is set to orthographic mode and the Ortho Width matches the Viewport’s width. So, this won’t work in any actually 3D game (and therefore VR), because they require a perspective camera.
Setting up the UI
Before we can begin, we need to make two User Widget blueprints. The first one is the UI you actually want to post-process, and the second one is a “viewport” widget. Fun fact, UMG treats the player’s viewport as a sort-of Canvas Panel, so, that s exactly what our viewport widget will be.
So, create a new User Widget and call it “PostProcessViewport” or whatever you see fit. When the UMG editor opens up, select the Canvas Panel and make sure Is Variable is checked in the Details panel. Name the variable “Viewport.”
Then, pop over to the Graph section in the UMG editor. Create a new Function and call it Add Widget to Viewport (do NOT get it confused with Unreal’s Add To Viewport function! We’re recreating that.) This function will take in a single Input of type Widget Object Reference. Name it “In Widget.”
I can’t show a Blueprint graph right now since I’m not at a computer with UE4 installed, but basically this function will check if “In Widget” is valid. If so, then it will add the widget to the Canvas Panel we named “Viewport” using the “Add Widget to Canvas Panel” node. That node returns a Canvas Panel Slot, which we need to set up so it fills the entire canvas.
To do that, simply set the Canvas Panel Slot’s Anchors to (0, 0) minimum and (1,1) maximum. Set the Alignment to (0,0), and set the Offsets to (0,0,0,0). Compile, then leave the UMG editor.
Setting up a Pawn
Next we need to set up a Pawn that we can possess which manages the UI. So, go ahead and create a new Pawn blueprint. Theoretically we could use any Actor for our blueprint but the Pawn makes it easy since we can possess it.
In the Pawn, add the following components:
- Widget Interaction
Select the Camera and change it from Perspective to Orthographic. This makes it so that objects won’t scale based on how far they are from the camera.
Select the Widget and set the Widget Class to that Viewport Widget we just made. if it doesn’t look like it did anything, don’t worry. Remember it’s just a Canvas Panel with a function that adds a widget to it. You may want to temporarily add something like an Image to that widget just so you can see it in the 3D viewport so you can make sure the camera is facing the UI.
The Widget Component will only render one-sided, so if it’s not rotated to face the camera you won’t see any of your UI. If this happens, try rotating the UI 180 degrees on the Z axis.
This one is a little tricky. You need to make sure the debug arrow touches the UI widget. You also need to make sure that the Interaction Source is set to Mouse rather than World so that it moves with your mouse. If you need to, move the camera back a fair bit so that you can see the debug arrow in-game. It’ll appear as a red dot in-game and your UI won’t shrink, because the camera is orthographic.
Scripting the Pawn
Now we need to set up the Pawn’s functionality. All we need to do is create a function that wraps that one we created in the Viewport Widget, then make sure the Camera and Widget Component both update to fit the viewport.
Wrapping the Viewport Widget
Createe a function that takes in a Widget Object Reference. This function will take our Widget Component, get its User Widget Object, cast it to the Viewport Widget class, and call the function we made for it passing in our Widget Object Reference.
You can also create a Function Library that wraps this function so you don’t need a reference to your Pawn to add UI to it.
Updating the Camera and Widget Component to fit the viewport
Go to your Pawn’s Event Graph and grab an “Event Tick” node. Every tick, you’ll want to Get Viewport Size and Get Viewport Scale. Divide the result of Get Viewport Size by the result of Get Viewport Scale and you’ll get the viewport’s DPI-scaled size. Break that vector to get its X and Y values individually. Set the Camera’s Ortho Width to match that X value, and set the Widget Component’s Draw Size to the X and Y values of that same vector.
Then, in Event Begin Play, try adding a widget to your new 3D user interface viewport and make sure the game automatically uses your custom Pawn for the player. Try playing the game in-editor, and you should see the UI as if it were added to the 2D viewport. If you eject from the pawn, you should be able to move the camera around and see that same UI in 3D space.
Some things you may want to consider is making sure the Widget Component accepts hardware input, in case input doesn’t work right. I haven’t tried it without that on. You may also want to look into only getting the UI to re-render when something changes so that the game doesn’t have to work as hard. Widget Components use render targets, and creating one the size of the viewport is expensive. For the most part it runs well but you may experience some hitches sometimes.
So now you can post-process a user interface. Now the only thing you need to do is adjust the Camera’s post-process and effects settings to your liking. In my case, I just have a subtle bloom enabled. One thing you definitely need to do is disable that auto-exposure thing that Unreal likes to do. It’s going to make your UI look extremely awful and unreadable if you leave it on. You can disable it by setting the Min Brightness and Max Brightness to 1 in the Camera’s exposure settings.
Also, thank you to Alexander Mejia from Human Interact as well as ImmutableLambda from the Unreal Slackers discord for helping me troubleshoot various bugs I’ve had while setting this up.
I’ll edit this post and add Blueprint graphs and screenshots when I get home tonight.