Sol's Graphics Tutorial

11 - Kefrens Bars

Let's do a classic Amiga effect.

Take a copy of the previous chapter, resulting in ch11.

Now, I wasn't there, nor do I know the exact history, but this effect went something like this. The Amiga hardware, like its contemporary PC hardware, was relatively low-powered and just going ahead and drawing the whole screen every frame was something you didn't do.

But you could trick the hardware in various ways. In the case of this effect, you could reset the address the screen was read on every scanline, creating a single-line framebuffer. Anything on it would be copied down the screen. While the display was updated you had a little bit of time to update said framebuffer, and every change you did would then get copied downwards.

Let's start with this render() function:

void render(Uint64 aTicks)
{
  for (int i = 0; i < WINDOW_WIDTH * WINDOW_HEIGHT; i++)
    gFrameBuffer[i] = 0xff000000;

  int* prev_line = gFrameBuffer;
  for (int i = 0; i < WINDOW_HEIGHT; i++)
  {
      int* curr_line = gFrameBuffer + i * WINDOW_WIDTH;
      memcpy(curr_line, prev_line, WINDOW_WIDTH * sizeof(int));
      
      curr_line[i] = 0xff0000ff;

      prev_line = curr_line;
  }
}

Compile and run.

Here we first clear the screen to black, and then, line by line, first take a copy of the previous line, and then alter the current line a bit. In our case, we plot a single pixel every time.

The way the prev_line an curr_line work is that in the first iteration, we just copy the first line over itself, and after that on each iteration we copy the previous one.

Even though we only plot a single pixel each line, the result looks like this:

How do we leverage this to make it look nicer? By drawing a few pixels we can create columns.

Let's first replace the single pixel change with this:

for (int j = 0; j < 16; j++)
{
  curr_line[(j) + WINDOW_WIDTH / 2] = j * 15 | 0xff7f0000;
  curr_line[(j + 16) + WINDOW_WIDTH / 2] = (16-j) * 15 | 0xff7f0000;
}

Compile and run. Okay, that's one column. Next we'll need to adjust the horizontal offset on every line to make use of it. Replace the code you just pasted with this:

int xoffset = WINDOW_WIDTH / 2 + sin((i + aTicks * 0.01) * 0.03) * (WINDOW_WIDTH / 2 - 32);

for (int j = 0; j < 16; j++)
{
  curr_line[(j) + xoffset] = j * 15 | 0xff7f0000;
  curr_line[(j + 16) + xoffset] = (16-j) * 15 | 0xff7f0000;
}

Compile and run. That's more like it. All we have left to do is to complicate the xoffset curve through a few more sine curves:

int xoffset = sin((i + aTicks * 0.01) * 0.03) * 
              sin((i + aTicks * 0.02) * 0.04) * 
              sin((i + aTicks * 0.03) * 0.05) * 
              (WINDOW_WIDTH / 2 - 32) +
              WINDOW_WIDTH / 2;

for (int j = 0; j < 16; j++)
{
  curr_line[(j) + xoffset] = j * 15 | 0xff7f0000;
  curr_line[(j + 16) + xoffset] = (16-j) * 15 | 0xff7f0000;
}

Compile and run. We're getting somewhere! And finally, to draw a few different colored columns:

for (int k = 0; k < 3; k++)
{
  int xoffset = sin((i + aTicks * 0.01 * k) * 0.03) * 
                sin((i + aTicks * 0.005 + k * 100) * 0.04) * 
                sin((i + aTicks * 0.0025) * 0.05) * 
                (WINDOW_WIDTH / 3 - 32) +
                k * WINDOW_WIDTH / 6 +
                WINDOW_WIDTH / 3;

#define ROT(a,b) ((((a) << (b)) | ((a) >> (24 - (b)))) & 0xffffff)

  for (int j = 0; j < 16; j++)
  {
    curr_line[(j) + xoffset] = ROT(j * 15 | 0x3f0000, k*8) | 0xff000000;
    curr_line[(j + 16) + xoffset] = ROT((16-j) * 15 | 0x3f0000, k*8) | 0xff000000;
  }
}

Compile and run. The various values and multiplications are there as a result of a few minutes of experimentation, and don't have any special significance. Feel free to tweak the values and see the result.

The only interesting bit here is the ROT macro. It rotates the bottom 24 bits of a 32 bit value by given number of bits. In this case we rotate BGR to GRB to RBG for our three wiggly walls.

And that's it for this bit.

Additional things to try:

  • Play around with the magic numbers
  • Alter the width of the columns
  • Animate the width of the columns
  • Redo the colors, animating the colors
  • What if we blend the columns instead?
  • Load a small picture and use its different scanlines as source of the columns

Next up: 12 - Infinite Sprites

Any comments etc. can be emailed to me.