Implementation

One fairly important thing to bear in mind here is how to avoid a number of pitfalls. For example, the pseudo-code listed above is pretty rubbish - it performs an excessive number of multiplications! Also, I'll try and give some hints on colouring and a bit of troubleshooting help.

Reducing the number of calculations

For every pixel on the screen, we need to calculate the distance to each blob. This distance is calculated by summing x-difference squared and y-difference squared. The blobs are in the same position as you draw the frame. This means that for every row you draw, you are stuck in the same y coordinate and thus can use the same y-difference squared value. What this means is that a better way to operate is:

  • For each blob:
    • Go through the full range of x-values, calculate the difference between that x and the x coordinate of the particular blob you are looking at. Square it, and save it to a table.
    • Go through the full range of y-values, calculate the difference between that y and the y coordinate of the particular blob you are looking at. Square it, and save it to another table.
    ...then...
  • For each pixel:
    • For every blob, take the x-offset2 and y-offset2 values from the tables you just computed and add them. Multiply all these up.
    • Take the brightest pixel value, subtract our total multiplied distance from it, clip it, draw that pixel.
// First: compute those tables:

// Go through every blob
for i is 1 to number_of_blobs

    // Compute the x-offset2 table:
    for x is 0 to screen_width - 1
        x_offset_table(i, x) = (x - blob(i).x)2
    next x

    // Compute the y-offset2 table:
    for y is 0 to screen_width - 1
        y_offset_table(i, y) = (y - blob(i).y)2
    next y
next i


// Now we have those tables, we can use them to calculate the
// total multiplied distance2 value.
for y is 0 to screen_height - 1
    for x is 0 to screen_width - 1

        // Calculate the multiplied distance2:

        // Set it to one initially.
        // This is so that the first time we multiply it
        // by the first value it becomes that value.

        distance_squared = 1

        for i is 1 to number_of_blobs
            distance_squared =
                distance_squared * (x_offset_table(i, x) + y_offset_table(i, y))
        next i

        // Turn it into a pixel brightness:
        pixel_brightness = 255 - (distance_squared / 8589934592)
        // Notice the HUGE division number!

        // Clip it into the range 0→255:
        if pixel_brightness < 0 then
            pixel_brightness = 0
        end if

        if pixel_brightness > 255 then
            pixel_brightness = 255
        end if

        // Set the pixel on the screen:
        set_screen_pixel(x,y) = pixel_brightness
    next x
next y

Sorry that that's quite a large block! Hopefully it makes sense to you as to why we should calculate the x and y offset tables first.

One thing that has changed since last time is that huge number I divide the the distance_squared by. Why am I doing that?

Let's take a situation where we have 3 blobs and a screen resolution of 400 pixels by 400 pixels. The average x offset will be about 200 as well as the average y offset being about 200. For each blob we take the distance2 as the sum of the x-offset2 and the y-offet2 and multiply them all up. In our case, this results in:

(2002+2002)*(2002+2002)*(2002+2002) = 512000000000000

That is not a small number! Seeing as we need to scale our brightness between 0 and 255, it's a pretty rubbish number to try and use, so we divide it down to a more sensible value - in our case:

512000000000000 / 8589934592 = 59604.64

Crumbs, it still looks to be far too large. Fear not, though - using 200 as the average distance is a pretty poor way to judge this. Let's try somewhere closer to home - a point that is 60 pixels away in x and y coordinates from the centre of all 3 blobs. Using the same sum as above, we get:

(602+602)*(602+602)*(602+602) = 46656000000

46656000000 / 8589934592 = 5.43

...and a value of 5.43 (when subtracted from 255) will give us a nice bright colour. Balancing this value so you get a good range of brightnesses is something that can be done with simple fiddling until it looks right. As for the "magic" value of 8589934592 that I seem to have picked out of the air - it's 233, and dividing by powers-of-two can be done with a bit-level shift.

One huge, nasty problem appears here - having variables chunky enough to hold the gigantic values we're building up. Well, we could divide when building our tables (which loses precision), switch to floating point and divide when building the tables (which loses speed) or just use variable types like DWORD64 - which is more than sufficient for our needs. See the "Troubleshooting" section for more information!


PreviousNextShow All IndexBlob effect demo