Procedural Generation using Noise

Manual version where you specify the band locations and thickness. Note that color1 has no band settings since it is the background color that will show up in the gap between the other bands. You can easily use power on the alpha gradients for different falloff. The nicest look is from using ExponentialDensity on the alphas.

Note that the A and B inputs are flipped in this version to avoid using a 1-x.

Mathematically, using “Abs” is the same as distance on a 1d value.

Yep, this was much harder to visualize than I expected, thanks for this. Definitely going in my bag of tricks.

is an example of a quick value noise function interpolated as 2 dimensional noise.

Starts with a generic 2d pseudo random number function:


 float2 noise = (frac(cos(dot(uv ,float2(10.3,63.233)*2.0)) * 6743.31385));
    return abs(noise.x + noise.y) * 0.5;

The idea behind noise is to actually sample it at fixed intervals. To that end, you need to sample 4 different noise points for each pixel (for two dimensions that is. three dimensions require 8 corners of sampling). These are the 4 corners and each one is a copy of the custom noise function with offset coordinates:


Notice that since the numbers being fed into the noise node are integers (thanks to the floor), we just have to offset by 1 to move over 1 whole ‘cell’.

See how the positions sampled for each corner come out of the Floor node, and then the blending across each cell is handled by the frac of that UV spot, which is like a 0-1 gradient across each cell. Then the corners are each blended in turn using lerp nodes.

Then to make it possible to make multiple octaves, we make a quick material function that only has one input (UVs) and one output:

Then by just using 4 of these function nodes we can have 4 octaves of noise. That is achieved in this example by dividing each UV by 0.5 (cumulatively) and then constant bias scaling each noise return into the -1 to 1 range and then multiplying each octave by persistence^octave.

That means the first octave gets multiplied by 1, the second by persistence, the third by persistence^2, the fourth by persistence^3 etc.

Ryan, you should package everything you posted and pass it on to the documentation team. I will definitely be using this as a reference in the future if I find myself researching noise functions for UE4! :slight_smile:

So, by writing a noise function using HLSL, can you allow the passing of a scalar parameter from a random seed so it can generate a different set of noise? That was one of the issues I had with the built-in function which only allowed two parameters which seemed fairly useless to me. Then again, I’m new to this and still new to learning about noise functions, so I imagine I’ll need to figure out how Interpolation and Multiplication work in terms of blending noise functions together. The main idea though is by the end to have rocky moons done with Voronoi noise, and earth-like planets done with Perlin/Fractal noise which looks realistic. A bit off-topic, but how does one tune the noise to make realistic looking continents? Can these noise functions also create a planet out of one layer, or will I need multiple layers to apply (ocean, land, relief, atmosphere) etc?

well ocean is by definition just land below a certain elevation. it is up to you whether you need separate noise to mask the oceans. I would think you need a different noise pattern for things like clouds.

Yes you could easily make the things like number of octaves and a “seed” offset. The version above is just psuedo random so the “Seed” there would be to just offset all the values before going into the function.

I might need a reference guide to starting out with these algorithms, since I looked at the HLSL code you wrote regarding generating a noise function and had absolutely no idea how it worked. The algorithm I looked at for Perlin noise definitely couldn’t be condensed into two lines of code! Maybe also for materials as well and how dividing and adding can shape the noise pattern, since its all gone completely over my head.

the noise function itself wasn’t meant to be the useful bit from the above example, more how to structure the blending of the noise result. The function I listed is just a way to take a given point at x,y and convert it to a 0-1 rotation that gets multiplied by a large uneven number. Because the number it is multiplied by is not a whole number, that means for each rotation of the initial values, some “random” extra rotation is left over. it is not perfect and would eventually show repeating artifacts. Imagine taking a series of pictures of a spinning ballerina. If you took photos at a speed that divided with the ballerina rotation speed, all the photos would be the same since you would catch the ballerina at the same point around the 360 spin at each rotation. But if the speed you took photos did not divide evenly, each photo of the ballerina would be slightly different. The angle in each photo is your random number. That is all the function I used is doing. Rotating something then making sure it isn’t sampled at neat intervals.

