Creating Polar Coordinates / Circular Distortion in UE5.
In this tutorial we cover how to make an Unreal 5 material shader VFX. There are two other parts in this tutorial series. In “Part One” we created the base static geometry needed for the VFX. Then in “Part Two” we created some textured noises in Substance Designer.
In this third part, we will use the textures created inside an Unreal 5 Material Graph. First we will created the “parent” material, then we will copy an instance of it and tweak the values of the different scalars and parameters and get the visual that we see below.
Software that we will be using:
- Unreal Engine 5
- Substance Designer
In this third part, I will be created the parent material in Unreal Engine 5. If your project is in Unreal Engine 4, you can still follow. The tutorial does not use any material graph nodes or features that are locked to UE5. Everything is available in UE4 as well.
If you are curious for the exact version of my engine, I will be using UE5.3. You can use any previous ones though, as already mentioned above.
As usual, before we go into UE5 and start working, lets see a reminder of what the end result will be:
(Youtube embedded video above, showcasing the final vortex VFX we will be creating.)
The previous two parts of the tutorial we made the geometry and the textures, now we will create the main part - the shader.
(Youtube embedded video above. Shows the VFX basin duplicated three times. One is blue, the other is purple, the last one is green.)
As seen before, here are screenshot previews:
As said in the previous entries, we are making only the VFX, not the rest of the room or art, but you can read about the creation process of those over here.
Before we get into creating our UE5 material graph, I want to take a moment and talk about the shader itself. The technique we are after is UV distortion and tiling that with a panner makes a movement towards a central point. Like a vortex. This effect is accomplished through “polar coordinates”. It is all UV coordinates distortion. We feed in an ordinary tiling texture and we manipulate its UVs to get a circular, inwards motion (or outwards, we can change direction just through one parameter).
A very simple, short explanation of what exactly we are doing is the following. We take linear direction coordinates: on the U and V axis and we use maths to distort them into circular coordinates.
If you would like to buy the work files behind this tutorial series, follow the link by clicking the image below.
In Other Games
The above explained technique is incredibly powerful. It’s not hard to create and its not very demanding resources wise (it scales on the GPU, which is always a good thing for performance in games). As such its very commonly seen in games. That is both in old and new titles. Both in AAA and indie games.
As such I wanted to show you a few examples that I came across randomly, without searching, while I was playing some games recently. That will help you see the elegance of the technique we are about to use.
In Diablo 4 that I’ve been playing quite a bit at the time of writing this, there are a few world bosses. The map gets a pin with a timer that gives you 30 minutes to get to a special zone where the boss entity will spawn. Lots of players gather there and if you are early you get to see a few different animations and effects that show the “preparation” of the spawning of the giant enemy you are about to face.
When Ashava the Pestilent is about to spawn, their presence in Saraan Caldera is announced by this large, toothed mesh:
If you look past the mesh and its movement, and focus on the middle, you will see a flat plane with a VFX material applied to it. That material is absolutely identical to the one we are making. Its a vortex like motion, panned outwards. The artist at Blizzard have made and applied normal and albedo textures to give it a dark, tar-like appearance. Looks amazing!
Baldur’s Gate 3
Another title I’ve been obsessed with (many of you most likely are as well) is Baldur’s Gate 3.
If you look under the feet of my Tiefling Sorcerer in the video above, on the dark floor tiles of Moonrise Towers you will see a bright, white vortex with outwards movement. From time to time it gets a chromatic tint that gives it a subtle rainbow look.
This again is exactly the polar coordinates UV distortion we are making. The difference between this one and ours (and the one in Diablo 4) is that here, the devs at Larian Studios have also put a low opacity mask. That gives the effect a more transparent look.
Material Graph Preview
Lets first see the entire material graph:
Then lets zoom in for better readability:
With the output (final node) selected, here are its material settings to the left side of the screenshot:
For “Material Domain” we use “Surface. In the “Blend Mode” we go “Opaque”. For “Shading Model” we use “Unlit”. Two Sided is ticked off, Use Material Attributes is off as well. “Cast Ray Traced Shadows” is on and for Subsurface Profile there is nothing. The Advanced settings are left to their default states.
If you only want to get the visual result and are not interested in any technical, mathematical or further detailed explanation behind some of the steps and nodes, you can replicate what you see from the zoomed up pictures above and skip most of the tutorial below.
Lets start from the beginning though:
Creating the Polar Coordinates
Create a “Materials” folder in your project directory. Inside create a “Parents” folder. In there we will create our parent materials from which we will create our instanced materials later on.
Right click in there and make a new material. Name it “MAT_Parent_VFX_Vortex”. Open it. Click the output node and lets tweak the settings, putting them to what we mentioned tiny bit ago:
Right click anywhere. In the search box that appears write “tex” and once “TextureCoordinates” appears, click on it to add it.
Hold “S” on the keyboard. Press once on the board, under the newly added TexCoord node. A new “Scalar Parameter” will appear. It will have its name set as “Param”. Edit it and name it “Polar Centre”.
Using the search we explained, find and add “Subtract” node. Connect it like what is seen below. Then search “ComponentMask” and add two of them. We will order and tweak them in the next step.
Select the “ComponentMask” node. By default “R” and “G” channels are on. For node that is on the top row, leave only “R” on. For the node that is at the bottom, leave “G” on. Then search for a “Multiply” node. Add it like seen below (which is both A and B connected to the Subtract).
Search for “Arctangent2” and add one. Connect the two masks into it.
Arc tangent is an inverse tangent function. We do an inverse tangent function because we are switching from Cartesian coordinates and moving into Polar coordinates. We are doing Arctangent2 instead of Arctangent because we are dealing with a coordinates system split into four quadrants. Arctangent would limit us only into quadrant number one and four. By the end of this tutorial I've linked further reading on the topic.
(If you would like a bit more optimized result, you can replace your Arctangent2 for Arctangent2Fast. It is a node a bit less accurate, but still accurate enough for our visuals to remain the same.)
Add a “Component Mask” again, this time change its settings so only “R” channel is on. After it add a “Divide” node and connect the Mask into A. Then search for “math pi” and select Pi to add on the board.
Add a Scalar Parameter with a value of 2 and connect it into Multiplier (S) input of the Pi node. Connect the Pi output into the B input of Divide node.
Let's say you are recreating this in Unity in Shader Graph or in Amplify Shader Editor. Because you totally could, all nodes and functions exist over there, no matter it is another engine. If for some reason Pi is not defined in whatever package you have, you could just add a new scalar parameter and give it a value of == 6.2831 which would be correct enough approximation of 2 x Pi for the shader to work well.
After the “Divide” node, add a “Frac” node. Now take a look at the lower row, right after the “Multiply” node. Create a Mask (R) and Mask (G) nodes. Connect both of them into an “Add” node.
Search for “SquareRoot”. Add it into the board (it will show up as “Sqrt”). Now put two “Add” nodes at each row and connect into “A” of each.
Add two “Multiply” nodes and connect them into “B” of each “Add”. In the “B” of the multipliers, add new Scalar Parameters. Name them “Polar Speed” for top row and “Polar Diagonal Speed” for bottom row.
Search for “Time” node and add it. Connect it into both “A"s of each “Multiply” node (as seen in picture below).
Add two “Multiply” nodes. The previous “Add” node seen on top row, connect into A of Multiply top. The bottom “Add” node, connect to “B” of the new, secondary “Multiply” on that row.
Add two new Scalar Parameters. Top one call “Tile U” and connect to “B” of top row Multiply. Bottom one call “Tile V” and connect to the “A” of bottom row Multiply.
Search for “AppendVector” from under “Math” category. Add it to the board. Connect each row as seen here.
Tip for a tidy board:
You can select all of the nodes and press “C” on the keyboard (C stands for Comment in this case). That will add a big frame over it all. For text put in “Polar Coordinates”. Change the color to blue or whatever you like.
Focus on the right side of the previous work, right after the “Append”.
Search “Texture Sample”. Add one and connect “Append” into its “UVs” pin. With the “Texture Sample” selected, rename it to “AlbedoTexture”. In its settings change the following: MipValueMode: MipLevel (absolute, 0 is full resolution). Sampler Type: Linear Color. (these are pointed in the image below).
Lets create the final part of the shader. It will be a bunch of new nodes that we will then frame into a red comment box, as seen below.
Start by adding a “Vector Parameter”. Name it “Base Tint”. Connect it to a new Multiply. Then add a new “Scalar Parameter”. Name it “Tint Power”. Connect it to the Multiply as well.
Add another “Scalar Parameter”. Name it “Brightness”. Connect it to the “B” of a new Multiply node. In the A connect the AlbedoTexture node from the previous steps.
Add a final “Multiply”. In the A input of it, add the previous “Multiply”. In the B, add the “Multiply” that comes right after the “Base Tint” color node (vector parameter). The result of this final “Multiply”, connect into the “Emissive Color” of the final Material Input.
(When you are done with all of the above you can select it all and add yourself a red comment frame, like seen below. Name it “Color Control”.)
We are all done! Zoom out and take a look at all of it.
Go back to your Project Directory. Find where this material parent that we just made is. Right click on it and select “Create Material Instance”. Call it “MAT_VFX_Vortex_A”. Open it. All of the Scalar Parameters and Vector Parameters (color) have default values but we now want to connect our texture in there and also change the values so we get the visual we want.
After opening the newly created material instance, use the parameter values as seen here:
Tint Power: 1.3
Polar Centre: 0.5
Polar Diagonal Speed: 0.025
Polar Speed: 0.02
Tile U: 1.0
Tile V: 3.5
Albedo Texture: TEX_Liquid_Albedo (this is the black and white texture we made in the previous tutorial)
Base Tint: FFFFFF00 (white color)
Preview in Action
Drag into a scene the imported vortex geometry you created in part one of the tutorial. Then change its scale to 1.5 1.5 1.3 (this is optional, I just scaled mine a bit so its not too small when compared to a 2m tall human mannequin).
Then change the material settings. Select the Material Instance we created - “MAT_VFX_Vortex_A”.
Congrats! This is it. You should be able to see the movement and animation in the VFX.
If you dragged your material shader onto the geometry and your object suddenly turned pitch black, then lets fix that.
This most commonly happens on older versions of UE.5. Its due to the material graph parent shader not being suitable for “Nanite Geometry”.
Remove the imported static geometry vortex basin from the project directory (content browser). Now re-import it but this time around from the Import settings tick “Nanite” off. Now retry dragging it into the scene and applying the material and it should work.
If you look at the VFX result from a camera angle from above, you will see UV distortion imperfection. We resolve that in the next part of the tutorial. Even if you don't want to create a refraction shader, and would like to skip that, make sure to still visit the next tutorial and find the solution to this UV distortion issue.
Next part in the tutorial where we can see the solution to fixing the UV distortion in the middle. Scroll down to the “Troubleshooting” chapter in there to see it.
We keep it to white here because in the final part of the tutorial series, we will create a Blueprint in which we control the color tint. That part is optional. As such if you prefer to skip it, and instead want the colors right away, as seen in the tutorial previews (blue, purple, green), then do the following.
Create 3 copies of the above material instance. That is the one we just made and called “MAT_VFX_Vortex_A”. Those copies rename “MAT_VFX_Vortex_Blue”, “MAT_VFX_Vortex_Purple” and “MAT_VFX_Vortex_Green”. Go in each and change the “Base Tint” HEX linear value.
In this tutorial we created the main driving force of the VFX: the polar coordinates shader. We then created material instances of it and went over what settings to use in order to get the visual we want.
In the next part of the series, we will quickly create a “Refraction” material shader. We will use that as a floating layer of detail, above the vortex we have now. This step will be incredibly short and easy to make in comparison to the length and difficulty of the previous 3 parts of this tutorial.
Next part in the tutorial - Creating Refraction Material Graph. Adding it as a layer of detail on the vortex.