Generalized Worley Noise

Worley noise is a type of noise used for procedural texturing in computer graphics. In its most basic form, it looks like this:

That’s ugly and boring, but it’s a quick way to see what the effect looks like. If we use Worley noise to distort a 3D shape, we can get something like a hammered or cratered texture:

Like many procedural textures, it looks a lot better if you repeat the effect a few times with different frequencies:

There are some visual artifacts in these renderings, because they’re using a fast approximation of Worley noise that gives the wrong answer for some values.

That’s not very satisfying, but in order to explain where these artifacts are coming from, we first have to talk about how Worley noise works.

It’s pretty simple: you start with a grid of points.

Then you move each point by some random offset:

When you’re writing a shader, you can’t actually generate random numbers, so we’re using a hash function to produce random-looking offsets based on the logical position of each point (that is, $i = [0 0] for the center point, $i = [1 0] for the point to the right of that, etc).

Finally, once you have the points at random-looking positions, you compute the distance to the nearest point for every individual pixel in your input – and that’s Worley noise.

How do you compute the distance to the nearest point for any pixel you ask about? It’s actually pretty simple: you know that you started with a perfectly even square grid. For any pixel, you can compute the “grid cell” that that pixel falls into ([0 0], [0 1], etc). It’s just the pixel divided by the grid size, rounded to the nearest integer.

And you know that the nearest point is either in this grid, or it’s in one of the immediately adjacent grids, because we only offset our points by at most half the grid size, so each randomly distributed point is still inside its original grid cell. Which means there’s no point inside any other cell that could be nearer than any point in one of the adjacent cells.

So that leaves you nine points to check, for every single pixel in your shader. Here’s the optimization that’s causing visual artifacts: instead of checking all eight adjacent points, only check the three nearest points. The nearest point to your sample position is probably in one of those cells, but it doesn’t have to be, so whenever you get unlucky you wind up with some visual artifacts.

9 points to 4 points is a nice improvement in 2D, but in 3D this optimization takes you from 27 points to 8 points, which can be the difference between realtime and offline rendering.

But notice: this is getting a little bit complicated. And the original code snippet I showed you wasn’t very complicated at all:

Nowhere does that code compute cell coordinates or check for the nearest point. I just constructed this thing, said shape/distance, and somehow that just… gave me the distance to the nearest point.

I was able to do that because Bauble is a playground for making 3D graphics with signed distance functions. Bauble’s whole deal is computing distances to things! And Worley noise is just the signed distance function of a bunch of randomly distributed points. I’m used to thinking of signed distance functions as defining implicit surfaces of 3D shapes, but Worley noise uses the distance as a scalar in its own right.

So.

This is interesting.

What if… we took other signed distance functions, and used them as procedural noise distortions?

We’ll start simple. Instead of points, what if we randomly distribute a bunch of squares?

It’s not obvious that that will be interesting. Let’s look at it in action:

Since we only defined this noise function in 2D, we need a two-dimensional input. That’s a pretty boring 2D input. This is a little more interesting:

We can apply multiple octaves of this, to get… something.

But so far this is not a very interesting effect. What if we vary the orientation as well?

It’s a little bit more random-looking, I guess:

But distorting 3D space with 2D noise is not… it doesn’t look great.

Let’s jump to 3D.

It’s a lot harder to visualize the distance field in 3D. What you’re seeing there is the distance field at the plane that passes through the origin and faces towards the camera – you can click and drag the camera around to take different slices of space. I know it’s not a great visualization, but the point is that this technique generalizes to 3D (even if it’s hard to imagine the distance field at every point in 3D space).

Let’s see how this looks when we use it to distort a 3D shape:

It’s kind of interesting? Definitely better than what we had before. Sort of a faceted gemstone effect.

Do you think our computers will catch on fire if we try multiple octaves of this?

I’m glad you’re still with me.

Let’s trade the boxes for cones:

It’s kind of an interesting pinecone-y texture? I guess?

There are more primitives to try. But of course we don’t have to limit ourselves to primitive shapes.

This is a classic SDF example to demonstrate how easy it is to do constructive solid geometry stuff:

What if… we used that as the basis for our Worley noise?

I think it’s kind of more interesting without the randomization.

We’ve constructed an interesting 3D noise function, and we’re using it to distort 3D space. But of course, we can go back to considering this a “noise texture” in the original sense of the word:

Kinda neat.

The point of all of this is: Worley noise invites us to reconsider signed distance functions as more than implicit surfaces. And since Bauble makes it easy to construct signed distance functions, it’s a good playground for experimenting with textures like this.

Even if we never found anything particularly attractive, it’s fun to play around with space.

If this is your first time seeing Bauble, hey welcome! This post is an expansion of something I briefly talked about in a YouTube video once. The video has many more examples of the sorts of things that Bauble can do. Check it out if this piqued your interest!

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论