Fundamentals of Fractals 3: The Sierpinski gasket

image

The Sierpinski gasket is a fractal and a very basic fractal of self-similar sets, a mathematically generated pattern with similar patterns. The Sierpisnki gasket is typically generated using triangles, but you can render any shape you want in the space, and it’s an example of an iterated function system (IFS).

The Sierpinski gasket can be generated in many different ways. You can create a triangle, and cut out the parts that that’s not part of the Sierpinski gasket, or draw dots that after many plots, fill out the entire thing.
image_thumb3

 

image

Programming your first fractal using dots
Implementing the Sierpinski gasket using dots is actually very simple. All you need to know is how to divide! You draw dots many at given positions, using the previous position as the input to the next position.
image

So, each point Sk is based in the previous point Sk-1.

Next we need to know what the function f(x) does, except for returning the coordinate of a point inside the Sierpinski gasket.
1. First of all, you need to select three points that form a triangle of any sort. These three points is defining the edges of the triangle:
image

Here we define point P0, P1 and P2 in a 2d coordinate system.

2. Next, use a random function to select any of the three points as a starting point. Let’s name this Pstart.

3. Create the next point Sk as the midpoint between a random edge point P and the previous point Sk-1:
image

4. Draw Sk.

5. Jump to step 3

After a few iterations, this is what you will see:
image

S1 is the midpoint between P0 and P1, S2 is the midpoint between S1 and P2, and S3 is the midpoint between S2 and P1.

Implementing the Sierpinski gasket in DirectX 11
First, we need to define the edges of the Sierpinski gasket:

XMFLOAT2 EdgePoints[3];
EdgePoints[0].x = 0;
EdgePoints[0].y = 100;

EdgePoints[1].x = -100;
EdgePoints[1].y = -100;

EdgePoints[2].x = 100;
EdgePoints[2].y = -100;

Here, we create a triangle that is looking like the one in the figure above, it’s 200 wide and 200 high, ranging from –100 to 100 on both the x- and the y-axis.

Next, we need to select one of the edgepoints P at random:

int index = rand()%3;
XMFLOAT2 StartPoint = EdgePoints[index];
SierpinskiPoints[0] = StartPoint;

In the code above, we use a random-function as the lookup value into our edge points, grab the coordinate and store it in an array named SierpinkiPoints as the first element. This array will contain all our points, including the start coordinate. This means that this array will need to have the same size as how many dots we want to draw our Sierpinski gasket with.

Then we need to calculate the rest of the points:

for(int i = 1; i < Iterations; i++)
{
    index = rand()%3;
    StartPoint.x = (StartPoint.x + EdgePoints[index].x)/2.0f;
    StartPoint.y = (StartPoint.y + EdgePoints[index].y)/2.0f;
    SierpinskiPoints[i] = StartPoint;
}

Here, we will loop through step 3 to step 5 “Iterations” number of times, starting from element 1 as 0 is the starting point we created earlier. Then we select another random starting point and calculate the midpoint between the previous point that exist in StartPoint and the randomly selected EdgePoint. After that, we store our point in the SierpinskiPoints array and continue with the next point until all Iterations have been processed.
And that’s it, all we need to do now is do render all the points in the SierpinskiPoints array. But first, let’s take a look at the entire function we just wrote:

// Sierpinski gasket
const int Iterations = 20000;
XMFLOAT2 SierpinskiPoints[Iterations];

void BuildSierpinski()
{
    XMFLOAT2 EdgePoints[3];
    EdgePoints[0].x = 0;
    EdgePoints[0].y = 100;

    EdgePoints[1].x = -100;
    EdgePoints[1].y = -100;

    EdgePoints[2].x = 100;
    EdgePoints[2].y = -100;

    int index = rand()%3;
    XMFLOAT2 StartPoint = EdgePoints[index];
    SierpinskiPoints[0] = StartPoint;

    for(int i = 1; i < Iterations; i++)
    {
        index = rand()%3;
        StartPoint.x = (StartPoint.x + EdgePoints[index].x)/2.0f;
        StartPoint.y = (StartPoint.y + EdgePoints[index].y)/2.0f;
        SierpinskiPoints[i] = StartPoint;
    }

}

I’m using a square as the dot that will be rendered at every point in the SierpinskiPoints array.

// Clear the back buffer
float ClearColor[4] = { 0.33f, 0.4235f, 0.0627f, 1.0f }; // red,green,blue,alpha
g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
g_pImmediateContext->ClearDepthStencilView( g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0 );

// Render our startpoint
g_World1 = XMMatrixScaling( 0.5f, 0.5f, 0.5f ) * XMMatrixTranslation( SierpinskiPoints[0].x, SierpinskiPoints[0].y, 0.0f );

CBShaderParameters cb1;
cb1.mWorld = XMMatrixTranspose( g_World1 );
cb1.mView = XMMatrixTranspose( g_View );
cb1.mProjection = XMMatrixTranspose( g_Projection );
g_pImmediateContext->UpdateSubresource( g_pCBShaderParameters, 0, NULL, &cb1, 0, 0 );

// Render a triangle
g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 );
g_pImmediateContext->VSSetConstantBuffers( 0, 1, &g_pCBShaderParameters );
g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 );
g_pImmediateContext->PSSetConstantBuffers( 0, 1, &g_pCBShaderParameters );
g_pImmediateContext->UpdateSubresource( g_pCBShaderParameters, 0, NULL, &cb1, 0, 0 );
g_pImmediateContext->Draw( 3, 0 );

for(int i = 1; i < Iterations; i++)
{
    g_World1 =  XMMatrixScaling( 0.5f, 0.5f, 0.5f ) * XMMatrixTranslation( SierpinskiPoints[i].x, SierpinskiPoints[i].y, 0.0f );

    CBShaderParameters cb1;
    cb1.mWorld = XMMatrixTranspose( g_World1 );
    cb1.mView = XMMatrixTranspose( g_View );
    cb1.mProjection = XMMatrixTranspose( g_Projection );
    g_pImmediateContext->UpdateSubresource( g_pCBShaderParameters, 0, NULL, &cb1, 0, 0 );

    // Render a triangle
    g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 );
    g_pImmediateContext->VSSetConstantBuffers( 0, 1, &g_pCBShaderParameters );
    g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 );
    g_pImmediateContext->PSSetConstantBuffers( 0, 1, &g_pCBShaderParameters );
    g_pImmediateContext->UpdateSubresource( g_pCBShaderParameters, 0, NULL, &cb1, 0, 0 );
    g_pImmediateContext->Draw( 6, 0 );
}

All we do here is to first transform our first quad to the first point in the SierpinskiPoints array, the starting point, and draw the quad at this position. Next we iterate through the entire array, transforming and drawing each quad at it’s position in the Sierpinski gasket.

The result will be a Sierpinski gasket created entirely with dots:

image

You could also create this in a pixel shader, where you evaluate if every point on the screen is inside the Sierpinski or not.

Download: Source and executable (DirectX11, Visual Studio 2010)

Any feedback is very welcome, and please tell me if there are any mistakes here!

About these ads
This entry was posted in DirectX11, Fractal, Graphics, Math, Tutorial. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s