Noise Fundamentals for 3D Artists
Noise is such an important concept in the context of computer graphics. I will try to explain the many applications of noise in 3D art, first providing a framework of what noise is in mathematical terms. I won’t describe how they are constructed in detail, just the minimum knowledge necessary to art direct them.
This assumes high school level math fundamentals. Do you know what a function is? Do you know your basic trigonometry? Do you know what the difference between a scalar and a vector is? A little bit of statistics knowledge will help too. If so, then you’re in for a real treat. I don’t have a chance to talk about noise being amazing often, so here I am spitballing what I think is relevant information for a first introduction.
First, let us qualitatively describe what noise is. Noise, in essence, is some sort of variance. Noise exists everywhere in nature, but it is distinctly different from pure randomness.
Noise is annoying in real life. In a laboratory when you are working with measured data, noise muddies up measurements and takes away from the clarity of a relationship between variables in an experiment. In a hospital, minor variability in judgement between healthcare workers can lead to big trouble. Often times, the goal is to quantify this noise and seek to remove these random variances.
Noise is something we often want removed. Given a random variable, we can try to predict the distribution of its outcome. A pure random variable has a uniform distribution, while something like sampling the height of random people leads to a Gaussian distribution because few people are freakishly tall or short. Noise often has a method to its madness. Maybe a judge in a courtroom gets hangry towards the evening and leans towards harsher sentences later in the day.
One of my favorite quotes about randomness is: “We consider a phenomenon random because we do not know everything about it, or simply because we do not need to know everything about it.” Do not underestimate human ingenuity to find a model to describe things even considered random, because if you want to dig into the philosophical rabbit hole, everything can be deterministic in a sense. But do we want to measure every little thing that affects a system? Hell no! Creating simplified models is how most problems are solved and saves time.
In the context of computer animation, noise is an incredibly powerful tool. Noise provides a sense of organic randomness. And you don’t need to know how it works in order to get great use out of the concept. The invention of noise textures in computer graphics ushered in a golden age of 3D artistry.
In computer graphics, we like to work with pseudorandom processes. What this means, is that when we use randomness to describe something, like a texture or random scale of scattered objects in the wild, the distribution of values appears to be random, but there is an underlying procedure that is reproducible to define that randomness. This is why there is a random “seed”, where you can input the same seed in a random number generator to get the same exact random number with the same seed.
Noise, as a texture, is designed to be deterministic. Given the same input parameters, you can the same exact output every time. It’s driven by pseudorandom processes, where the underlying math functions to define the texture has useful visual qualities to manipulate.
The ultimate goal as a 3D artist is to be able to quantify a visual result into a language that computers can understand. But first, we have to provide a framework where we understand it ourselves.
How would you quantify the two images above? I hope you think about this, even if you think it is pointless. It is good to get into the habit. After you’ve done that, pull up your sleeves. This is where we get into the details.
Noise, as a texture in CG, is defined as a function of position coordinates. A position can be a scalar or a vector, depending on if you are working in 1 dimension or >=2 dimensions. For the sake of simplicity, we usually work up to 3 dimensions because we work within a 3D coordinate system, sometimes 4 dimensional coordinates are useful, but the concept works for an N number of dimensions.
This can be simplified to the form: noise(p) = c
p is an input of N dimensions. For example:
1D: x or t
2D: x, y or u, v or t
3D: x, y, z or r, theta, z, or rho, theta, phi for Cartesian, cylindrical, or spherical coordinates respectively… and/or t
The way we represent spatial coordinates doesn’t matter as the important part is that we can sample points in space, but we usually work in Cartesian coordinates. People who work in other coordinate systems tend to want to physically model situations where other coordinate systems are convenient.
But no matter what, the texture retains the same visual quality in space, we just use position coordinates to sample the output of the texture. The output c as defined in my equation above can be a scalar or vector output.
In the context of a black and white 2D image, you can imagine the center as the origin (0, 0), and the output is the grayscale of a pixel from 0-1, which is a single scalar output. It is supremely important to know that, even though we are treating the noise() function as a black box, you should know that it is a continuous function. Know that we are working with an image with discrete resolution, noise is mathematically defined to have infinite resolution. Therefore, you must consider the resolution of whatever is sampling your image texture, because it may not capture all the detail you’d like.
Good! Now we have a definition for the noise as a function. There are two important parameters that noise textures have to art direct them. They are frequency and amplitude.
The concept of frequency is one of the most important ways of quantifying data in the real world (google Fourier analysis and its many applications), and this idea is the primary way of art directing noise textures. Do you remember frequency of a sine wave from trigonometry? Or frequencies of the light spectrum? This is the same idea. A pure sine wave repeats itself identically over space while a noise texture does not, but it still retains an overall ebb and flow.
If you look at noise textures, you might think adjusting frequency is the same as scaling a texture. In a sense, that is happening. But frequency is a more descriptive term, because it implies repetition. In the case of textures, it is defined as repetition with respect to spatial coordinates. How often does this visual ebb and flow of the texture repeat within a fixed amount of space?
Given two noise textures, the first having double the frequency of the other, the first pattern repeats twice as much as the second pattern given the same unit of space.
The second most important parameter is amplitude. In the general form of a sine wave, the amplitude increases the range of values of the output. For example, in the same example of a grayscale 2D image, you can represent the output values to have a range from 0-1 at a defined amplitude of 1. But you can increase the amplitude to an arbitrary amount, like 0-100. The amplitude matters because the values you use from the noise textures can be used to drive any other visual qualities.
This interplay between frequency and amplitude can be seen everywhere in nature.
Here is a video by Inigo Quilez, I link to a section of a video I think you should watch, from 5:26 to 6:00. If you can describe what he is doing with frequency and amplitude in mind, then you are halfway there.
Do you see what he did there? He took multiple 2D noise textures, using the output to map to a heightfield.
Initially, he used a low frequency, high amplitude texture. As he went on, he used higher frequency, lower amplitude textures. He summed them all together to get one final heightfield that looks like a convincing terrain, no?
Everywhere you look you can see this.
As you get further into working with noise, you may notice other settings, such as: turbulence, roughness, octaves, etc. These are all other settings that work to layer noises of different frequencies and amplitudes for you. So as long as you know about frequency and amplitude, you are set to understand most there is about noise.
Here is my overall workflow to working with noise:
i) What kind of input does my noise texture require?
Do I want a 1D, 2D, 3D position coordinate? What am I working with in the context of my problem?
ii) What output does my desired visual problem require?
Do I want a scalar output or a vector output?
iii) Is the range of values of the output of my noise texture physically meaningful for my desired application?
Noise textures sometimes have different ranges of outputs. They can be zero centered (0 +/- amplitude), non-zero centered (constant +/- amplitude), positively valued (0 + amplitude), etc. You can use a ramp or fit function to manipulate this (look these functions up if you have to, they are used all the time in VFX), as well as do simple multiplication or addition operations on the variable to affect the final range of values. Or you could use a multitude of shaping functions to either affect the range or the distribution of values. Or both. It’s on you to art direct them and to look up documentation on what the default settings noise provides you in your choice of software.
If you can answer these three questions, then you can use noise to solve lots of visual problems. The rest is mostly layering on other effects with other noises. In the case of VFX, this also involves variables driven by many other attributes which mask and affect noises in ways that allow them to sum together in order to create one cohesive effect.
Let’s examine three distinct cases.
Case 1: The light of a campfire flickers with respect to time. It’s not completely random, otherwise you will have direct jumps from pure dark to pure light, there is no sense of continuity.
Case 2: The motion of leaves flying in the wind, but with a large, readable frequency. Wind is turbulent, with larger air currents, but their movement may swirl through space with slight jitters with smaller air currents of higher spatial frequency.
Case 3: A grass field has local areas of larger and smaller grass clumps. In addition, larger clumps are more yellow because they are older and are having trouble sustaining themselves. Smaller clumps are greener and lush.
Case 1:
i) I require a 1D input. The flicker depends on time, which moves forwards or backwards.
ii) What I want to model is flickering of light. I can play around with the frequency of the rate my noise texture is sampled by multiplying time by a value with a magnitude greater than or less than 1. I want to output this to an intensity channel on a light object in my scene. This requires a scalar output.
iii) Fire flickers, but it would never lead to pure darkness. I would choose an intensity range that leads to a max brightness that I will visually allow, and a minimum brightness greater than 0.
Case 2: You could use simulation methods to be more accurate, or you could not and try and fake it using noise.
i) I require a 3D input. I want to fake movement of objects translating anywhere in 3 dimensional space.
ii) What I want to model is motion. I want each leaf to translate in 3D space, from one position in time to another position later in time. I require a vector, or 3D output. I also want to model wind in nature, where there is no vacuum, or sinks or sources of wind. Turbulent wind does NOT collapse on itself, or converge in local points in space naturally. Mathematically, this is described as a divergence free field, you have seen this if you’ve studied vector calculus. There are ways to ensure this property is met. Curl noise is a common noise used in computer graphics, where if you use points to trace paths through this vector field, you get entirely smooth motion. Software like Houdini has a node where you can input an arbitrary volume vector field and does some black magic to reproduce a similar field where vectors do not collapse or point away from localized points in space. Isn’t that amazing?
iii) Due to the nature of divergence free fields, it covers the traits I’d want for turbulent motion. I don’t care that the vectors are negatively valued, zero centered values work fine for me. In fact, restricting to a positive domain would restrict you to the first quadrant, which is motion we probably would like to avoid. Just adjust the amplitude so that they move faster or slower to my liking. With more turbulent, frenetic winds, I would increase the amounts of layered noises so there is more higher frequency motion, and increase the amplitude of the noises overall so they move faster.
The caveat with this case is that I want to iterate this addition operation every frame based on the previous frame’s position. Therefore, I would put this in a solver, or some sort of feedback loop.
In addition to a layers of curl noise, if I wanted to push the leaves in a singular direction, I would add a high magnitude, constant vector field in addition to the curl noises, and have that as the primary driver of motion.
Case 3:
i) I would scatter points along ground geometry then use their position to sample a 3D noise texture. Alternatively, you could also sample a 2D noise texture if the scattered points sample the UV coordinates of a ground plane. It’s all about context! Sure, you lose information with only a 2D texture, but ask yourself if that is truly necessary. The more dimensions you add to a texture, the more computing power that texture requires. In the context of grass, let’s define the noise texture and the scattered points to be static in time, so the texture will only need to bake once, but in time dependent situations, it may be a nice little optimization.
ii) For “smaller and larger” grass clumps, assume that I am talking about the scale of a single model that we copy paste and instance on all these points. This would be a scalar output, because I’m using this to drive the uniform scale of the parent model. The same applies to the color variation.
iii) For the scale variance, I would want the noise values to be centered around the value of 1. This is because, maybe I like the original scale of the grass clump. Then I add and substract a constant value to provide minor variation. I explicitly do NOT want a range from 0-1, because I don’t want extremely tiny grass clumps.
For the color shift, we can accomplish this with an operation that applies a hue shift to each instance of grass. In my software of choice, Houdini, it describes one trip around the color wheel at every integer jump. So, an arbitrary hue value h, and an integer value k, added together gives back the same color. Then, I will want to use the same noise texture that drives the scale of each grass clump and manipulate its values so that smaller grass clumps are green and larger clumps are yellow. You can manipulate the output of the original noise texture to add a small value based on the noise output to slightly shift the hue towards yellow or green.
If you are smart about it, you can use the same noise texture to drive multiple visual qualities of this grass field. Isn’t that so cool? Using the same texture can provide so much variation in the visual elements of the scene. Yet, since you are using the same noise texture, you also have this sense of visual unity, in addition to the variety of visual elements.
I chose these three cases because it demonstrates the many applications of noise. It is a general concept that models so many things in nature. Remember, as an artist, you are probably pulling inspiration from nature in some form. And it is much more important to have this quantitative understanding when working with computers and the technical nature of 3D art.
At the end of the day, a noise texture is simply a texture that outputs values. There are many different kinds of noise textures with much different visual identities.
If you have any visual element that looks too uniform, 99% of the time it could be easily fixed by implementing noise blended in some way.
You can manipulate noise textures and its outputs all you want. You can ramp or fit the output instead of having it work along a linear spectrum.
You can quantize the output to stair step the values.
You can stretch the texture in certain directions, you can use it to mask other effects or textures.
You can threshold the output of noise textures to construct a mask (0 or 1 output).
You can use noise with a myriad of blending modes, similar to the way you layer things in photoshop.
You can do a technique called domain warping to warp the position coordinates before you input them into the noise to get cool textures.
This tip is applicable to much more than plugging into a noise texture, rather you can manipulate coordinates into any function that requires position coordinates. Anything!!! And so many things can depend on P!
Even if you are working in a 1D context, you could theoretically sample a line in a 2D or 3D noise texture, just keep the other coordinates constant. Then if you want some variance from this 1D texture, then you can step off that line. The same applies to 2D sampling a 3D texture. And this is why 4D noise textures are relevant, because you can have a 3D noise texture evolve in time, as noise is something that describes continuous, organic randomness along an arbitrary number of dimensions.
Understand that coordinate systems are arbitrary, do not box yourself into thinking of straight lines for axes. You already know this if you’ve worked with UV unwrapping, because we are working with a surface that once had context and positions in 3D space.
What if you worked within the local coordinate system of a curve? Often, curves are parametrized between values of 0 to 1 from the beginning and end of the curve. You could use a 2D or 3D noise texture to sample a line stuck in the 0-1 range along a given axis.
I learned this technique when studying lightning VFX, which I found to be a super cool use of noise. A 3D noise texture outputs a 3D vector, which displaced points along the curve. A sufficient resolution of points along the curve is necessary to capture higher frequency detail. Then, to provide variance between different curves, I varied the other two coordinate axes depending on the curve_id input into a random function, curve_id an attribute I constructed earlier per curve. If you wanted to layer on extra detail and make the coordinates of the curve dependent on the length of the curve, then you can shift coordinate systems to depend on curve length in 3D space instead of a normalized length.
You can do an unlimited number of manipulations to noise, and this is all thanks to the mindset of data driven 3D artistry.
The key point to understand is that this is distinctly different from using pure randomness, because a noise texture has continuity across space. So, even though I have shown you this neat lightning effect, this is only meaningful because the coordinate system across the curve is physically meaningful. This spatial relationship is what is most important. You plug in random numbers for coordinates, you get garbage out.
To sign off, one of the most memorable statements I’ve heard from any of my math professors is, “Are you mathematically happy?” Can you study _put any subject here_ without feeling the dire need to succeed on an exam, or learn skills purely for a practical purpose? Have you lost your ability to learn while having fun? This is something that I’ve talked about with a lot of my peers, and it’s something so much of us learn later in our academic careers. The same followed for 3D art. But then I remember why I love this subject so much, and today I want to share why I find a subject as plain as noise so interesting. How, after months of learning 3D art, a collection of all the learning I’ve gone through expresses the love I have for VFX and 3D art in general.
As a student who originally loved to learn new things and got into the habit of studying just to study, this meant a lot to me. Here’s a video that describes the many applications of noise in VFX. I hope you watch it and think, wow, that’s amazing. I know I did. Then afterwards, it would be a fun exercise to see how you can replicate these situations yourself. But first, just watch and enjoy.
Miscellaneous sources:
Houdini noise documentation (my personal favorite, an incredible amount of settings which reaches the tip of the iceberg when manipulating noise)