The Koch Distance Estimator

There are many types of inspring and beautiful fractals that you can explore once you’ve got a distance-estimate based raymarcher. We’re working up to a discussion of some really funky classes of fractal known as kaleidoscopic IFS fractals, but for today I want to talk about simpler iterative function systems to provide a bit of a primer!

Iterative function systems are the easiest type of fractal to understand and lead to an intuitive shape which can be easily understood. Probably the first fractal of this type I ever saw was the Koch curve. The principle of a Koch curve is beautifully elegant. Take the following set of objects…

Basic iterator for the Koch Curve

Koch Curve iteration 1

And replace each of those objects with the same set of objects scaled to fit…

Second iteration of the koch curve

Koch Curve iteration 2

And again and again and again…

5th iteratoin of the Koch curve

Koch curve iteration 5

The script to generate this out of boxes is pretty simple and looks like the following… remember though that you don’t want to go too far down in the iteration depth because the number of primitives in each iteration is pow(4, iterations)!

rule main {
recursions = 10 // koch iteration *2
koch
}

rule koch
{
{pos.x -= 1/3 kochbar} // left segment
{pos.z += 0.09622504 // middle 2
 {ry -=60 pos.z += 0.09622504 kochbar}
 {ry += 60 pos.z += 0.09622504 kochbar}
}
{pos.x += 1/3 kochbar} // right segment
}

rule kochbar
{
if (recursions < 1)
 {scale *= vec(1/3,1/3,1/30) box}
else
 {scale *= vec(1/3,1/3,1/3) koch}
}

Another iterative fractal that I've covered before is the pythagoras tree which I wrote an article on a *long* time ago. A quick image as a refresher...

Full asymmetric pythagoras tree

Asymmetric Pythagoras tree (3,4,5)

There are lots of fun IFS fractals which you can find from a quick search on google, but the fun here starts to happen when we generalise these IFS fractals into distance estimators. To illustrate the principle let's start with the basic koch snowflake. As described before this is a sequence of 4 objects tied together to make the distinctive angle shape.

With distance estimators we can actually produce this shape by folding 3d space around a single object.

Let's start with a small bar that starts at -1 and continues to -0.3333 (-1/3rd). We're going to fold space back onto this primitive to build the basic shape of the koch curve. Let's start then with the basic primitive...

Koch distance estimator, part 1

koch DE part 1

rule main {
distancefunction(koch)
distance
}

shader koch {
diff = pos
distance = box(diff, vec(0.3333,0.1,0.1), vec(-0.66666,0,0))
}

First off lets fold space along the point from -0.3333,0,0, in the direction of -0.8666,0,-0.5 (this effectively folds space around the angle of the triangle leading to the following simple shape.

Second part of constructing the koch curve distance estimator

koch DE part 2

shader koch {
diff = pos
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
distance = box(diff, vec(0.3333,0.1,0.1), vec(-0.66666,0,0))
}

Next up we're going to fold space again, this time along the centre line to form the full basic koch primitive.

3rd step in constructing the koch curve distance estimator

koch DE part 3

shader koch {
diff = pos
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
distance = box(diff, vec(0.3333,0.1,0.1), vec(-0.66666,0,0))
}

Now lets be a bit perfectionist and tidy up the messy corners. I'm just picking random numbers to make sure the the apex of the triangle is complete and these numbers fit. I'm sure I could do the trigonometry to work out the exact values, but it's late and I want to finish this article!

4th part of constructing the koch curve distance estimator

Koch DE part 4

shader koch {
diff = pos
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
distance = box(diff, vec(0.5,0.1,0.1), vec(-0.66,0,0))
}

So I'm happy with that as the basic DE, but now it's time to do the iterative bit of an IFS fractal. The folds that were carried out above are going to be used again to make the fractal, but for that to lead to a fractal shape we have to start by "shrinking" the existing segment to be 1/3rd the size. This is achieved by *multiplying* space by a factor of 3. Because we've scaled space, we also need to divide our distance estimate by the same factor (3).

Resizing the koch curve ahead of the iteration

Koch DE part 5

shader koch {
diff = pos
diff = (diff+vec(0.6667,0,0))*vec(3,3,3)
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
distance = box(diff, vec(0.5,0.1,0.1), vec(-0.66,0,0))/3
}

And now we're ready to apply the folds again to iterate the fractal.

Iterating the koch curve to iteration 2

Koch DE part 6

shader koch {
diff = pos
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
diff = (diff+vec(0.6667,0,0))*vec(3,3,3)
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
distance = box(diff, vec(0.5,0.1,0.1), vec(-0.66,0,0))/3
}

Beautiful. Now my shader language doesn't have a repeat function (yet), so to iterate the fractal multiple times I'm just going to repeat the fold and scale instructions until we're at iteration 4 of the fractal. Note that we're now dividing the distance estimate from the final call by 81 (3 to the 4).

Iteration 4 of the koch curve fractal

Koch DE part 7

shader koch {
diff = pos
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
diff = (diff+vec(0.6667,0,0))*vec(3,3,3)
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
diff = (diff+vec(0.6667,0,0))*vec(3,3,3)
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
diff = (diff+vec(0.6667,0,0))*vec(3,3,3)
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
diff = (diff+vec(0.6667,0,0))*vec(3,3,3)
diff = singlefold3d(diff, vec(0,0,0), vec(-1,0,0))
diff = singlefold3d(diff, vec(-0.3333,0,-0), vec(-0.866,0,-0.5))
distance = box(diff, vec(0.5,0.1,0.1), vec(-0.66,0,0))/81
}

I'll leave converting this DE into the koch snowflake to the reader, but in case you want to know how it looks, here goes!

Raytraced image of the full koch snowflake

Full koch Snowflake

This basic DE is a simple way of introducing the principles that we're going to need to understand kaleidoscopic IFS fractals, which are coming up in the next article! If you'd like to play with the scripts above just click download at the top of this page to pick up the latest version of Wooscripter.

For updates and all the latest news on the progress of wooscripter, follow my twitter feed at @dom767.

You may also like...

3 Responses

  1. Vladimir says:

    Hi, Dom!
    Not working for me singlefold3d…. This new option, which I do not have? (((((
    http://i60.fastpic.ru/big/2015/0428/59/ea5a863c18c087e8960820cffdc69459.jpg

  2. Dom Penfold says:

    Apologies, I forgot to upload a new version of Wooscripter that supports this function!! Added now.

  3. Vladimir says:

    Hi, Dom!
    Thanks, now it works!

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>