Also if you took pictures every 1/2 rotation, then only half of the pictures would be different, so the interval must be chosen for the least amount of repetition. That is why pseudo random number generators usually have large prime numbers mixed in for good measure.

info on randomness:
http://www.redblobgames.com/articles/noise/introduction.html

So I’ve kinda switched to the Java generating heightmaps and putting them into UE4, but I get this pretty funky result:

This is what happens when I blend the noise with the noise heightmap I generated in Java. It looks pretty great in making more continent-like areas, but since its blending an already existing series of dysfunctional islands it looks really strange. Any ideas how I can make this look more like an earthlike? My rgb assignment is pretty basic right now, assigning light blue to values below 0, dark green to 0 - 0.4 and brown > 0.4. This is probably where procedurally generating biomes might come in handy! If I remove the noise attachment to the UV it looks pretty awful:

I am not sure what is going on there, are you trying to use that as a one dimensional lookup texture for the colors? If so you have it set up wrong. You need it to be in a straight line and the colors need to be in the position you want it to be looked up. So your gradient texture should look like the vertical rainbow stripe gradient I showed above. You dont want that texture to also have any noise inside of it.

If you want to control color with a texture, your colors need to be in a straight line like this (the texture can be 4 pixels tall to save memory).

7364571508fc28a0d42d41f7a7b912974383fac5.jpeg

Then you need to take the noise node and hook it up into the UV input of the gradient texture. That is the same if you want to use another greyscale noise texture as the actual noise. Hook up the noise texture as the UVs of the gradient texture.

a7d37f773bfed9ec6293aea75f39c32942d8f8c6.jpeg

The noise texture on the left can be anything, your custom noise texture or noise function etc.

Again whether you use one or many noise functions to define your continents is completely an artistic choise. In theory you could get all of it in one noise with nice high and low frequency detail, but in practice it can be easiser to make masks and blend between different noise for different areas. YMMV.

This is by far my favorite thread on the forums. Noise is one of the best tools a developer can use, IMO. Really great stuff, Ryan.

I am glad you are finding it useful.

I needed a quick distraction from another task so I wanted to see how much code the above noise function turned into. Ok so maybe it is a bit longer than a few lines like I thought, but it is not too bad. Most of the length is from blending corners. I seem to recall an even shorter version somewhere but this was as concise as I could get it. The one unfortunate thing about using Custom nodes in the material editor is that you cannot call other functions so all of the code for a function must be included in it. That’s why I use a strange method of storing the corner samples into an array since it isn’t able to call some other tidy function to do it.


float P=1;
float accum, singlesample, toprow, botrow=0;
float2 offsets[4] = {float2(0,0),float2(1,0),float2(0,1),float2(1,1)};
float corners[4] = {0,0,0,0};


//sample each octave
for (int i = 0; i <= Octaves; i++)
{
	//sample 4 corners	
	for (int j = 0; j < 4; j++)
	{

		float2 noise = cos(dot(floor(uv) + offsets[j] ,float2(10.3,63.233)*2.0)) * 6743.31385;
		corners[j] = frac(noise.x + noise.y);
	}

	//cosine interpolation
	float2 cosblend = (1-cos(frac(uv)*PI))*0.5;
		
	//blend the 4 corners
	toprow = lerp(corners[0], corners[1], cosblend.x);
	botrow = lerp(corners[2], corners[3], cosblend.x);
	singlesample = (lerp(toprow, botrow, cosblend.y)-0.5)*2.0;

	
	//accumulate octaves
	accum+=singlesample*P;
	P*=Persistence;
	uv*=2;
}

return 0.5 * (accum+1);

requires inputs for:
uv
Octaves
Persistence

Okay, so I made that colorlut into a material function and hooked up the noise into each UV slot and it worked like a charm. Pretty much have complete control of the color and noise values. I’ll post some photos later on if anyone wants to see.

Edit:

