Pixelate:Issue 14/Stars
From Allegro Wiki
| Stars | |
| Original author: | SteveT |
|---|---|
| Website: | |
| zip: | stars_src_w32bin.zip |
Stars
Stars effect
This tutorial was inspired by the NeHe Lesson 9 which was entirely in OpenGL. After watching the demo from the NeHe lesson I thought it would be neat to attempt this using Allegro and a pure software render using sin and cos. The code is rather simple but the effect it produces is rather nice. The first thing that we need to set up to achieve this effect is set up our stars. Each star is a vector or point in space derived from an angle and distance from the center of the screen.
typedef struct // Create A Structure For Star
{
int r, g, b; // Stars Color
float dist, // Stars Distance From Center
angle; // Stars Current Angle
}
stars;
The algorithm to achieve this effect is rather simplistic. The idea is that each star has a random distance and angle from the center of the screen, as the stars angle increases, they will move in a circle, however the distance is decreased and the speed is increased of each star as time progresses. When the star reaches the center of the screen it is reset to the outer radius and given a new random color. This gives us the effect we are trying to achieve.
Here is a simplified demonstration:
#define MAX_RADIUS 200
#define NUM_STARS 50
stars[NUM_STARS];
// Initialize our stars
for (loop=0; loop < NUM_STARS; loop++){
// Initial angle to 0
star[loop].angle = 0.0f;
// Initial distance proportional to position in array
star[loop].dist = (loop / (float)NUM_STARS) * MAX_RADIUS;
// Random color
star[loop].r = rand() % 256;
star[loop].g = rand() % 256;
star[loop].b = rand() % 256;
}
// Main loop logic
for (loop=0; loop < NUM_STARS; loop++){
// Set up coordinates and color
int x = (SCREEN_W >> 1) + (star[loop].dist * cos(star[loop].angle));
int y = (SCREEN_H >> 1) + (star[loop].dist * sin(star[loop].angle));
int col = makecol(star[loop].r, star[loop].g, star[loop].b);
// Draw the star with a 16 pixel radius
circlefill(buffer, x, y, 16, col);
// Increment the angle based on the distance from the center (closer is faster)
star[loop].angle += loop / (float)NUM_STARS / speed;
// Decrement the distance from center
star[loop].dist -= 0.8f;
// Star has hit dead center
if (star[loop].dist <= 0.0f){
// Reset distance to MAX_RADIUS
star[loop].dist += MAX_RADIUS;
// Randomize star color
star[loop].r = rand() % 256;
star[loop].g = rand() % 256;
star[loop].b = rand() % 256;
}
}
That's all there really is too it, however I didn't stop there because that gives a rather bland representation of the NeHe Lesson. Because the NeHe Lesson used OpenGL it had the advantage of using GL_Textures and GL_Quads, both of which aren't available in Allegro. To achieve this effect I added a BITMAP structure to my stars stucture to hold a picture of each star. Fblend assisted with transparency but Fblend doesn't have the capability of drawing grayscale images with color so I had to artificially color each star manually as a separate bitmap. This was achieved by taking the grayscale image as a base and the stars random color and using them to generate a second full color star.
These are the changes needed:
typedef struct // Create A Structure For Star
{
BITMAP *bmp // Stars bitmap
int r, g, b; // Stars Color
float dist, // Stars Distance From Center
angle; // Stars Current Angle
}
stars;
// Initialize our stars
for (loop=0; loop < NUM_STARS; loop++){
// ... Same initialization from earlier
// Individual bitmap for the star, this is because we need separate colors for each
star[loop].bmp = create_bitmap(star_img->w, star_img->h);
// Color in our star using the grayscale image supplied
for(y = 0; y < star_img->h; y++){
for(x = 0; x < star_img->w; x++){
col = _getpixel16(star_img, x, y);
r = getr(col) * (star[loop].r / (float)256);
g = getg(col) * (star[loop].g / (float)256);
b = getb(col) * (star[loop].b / (float)256);
_putpixel16(star[loop].bmp, x, y, makecol(r, g, b));
}
}
}
This gives each star a full color image for fblend to transparently blit to the screen.
Secondly I wanted to add some spin to the stars which the origional NeHe Lesson supported. This was achieved by using rotate_sprite onto a temporary bitmap. Fblend was then used to transparenly draw the temporary bitmap to the screen. A new spin variable was introduced that is incremented each logic cycle, this is how far the star is rotated on the temporary bitmap.
// Main loop logic
for (loop=0; loop < NUM_STARS; loop++){
// ... Same setup from earlier
// Rotate our star image onto a temporary image
rotate_sprite(tmp_img, star[loop].bmp, 0, 0, ftofix(spin));
// Additive blend
fblend_add(tmp_img, buffer, x, y, 200);
}
// Increment spin angle
spin += 0.5f;
One last thing was needed for this demo to be complete though and that was bluring. I wrote a custom function that simply dims every pixel in an image to black by an alpha value. When use in place of clearing the buffer, the buffer essentially recursively blurs onto itself since it's not being cleared each frame yet each frame is dimmed by a factor.
// Fade function, can fade recursively by not clearing the buffer (16bit color only)
// alpha ranges from 0 to 32, any higer and you get some neat effects though :)
void fade(BITMAP *src, int a) {
int y, x;
unsigned int color;
for (y = 0; y < src -> h; y++) {
for(x = 0; x < src -> w; x++) {
color = ((unsigned short *)src -> line[y])[x];
if(color != 0){
// Magic numbers supplied from fblend source
color = (color | (color << 16)) & 0x07E0F81F;
color = ((color * a)>> 5) & 0x07E0F81F;
color = color| (color >> 16);
((unsigned short *)src -> line[y])[x] = color;
}
}
}
}
Once all of the effects were in place the demonstration very closely resembled the NeHe Lesson 9 but using no OpenGL.
I hope you enjoyed this tutorial as it's my first. Please e-mail your questions or comments to skeevewoo3@hotmail.com
Editors Note: The source code for this article, as well as prebuilt Windows executable can be found [stars_src_w32bin.zip here]
