9.13.2016

Getting the Most Out of Noise in UE4

By Ryan Brucks

UE4 has had material based procedural noise for some time now, but most users have had to limit their usage of it due to its high performance cost. As a result, many users end up authoring tiling noise textures in separate programs then bringing those back into UE4 to use. Many have wanted to be able to do this step in UE4 but have lacked an easy enough way to do so. 

We have addressed these needs in Unreal Engine 4.13. These improvements can be broken down into a few categories:

1) Tiling noise options
2) Newly added Voronoi noise
3) Performance Optimizations and Misc Improvements

First, I would like to thank Rendering Engineer and travelling Professor Marc Olano who I worked with on the Tiling and Voronoi options. He also did all of the performance improvements and code cleanup. Many of the preexisting options have been optimized, and the tool-tips now give descriptions of cost. Note that even though these optimizations have helped quite a bit, most forms of procedural noise are still costly to render, so we will talk about the tiling noise options that allow us to bake static textures first. In the last section we will go over the relative expense of the different noise functions.

Tiling Noise

In 4.13, if you place a 'Noise' node into a material and select it, you will notice two new options at the bottom called Tiling and Repeat Size:

noiseNode

If Tiling is checked, the noise will repeat over the specified Repeat Size (aka the domain). If you want to bake out some tiling noise, the setup is pretty simple. You need to make sure that the Repeat Size matches the sampled size that you will be baking out. The simplest way to do this is to simply use default 0-1 Texture Coordinates for the input Position and then simply set Scale and Repeat Size to the same value like the above example image.

The reason why we want to use default 0-1 UV's for the position is so we can bake out a texture using the new Draw Material to Render Target feature courtesy of Rendering Engineer Daniel Wright. We are only scratching the surface of what this feature can accomplish in this post, by the way. Once you have done the setup below, you will be able to reuse it for any number of related operations so hang in there.

First, we need to make a new Actor Blueprint in the content browser. Click Add New and then Blueprint Class. When the new dialogue pops up, click Actor and then name the asset.

Next, we need to make a Render Target in the content browser. Click Add New, then Materials and Textures, then Render Target. After that, double click this new Render Target and set the resolution to be the size you want to bake. We are almost done!

Now open up your newly added Blueprint and navigate to the Event Graph. Add a Custom Event and name it "Bake". Specify the Material to bake and the Render Target that you created. 

Next, navigate to the Construction Script and call the custom event Bake:

bakeNode

constructionNode

After you compile the Blueprint, your material will be written to the Render Target. Now all you have to do is right click the Render Target and select Create Static Texture and the texture will be created.

createStaticTex

Voronoi Noise

Voronoi noise is great for billowy clouds, caustics and many other patterns found in nature. It is sometimes referred to as either Worley or Cellular noise but they are all the same thing. Here are some basic possibilities you can create right out of the box:

voroniNoise

From Left to Right:
1) One octave with power = 2
2) One octave, inverted (1-x)
3) Three octaves, inverted

Here's a cracked desert floor made entirely from Voronoi noise in UE4:

crackedFloor1

crackedFloor2

Here's a breakdown of the creation steps:

noiseBreakdown

From Left to Right:
1) Raw Voronoi noise with 1 octave, inverted
2) Voronoi noise with distortion applied by using small amount of "Gradient" noise added to input position
3) Voronoi Noise sampled with "High Pass" material which equalizes edge detail.
4) Using "Normal from Heightmap" function to generate normalmap.

The final sand image was created by using the texture from step 3 above as the heightmap in a Parallax Occlusion Mapping material.

The "High Pass" was performed using a new material function “High Pass Texture”.  Edge variation was achieved by adding another Gradient noise used to vary the offset width. This version of the high pass function expects a texture so the texture at this point was saved out in order to use:

materialSetup

There is also a “High Pass Function” version that takes functions instead of a texture for those who would prefer that method.

To bake the normal, you simply bake out the result of step 3 above to a Render Target and then use the Normal From Heightmap function and then render this material to RT:

normalFromHeightMap

Note that with only minor changes, such as decreasing the high-pass offsets and increasing the high-pass strength, variations like this caustics texture can be created:

caustics

Here's a stylized billowy smoke pillar:

smokeyPillar

This effect is created by sampling two simple Voronoi textures as panning textures. The textures get multiplied together  then the square root is taken to maintain the fullness of the billowy shapes. It is applied to a tessellated cylinder mesh. Note that the final height gets multiplied by the world space normal before connecting to World Position Offset. 

materialSetuo

Here's an example of Voronoi-based marble:

voroniMarble

And here's how to create marble type textures using Voronoi noise as a base. 

voroniMarbleWorkflow

From Left to Right:
1) Standard Voronoi Noise, 1 octave
2) Voronoi with "Gradient" noise added to input position, set to 0.05
3) Gradient noise multiplied by 0.3 before adding to Voronoi input position
4) Using result of step 3 as texture coordinates for random texture from engine\content:

Texture2D'/Engine/Engine_MI_Shaders/T_Base_Tile_Specular.T_Base_Tile_Specular'

This is using a technique known as gradient mapping. The exact range of colors that get used is controlled by adding to and multiplying the result before plugging it into the texture. You can paint custom textures to look up into or just try using any image that has the colors you want to pull from. 

marbleMaterial

This example just adds a scalar distortion into the input position. As you might notice in the above images, this results in diagonal streaking pattern. For marble, this is perfectly fine, but for other effects you may want to use different noise patterns for both the X and Y distortion or offset using a normal map.

Notes on Voronoi Noise Cost

​​Voronoi noise is expensive and there are 4 different quality levels available. The difference between them is the number of cells searched. Generally, there are fewer grid artifacts the higher the quality number. Note that when you place the noise node, it is set to 6 octaves by default, but you will only see the instruction count for one octave:

Quality 1 searches 8 cells,   ~160 instructions per octave
Quality 2 searches 16 cells,  ~320 instructions per octave
Quality 3 searches 27 cells,  ~540 instructions per octave
Quality 4 searches 32 cells,  ~640 instructions per octave

As a comparison, a typical material is around 100 instructions. A material with 6 octaves of Quality 4 Voronoi noise would be around 3800 instructions or around 30x as expensive to render. One octave of Quality 1 Voronoi noise would only be 160 instructions which compares reasonable to a basic material.

octaves

From Left to Right:
1) Quality 1
2) Quality 2
3) Quality 3
4) Quality 4

Note that quality 2 is a bit darker than the rest due to using two offset 2x2x2 grids, meaning the points are more densely packed. This is easily overcome by multiplying by 2.

Performance Characteristics

To demonstrate the real cost of using the noise node, I measured the performance cost for each function with 1-6 levels of octaves using a full-screen quad. I also added a comparison to a regular texture sample. For the regular texture sample, octaves were just additional samples added together.

noisePerformance

As expected, most of the noise options are significantly slower than a simple texture lookup. “Fast Gradient - 3D Texture” is by far the fastest of the noise functions because it has been baked to a volume texture, whereas the other “Texture Based” options use 2D noise textures to perform the randomness operation which requires additional math to sample as 3D. So if you need to use procedural noise and you also need to keep your scenes rendering fast, “Fast Gradient” is your best bet for now, but keep in mind it will show repetition if used over a large area or if the tiling is increased too much.

The high quality Voronoi noise functions are the most expensive so it is important to bake those into textures if you need to use them in a performance constrained environment.

I hope you found this information valuable and now can leverage noise effectively in your projects.

Recent Posts

Get Set for the Summer #ue4jam!

Get ready to buckle down and put your development skills to the test! Ne

...

Unreal Developers Shine at Develop:Brighton 2018

Numerous developers displayed their titles in the Unreal Networking Lounge ...

Unreal Powers On-the-Fly Compositing with Handheld Camera

Going beyond green screens: ARwall’s rear-projection system provides in-cam...