Noisy distance estimators

If you don’t know what a distance estimator is, you’ll find some quick introductions here. Having discussed folding space in some detail, I’d like to go over some interesting uses of perlin noise that you can apply to simple distance estimation functions.

This approach allows you to do full 3d displacement mapping on standard raytracing primitives. 3d displacement mapping is one of the things that we used to consider a holy grail of rasterisers when I worked in games. The problem is that bump mapping is a pixel operation, but distorting objects modifies the vertices, which in rasterised graphics is usually pretty expensive. The world of raytracing is quite different though. Truly displaced objects have been possible for a long time with raymarching algorithms searching along the intersection point for 3d displacements. However, distance estimation functions make 3d displacement embarrassingly easy.

First up, what is perlin noise? Basically it’s a form of random noise which is classed as coherent noise. This means that neighbouring samples will vary a little, while distant samples will vary randomly. Perlin noise is easy to generate and has been used for many years to generate procedural textures etc., in fact, ever since Ken Perlin came up with it back in the 90s (or at least formally documented it).

Let’s start up with a single octave of perlin noise. This simple picture shows random variables interpolated using a cosine interpolator to give smooth values. You can see that this basically looks like repeating white noise with some blurring over top.

Basic perlin noise, single octave

Perlin noise, octave 1

Strictly speaking this is already coherent noise within a certain interpretation, but we can make this texture look a lot more interesting by adding another layer of interpolated noise overtop, this time at half the wavelength (and with half the intensity).

Basic perlin noise, two octaves

Perlin noise octave 2

So lets add some more octaves to this noise and see what we get.

Basic perlin noise, 8 octaves

Basic perlin noise, 8 octaves

Now you can see why people use this a lot. A lot of landscape terrain generators start with this type of noise function, and if you look at things in nature like the edges of clouds, you can see similarities.

Applying perlin noise to a simple cube gives you a procedural texture on top of a basic flat surface. Note that we still have very hard and straight edges.

Cube with perlin noise

Cube with perlin noise

But what about if we render a cube using a distance estimation function, and then we factor perlin noise into the distance estimator? A very basic way to do this is to mix perlin noise into the dimensions of the box we’re testing against in the distance estimator. In Wooscript the shader looks like this. (note you’ll need the latest version of Wooscripter to be able to use the diffuse register as a temp store…)

rule main {
scale = vec(8,8,8)
distancefunction(perlindisplacement)
distance
}

shader perlindisplacement
{
diffuse = vec(add(0.9,mul(perlinxyz(pos, 1, 1.0, 5, 8, 0.5),0.05)), 0, 0)
distance = box(pos, vec(getx(diffuse),getx(diffuse),getx(diffuse)), vec(0,0,0))
}

And the rendered image suddenly has full 3d displacement.

Cube with perlin displacement

Cube with perlin displacement

This is very neat, but why does it work? Let’s go back to the origins of perlin noise. Perlin noise is a form of coherent 3d noise, it means that the values are continuous, i.e. not discontinuous, and therefore they can be used within distance estimation functions. You have to bear in mind though that there are two things that we’d expect from a typical distance estimator.

1. Continuous function
2. Represents the minimum distance to the surface

Once we start putting perlin noise into the function we break that second constraint. Going back to a standard distance estimator, a sample is taken for the distance to the object, the ray is stepped forwards this amount, and another estimate is calculated.

If constraint 2 doesn’t hold then we can’t step the whole estimate along the ray. Instead we need to multiply the estimate by a stepsize which takes into account the potential overestimate. In Wooscripter you can do this by setting

stepsize=0.8 // or whatever fudge factor is appropriate

Guessing the full fudge factor is dependent on the variance within the noise function you’re using. With this version of perlin noise 0.8 will do fine, but if we turn up the amount of perlin noise we’re using, we’ll need to reduce the stepsize down.

For the following render I’ve ramped up the noise level and I’ve tuned the stepsize down to 0.1 to capture the detail. I’ve also ramped the distanceiterations up to 2000 as it can take longer for these smaller step sizes to find the surface.

Cube with noisy displacement

Cube with noisy displacement

This approach also works nicely when render other basic primitives. Wooscript supports a sphere() distance estimator, and it’s trivial to blend perlin noise into the radius value. In this case we get a lovely red cube with a 3d displaced surface.

shader perlinsphere
{
diff = vec(0.5+perlinxyz(pos, 1, 1.0, 5, 5, 0.5)*0.5, 0, 0)
distance = sphere(pos, vec(0,0,0), diff.x)
}
Sphere with perlin displacement

Sphere with perlin displacement

We can also ramp the level of noise right up giving us some very interesting looking fragmented objects like this green thingy.

sphere with perlin noise

sphere with perlin noise

In this case we’ve almost lost the fact that we’re raytracing a sphere completely. Because perlin noise is varying across 3d space, we’ve also ended up with a 3d shape with concave surfaces carved into it. To avoid this and just get a noisy sphere we can change our DE to only sample the noise function in the unit sphere.

shader perlinsphere
{
diff = vec(0.5+perlinxyz(normalise(pos), 1, 1.0, 5, 5, 0.5)*0.5, 0, 0)
distance = sphere(pos, vec(0,0,0), diff.x)
}

Now we get a far more interesting exploding sphere object.

sphere with normalised perlin noise

sphere with normalised perlin noise

We can also extend this idea to things like toruses. Take a look at this example where we’ve modified the outerradius by a perlin noise function that’s normalised to the unit sphere. In this case we get a tube of varying radius. Cool!

torus with normalised perlin noise added to outer radius

torus with normalised perlin noise added to outer radius

Finally, let’s take a look at a CSG object where a standard spherical shell has a perlin jelly subtracted from it…

shader perlinsphere
{
diff = vec(0.7+perlinxyz(normalise(pos), 1, 1.0, 14, 5, 0.5)*0.5, 0, 0)
distance = max(max(sphere(pos, vec(0,0,0), 0.7),
neg(sphere(pos,vec(0,0,0), 0.65))),
neg(sphere(pos, vec(0,0,0), diff.x)))
}

Perlin sphere cutaway from spherical shell

Perlin sphere cutaway from spherical shell

Hopefully this has given you some inspiration to try things out for yourself. Wooscripter v1.0 is released in tandem with this article and now supports material shaders along with perlin noise in distance estimators. Download it here, or follow me on twitter (@dom767) for all the latest renders.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

Spam Protection *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>