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.

**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.

So, each point S_{k} is based in the previous point S_{k-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:

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 P_{start}.

3. Create the next point S_{k} as the midpoint between a random edge point P and the previous point S_{k-1}:

4. Draw S_{k}.

5. Jump to step 3

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

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:

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!