Alright, are two screenshots. The shader isn’t close to being what I’d like it to be, but it’s far better than the original one I posted earlier.


I still couldn’t get clouds to work with a standard lerp, so I added a band for it on the lookup generator and it works fine now. If I can somehow give the clouds like a drop shadow, I’ll be happy with the shader.

Looks great! I am always amazed at what you can do with simple functions in the material editor!

A lot of really great information in .

Definitely starting to look quite nice there James! Guys definitely are able to do some incredibly powerful things inside the material editor. I myself have never really spent much time playing around with it, it kinda seems like 1 of those things that is its entirely own unique way of doing things and in my programming experience I have never really come across something quite like it so most of the nodes and options in there don’t really tie back to anything else to easily grasp what everything is doing. So it’s great to see all of your details on how you go about doing things RyanB. Would love to see some tutorials and better documentation on why and how to do advanced materials like these.

Looks good!

As far as how to make cloud shadows, you probably need to sample the cloud noise function twice. For the shadow, you could use Bumpoffset. Use the noise as the UVs and then set the height value to be a flat scalar parameter. Then you can use that mask to lerp between 1.0 and a shadow tint color and then multiply that by your other colors. Then just add the clouds on top using emissive again but using the regular cloud coords without the bumpoffset for the non-shadow.

For a slightly more accurate bumpoffset, you could use this snippet from the POM node:

you probably want to replace CameraVector with LightVector (which would be a vector param you specify manually, could be computed as normalize(sunpos-planetpos) , but maybe camera vector itself will give a nice looking although fake shadow direction.

The difference is that bumpoffset does not use correct math (probably intentionally) so at glancing angles the offset is not nearly enough. It kind of helps hide artifacts when using a large bumpoffst with a heightmap (some papers on POM refer to this as “horizon flattening” and they do it on purpose to make the steps not look so bad at glancing angles). But since in this case your “heightmap” is a constant, you will always get the correct offset, you really just want accurate offset for that height without any horizon flattening. It would make your shadows flat near the silhouette.

My first attempt was likely going to be the noise offset but I was busy tweaking the atmosphere last night to have proper scattering so I didn’t get to it in time. I also thought about using a light vector last night as the project I’m working on needs accurate shadows from the sun position. However, I wasn’t sure if using it was a good idea as the project needs to be pretty light on performance and I haven’t ever used the light vector before, though I know what it can do. Thanks for the information, though. I will likely try out the light vector first and if it’s too much (currently getting a constant 120fps with lots of enormous PG planets in the scene and I’d like it to stay that way), I’ll try another method.

Next up, cloud shadows, thunderstorms and city lights.

Edit:

Got the alien-like planet looking a good bit better. I do have ONE last question for you, Ryan. Is it possible to rotate the standard noise node? I was trying customrotator among other things last night and it just wasn’t working.

Using a light vector is no different than using camera vector. When you use bumpoffset it already is transforming the cameravector into tangent space, so if you change the vector to be some other vector it doesn’t change the cost. Depending on how you calculate lightvector it may be some tiny cost (a vector subtract and normalize) but you could do the entire offset function (meaning the whole snipped I posted above) using custom UVs and it would be dirt cheap.

If you decide to specify the sun as a position instead of a vector, it will mean you can use one single parameter for all of your planets. I would make a material parameter collection with a vector parameter for SunPos. Then in each material, you access sun pos and compute lightvector as normalize(sunpos-worldpos). Then all your planets will automatically update their lightvector as they rotate around the sun. All of this is fake material lighting, you don’t need any actual light sources unless you want shadow terms but even those could be calculated analytically pretty easy for a sphere.

I would just use BoundingBoxBased_0-1_UVW instead of worldposition. It is local but it is 0-1 so you will have to multiply the value by the size of your planets in worldspace for the results to match what you have currently. Or just use a position transform from world->local if you want but I usually suggest the UVW node for simplicity.

Did not know that first part, but did plan on trying to to make the planets update as they rotated, so this will surely help out.