-
Notifications
You must be signed in to change notification settings - Fork 2
Box blur
Before reading this entry, it is recommended taking a look at color concept explanation, since box blur is pretty much continuation of concepts presented there. This wiki still links to part of color concept explanation pages when it is really necessary to know something from previous entries. Moving on the task at hand, what is box blur? Rather than displaying particles bare, a box blur effect is used to mask particle traveling to make them appear even more smooth and have particle leave nice looking trail effect behind them while they move. Again picture, or this case moving pictures, are worth than thousand words. Here is particles moving without any blurring effect:

Here is particles first moving rapidly with blurring effect and some slow motion added to the mix at the end:

As can be seen without blurring particles appear rather dull, just moving around slowly but with blurring they look rather speedy and frankly awesome. There is a link to Wikipedia article that explains how box blur is done with pseudocode and what it actually does. This document will still explain it from the perspective of C++ and how it is implemented to The Particle Fire Simulation Revision -program. Implementation begins with these two loops, which are called resolution loops from now on:
for (int y = 1; y < screen_height_ - 1; y++) {
for (int x = 1; x < screen_width_ - 1; x++) {These loops go through each pixel of the current resolution and adds blur effect to these pixels. Because of the nature of box blurring algorithm, the very first and the very last pixel of each axis are not needed to be included. The way it is implemented is to start going through y and x pixels from value 1 rather than 0 and instead of going through every pixel on the height- and width- variables, -1 is added to to end. When the loop starts Y is at 1 and X is at 1 so the starting point is at one pixel down the top and one pixel right from the left corner of the screen. From there the loop moves to right until second to last pixel of the x-loop reached. A new y-loop begins and the same is done until EVERY pixel of resolution is visited. To best express this, here is similar table that was used in Setting up the pixel buffer example in resolution of 800 pixels in width and 600 pixels in height:
| 🡇 Height | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ | ------ |
|---|---|---|---|---|---|---|---|---|---|
| Width 🡆 | X:0 | X:1 | X: 2 | X:.. | X:400 | X:.. | X:798 | X:799 | X:800 |
| Y:0 | No Blur | No Blur | No Blur | No Blur | No Blur | No Blur | No Blur | No Blur | No Blur |
| Y:1 | No Blur | Blur | Blur | Blur | Blur | Blur | Blur | Blur | No Blur |
| Y:2 | No Blur | Blur | Blur | Blur | Blur | Blur | Blur | Blur | No Blur |
| Y:.. | No Blur | Blur | Blur | Blur | Blur | Blur | Blur | Blur | No Blur |
| Y:300 | No Blur | Blur | Blur | Blur | Blur | Blur | Blur | Blur | No Blur |
| Y:.. | No Blur | Blur | Blur | Blur | Blur | Blur | Blur | Blur | No Blur |
| Y:588 | No Blur | Blur | Blur | Blur | Blur | Blur | Blur | Blur | No Blur |
| Y:599 | No Blur | Blur | Blur | Blur | Blur | Blur | Blur | Blur | No Blur |
| Y:600 | No Blur | No Blur | No Blur | No Blur | No Blur | No Blur | No Blur | No Blur | No Blur |
As can be seen on the table above, rather than adding the effect to whole screen, the outer edges of the are excluded. Think of this like a blurring screen within a graphic screen. Reason for this is explained next, when the new set of loops is initiated. Inside the resolution loops three new integer variable are created and initialized with value 0:
int red_total(0);
int green_total(0);
int blue_total(0);These variables are designed to hold the total of whopping nine different pixel’s color values by channel. If not familiar how RGB-color and color channels work refer to this wiki page, which has nice example and explanation of colors when working with C++. After color variable initialization, the program goes through yet another two loops:
//using #include <stdint.h> to make sure that variables are fixed size
for (int col = -1; col <= 1; col++) {
for (int row = -1; row <= 1; row++) {
int current_y(y + col);
int current_x(x + row);
uint32_t color(pixel_buffer[current_y * screen_width_ + current_x]);
//program continues...These loops and the syntax inside them go through the each neighboring pixel and calculates the average color values of them later in the program. These loops are known as blurring loops from now on. Blurring loops and the way they work is best represented with following table:
| rows 🡆 cols 🡇 | -1 | 0 | 1 |
|---|---|---|---|
| -1 | Pixel that is one pixel up and left from the current | Pixel that is one pixel up from the current | Pixel that is one pixel up and right from the current |
| 0 | Pixel that is one pixel left from the current | 🢄 🢁 🢅 🢀Current pixel position🢂 🢇 🢃 🢆 |
Pixel that is one pixel right from the current |
| 1 | Pixel that is one pixel down and left from the current | Pixel that is one pixel down from the current | Pixel that is one pixel down and right from the current |
While inside the blurring loops, current_y and current_x -variables are used to access eight neighboring pixels by taking one step to each direction on a two dimensional plane, and saving the color value of that pixel to variable known as color. To further demonstrate here is another table with real pixel values. The same 800 pixels in width and 600 pixels in height are used but this time the resolution loops have iterated to middle of the screen, meaning that y is at value 300 and x at 400. Blurring loops are then gaining access following pixel values:
| Width(X) 🡆 Height(Y) 🡇 |
-1 | 0 | 1 |
|---|---|---|---|
| -1 | Y:299 X:399 | Y:299 X:400 | Y:299 X:401 |
| 0 | Y:300 X:399 | Y:300 X:400 | Y:300 X:401 |
| 1 | Y:301 X:399 | Y:301 X:400 | Y:301 X:401 |
So to recap when the blurring loop begins col and row are both at -1 value, meaning that the table’s leftmost top pixel position at Y:299 and X:399 is accessed. The pixel access itself happens at uint32_t color(pixel_buffer[current_y * screen_width_ + current_x]); -syntax. To better understand how pixel buffer works, please refer to Setting up the pixel buffer. To continue with Y:299 and X:399 example, since current_y, which is 299, is multiplied by screen_width, which is in this case 800, equals to 239200 and when current_x value of 399 is added to that value total equals to 239599. Meaning that same pixel value that is inside pixel buffer array element 239599 is saved to color variable. This color variable is then “dismantled”. This is the opposite of combining color channels, which is explained in the wiki. Rather than combining different color channel values to one conversely this time color value from pixel buffer is broke in the own channels. Code syntax for this is following:
//Still using #include <stdint.h> to make sure that variables are fixed size
uint8_t red(color >> 16);
red_total += red;
uint8_t green(color >> 8);
green_total += green;
uint8_t blue(color);
blue_total += blue;Using the same bitwise shifting presented in combining color channels concept, color bits are shifted but this time instead of right to left from left to right. To keep it simple, the example presented here uses same value that was achieved at combining color channels:
0000 0000 0101 1111 1111 0011 0010 1100
What is needed to be extracted from this is the red channel which is located between |-pipes:
0000 0000 |0101 1111| 1111 0011 0010 1100
When this code example is run
uint8_t red(color >> 16);
red_total += red;Bits are shifted to right by starting from the leftmost bit by amount of 16 bits resulting the wanted red channel to moving to correct position:
0000 0000 0000 0000 0000 0000 |0101 1111| <1111 0011 0010 1100>
The rest of the bits are presented between <>-symbols to represent how the bits are moved. In real life scenario those bits are not even there and they are discarded because they are after the least significant bit (the bit that has the decimal value of 1 when it is active). Resulting value is then saved to variable called red and that value is added to red_total that was previously initialized to zero value. Rest of the color channels have the same kind of treatment, meaning that green is located here:
0000 0000 0101 1111 |1111 0011| 0010 1100
and bits are moved to right by 8 bits:
uint8_t green(color >> 8);
green_total += green;resulting:
0000 0000 0000 0000 0101 1111 |1111 0011|
Since green is uint8_t variable it can only hold 8 bit values only the first 8 bits are saved to that variable, meaning that anything after the 8th bit are discarded and not stored. Finally since blue channel is located at the end, there is no need to shift any bits and value is simply added to blue variable. After that another run of blurring loop is done, this time table’s top middle pixel position at Y:299 and X:400 is accessed. Blurring loop is run until all the neighboring pixels have been visited.
After the blurring loop is done program is at the end of resolution loop. Following algorithm is executed to make the blur effect happen.
uint8_t red(red_total / 9);
uint8_t green(green_total / 9);
uint8_t blue(blue_total / 9);
Why is the final result divided by 9 then? To quote Wikipedia’s Box blur-article:
... in which each pixel in the resulting image has a value equal to the average value of its neighboring pixels in the input image.
Since blurring loop saves 8 neighboring pixels plus the current pixels color channel values to red_total, green_total and blue_total -variables, the average of these is calculated by dividing them by 9. Then final step is to draw these blurred elements to screen. Since this entry is general purpose so this wiki will not go into detail how exactly use SDL-library to draw elements on the screen. For that it is suggested to take a look at Pixel setting method of Screen class and also how the box blur is implemented on Box blurring method of Screen class of the project and of course the at the source code itself at repo page.