How To: Create a Simple Fireworks Effect in OpenGL and SDL

I’m teaching the programming stream for Diploma in I.T. – Multimedia at the moment, so we’re going to be making some simple games and things over the next couple of months, but to start them off we’re doing a little bit of work on particle effects because they’re simple and fun – you just write a single class which describes the properties and behavior of your particle, and then scale it up by adding a large number of particles to get some pretty nice effects.

So instead of doing a particle fountain, I thought some fireworks might be kinda neat… Take a swiz at the video below and see what you think!

Full C++ source-code available after the break for those of a curious nature =D

Download the file: Fireworks-with-Trails.cpp

Update: I’m not using SDL anymore, so I translated this into GLFW – much cleaner! The GLFW source code appears after the SDL version!
Update 2 – Nov 2012: Fixes to glfw version (removal of glee/glew, modification of accumulation buffer copying order).
Update 3 – Dec 2018: Wow… you people like fireworks. You can find a Visual Studio 2017 version with the latest GLEW and GLFW3 libs here: https://r3dux.org/files/VS2017_GLFW3_Fireworks_2018.zip

  1. // Uncomment for Win32 systems
  2. // Search for and include the opengl32 and glu (GL-Utility) libraries
  3. //#pragma comment(lib, "opengl32.lib")
  4. //#pragma comment(lib, "glu32.lib")
  5.  
  6. #include 
  7. #include 
  8. #include 		// Needed to use random numbers
  9. //#include 	// *** IMPORTANT: Uncomment for Win32 systems - This must come -BEFORE- gl.h in the include list! ***
  10. #include 		// OpenGL headers
  11. #include 		// Simple DirectMedia Layer headers
  12. using namespace std;
  13.  
  14. // Define our initial screen width, height, and colour depth
  15. GLint SCREEN_WIDTH  = 800;
  16. GLint SCREEN_HEIGHT = 600;
  17. GLint SCREEN_BPP    =  16;
  18.  
  19. // Define our SDL surface
  20. SDL_Surface *surface;
  21.  
  22. const int FIREWORKS = 15;           // Number of fireworks
  23. const int FIREWORK_PARTICLES = 50;  // Number of particles per firework
  24.  
  25. class Firework
  26. {
  27. 	public:
  28. 		GLfloat x[FIREWORK_PARTICLES];
  29. 		GLfloat y[FIREWORK_PARTICLES];
  30. 		GLfloat xSpeed[FIREWORK_PARTICLES];
  31. 		GLfloat ySpeed[FIREWORK_PARTICLES];
  32.  
  33. 		GLfloat red;
  34. 		GLfloat blue;
  35. 		GLfloat green;
  36. 		GLfloat alpha;
  37.  
  38. 		GLint framesUntilLaunch;
  39.  
  40. 		GLfloat particleSize;
  41. 		GLboolean hasExploded;
  42.  
  43. 		static const GLfloat baselineYSpeed;
  44. 		static const GLfloat maxYSpeed;
  45.  
  46. 		static const GLfloat GRAVITY;
  47.  
  48. 		Firework(); // Constructor declaration
  49. 		void initialise();
  50. 		void move();
  51. 		void explode();
  52. };
  53.  
  54. const GLfloat Firework::GRAVITY = 0.05f;
  55. const GLfloat Firework::baselineYSpeed = -4.0f;
  56. const GLfloat Firework::maxYSpeed = -4.0f;
  57.  
  58. // Constructor implementation
  59. Firework::Firework()
  60. {
  61. 	initialise();
  62. }
  63.  
  64. void Firework::initialise()
  65. {
  66.     // Pick an initial x location and  random x/y speeds
  67.     float xLoc = (rand() / (float)RAND_MAX) * SCREEN_WIDTH;
  68.     float xSpeedVal = -2 + (rand() / (float)RAND_MAX) * 4.0f;
  69.     float ySpeedVal = baselineYSpeed + ((float)rand() / (float)RAND_MAX) * maxYSpeed;
  70.  
  71.     // Set initial x/y location and speeds
  72.     for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
  73.     {
  74.         x[loop] = xLoc;
  75.         y[loop] = SCREEN_HEIGHT + 10.0f; // Push the particle location down off the bottom of the screen
  76.         xSpeed[loop] = xSpeedVal;
  77.         ySpeed[loop] = ySpeedVal;
  78.     }
  79.  
  80.     // Assign a random colour and full alpha (i.e. particle is completely opaque)
  81.     red   = (rand() / (float)RAND_MAX);
  82.     green = (rand() / (float)RAND_MAX);
  83.     blue  = (rand() / (float)RAND_MAX);
  84.     alpha = 1.0f;
  85.  
  86.     // Firework will launch after a random amount of frames between 0 and 400
  87.     framesUntilLaunch = ((int)rand() % 400);
  88.  
  89.     // Size of the particle (as thrown to glPointSize) - range is 1.0f to 4.0f
  90.     particleSize = 1.0f + ((float)rand() / (float)RAND_MAX) * 3.0f;
  91.  
  92.     // Flag to keep trackof whether the firework has exploded or not
  93.     hasExploded = false;
  94.  
  95.     cout << "Initialised a firework." << endl;
  96. }
  97.  
  98. void Firework::move()
  99. {
  100.     for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
  101.     {
  102.         // Once the firework is ready to launch start moving the particles
  103.         if (framesUntilLaunch <= 0)
  104.         {
  105.             x[loop] += xSpeed[loop];
  106.  
  107.             y[loop] += ySpeed[loop];
  108.  
  109.             ySpeed[loop] += Firework::GRAVITY;
  110.         }
  111.     }
  112.     framesUntilLaunch--;
  113.  
  114.     // Once a fireworks speed turns positive (i.e. at top of arc) - blow it up!
  115.     if (ySpeed[0] > 0.0f)
  116.     {
  117.         for (int loop2 = 0; loop2 < FIREWORK_PARTICLES; loop2++)
  118.         {
  119.             // Set a random x and y speed beteen -4 and + 4
  120.             xSpeed[loop2] = -4 + (rand() / (float)RAND_MAX) * 8;
  121.             ySpeed[loop2] = -4 + (rand() / (float)RAND_MAX) * 8;
  122.         }
  123.  
  124.         cout << "Boom!" << endl;
  125.         hasExploded = true;
  126.     }
  127. }
  128.  
  129. void Firework::explode()
  130. {
  131.     for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
  132.     {
  133.         // Dampen the horizontal speed by 1% per frame
  134.         xSpeed[loop] *= 0.99;
  135.  
  136.         // Move the particle
  137.         x[loop] += xSpeed[loop];
  138.         y[loop] += ySpeed[loop];
  139.  
  140.         // Apply gravity to the particle's speed
  141.         ySpeed[loop] += Firework::GRAVITY;
  142.     }
  143.  
  144.     // Fade out the particles (alpha is stored per firework, not per particle)
  145.     if (alpha > 0.0f)
  146.     {
  147.         alpha -= 0.01f;
  148.     }
  149.     else // Once the alpha hits zero reset the firework
  150.     {
  151.         initialise();
  152.     }
  153. }
  154.  
  155. // Create our array of fireworks
  156. Firework fw[FIREWORKS];
  157.  
  158. // Function to release/destroy our resources and exit
  159. int quit(int returnCode)
  160. {
  161.     // Clean up the window
  162.     SDL_Quit();
  163.  
  164.     // Exit appropriately
  165.     exit(returnCode);
  166. }
  167.  
  168.  
  169. // Function to set some initial OpenGL state-machine properties
  170. int initGL()
  171. {
  172.     //  Enable smooth shading (i.e. gouraud shading)
  173.     glShadeModel(GL_SMOOTH);
  174.  
  175.     // Set our clear colour to black with full opacity. Syntax is (r, g, b, alpha).
  176.     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  177.  
  178.     // Disable depth testing (because we're working in 2D!)
  179.     glDisable(GL_DEPTH_TEST);
  180.  
  181.     // Enable blending (we need this to be able to use an alpha component)
  182.     glEnable(GL_BLEND);
  183.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  184.  
  185.     // Set the accumulation buffer clearing colour to black at 0,0f alpha
  186.     glClearAccum(0.0f, 0.0f, 0.0f, 1.0f);
  187.  
  188.     glPointSize(5.0f);
  189.  
  190.     return 0;
  191. }
  192.  
  193. // Function to draw our OpenGL scene
  194. int drawScene()
  195. {
  196.     // Set our clear mode to clear the screen -only-
  197.     glClear(GL_COLOR_BUFFER_BIT);
  198.  
  199.     // Take the contents of the current accumulation buffer and copy it to the colour buffer with each pixel multiplied by a factor
  200.     // i.e. we clear the screen, draw the last frame again (which we saved in the accumulation buffer), then draw our stuff at its new location on top of that
  201.     glAccum(GL_RETURN, 0.95f);
  202.  
  203.     // Clear the accumulation buffer (don't worry, we re-grab the screen into the accumulation buffer after drawing our current frame!)
  204.     glClear(GL_ACCUM_BUFFER_BIT);
  205.  
  206. 	// Set ModelView matrix mode and reset to the default identity matrix
  207.     glMatrixMode(GL_MODELVIEW);
  208.     glLoadIdentity();
  209.  
  210. 	// Displacement trick for exact pixelisation
  211. 	glTranslatef(0.375, 0.375, 0);
  212.  
  213.     // Draw fireworks
  214.     //cout << "Firework count is: " << Firework::fireworkCount << endl;
  215.     for (int loop = 0; loop < FIREWORKS; loop++)
  216.     {
  217.         for (int particleLoop = 0; particleLoop < FIREWORK_PARTICLES; particleLoop++)
  218.         {
  219.  
  220.             // Set the point size of the firework particles (this needs to be called BEFORE opening the glBegin(GL_POINTS) section!)
  221.             glPointSize(fw[loop].particleSize);
  222.  
  223.             glBegin(GL_POINTS);
  224.  
  225.                 // Set colour to yellow on way up, then whatever colour firework should be when exploded
  226.                 if (fw[loop].hasExploded == false)
  227.                 {
  228.                     glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
  229.                 }
  230.                 else
  231.                 {
  232.                     glColor4f(fw[loop].red, fw[loop].green, fw[loop].blue, fw[loop].alpha);
  233.                 }
  234.  
  235.                 // Draw the point
  236.                 glVertex2f(fw[loop].x[particleLoop], fw[loop].y[particleLoop]);
  237.             glEnd();
  238.         }
  239.  
  240.         // Move the firework appropriately depending on its explosion state
  241.         if (fw[loop].hasExploded == false)
  242.         {
  243.             fw[loop].move();
  244.         }
  245.         else
  246.         {
  247.             fw[loop].explode();
  248.         }
  249.     }
  250.  
  251.     // Swap active and visual pages to display our output to the screen
  252.     SDL_GL_SwapBuffers();
  253.  
  254.     // Take the contents of the current draw buffer and copy it to the accumulation buffer with each pixel modified by a factor
  255.     // The closer the factor is to 1.0f, the longer the trails... Don't exceed 1.0f - you get garbage.
  256.     glAccum(GL_ACCUM, 0.9f);
  257.  
  258.     // Return clean state
  259.     return 0;
  260.  
  261. } // End of drawScene function
  262.  
  263. // Function to initialise variables before use
  264. void initVars()
  265. {
  266.     // Seed random number generator
  267.     srand(time(NULL));
  268. }
  269.  
  270. // Function to reset our viewport after a window resize
  271. int resizeWindow(int width, int height)
  272. {
  273.     // Create a variable for our height/width ratio
  274.     GLfloat ratio;
  275.  
  276.     // Protect against a divide by zero error when resizing
  277.     if (height == 0)
  278. 	{
  279. 		height = 1;
  280. 	}
  281. 	if (width == 0)
  282. 	{
  283. 		width = 1;
  284. 	}
  285.  
  286. 	// Calculate our new (post-resize) window ratio
  287. 	if (width > height)
  288. 	{
  289. 		ratio = (GLfloat)width / (GLfloat)height;
  290. 		ratio = (GLfloat)height / (GLfloat)width;
  291. 	}
  292. 	else
  293. 	{
  294. 		ratio = (GLfloat)height / (GLfloat)width;
  295. 	}
  296.  
  297.     // Setup our viewport to be the entire size of the window
  298.     glViewport(0, 0, (GLsizei)width, (GLsizei)height);
  299.  
  300.     // Change to the projection matrix and reset the matrix
  301.     glMatrixMode(GL_PROJECTION);
  302.     glLoadIdentity();
  303.  
  304. 	// Change to orthographic (2D) projection and switch to the modelview matrix
  305. 	glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 1);
  306. 	glMatrixMode(GL_MODELVIEW);
  307. 	glLoadIdentity();
  308.  
  309.     return 0;
  310. }
  311.  
  312.  
  313. // Function to handle key press events
  314. void handleKeyPress(SDL_keysym *keysym)
  315. {
  316.     switch(keysym->sym)
  317. 	{
  318. 		case SDLK_ESCAPE: // ESC key pressed? Then quit program
  319. 		    quit(0);
  320. 			break;
  321.  
  322. 		case SDLK_F1: // F1 key pressed? Then toggle fullscreen mode
  323. 		    SDL_WM_ToggleFullScreen(surface);
  324. 			break;
  325.  
  326. 		default:
  327. 		    // Do nothing by default!
  328. 			break;
  329.  
  330. 	} // End of switch statement
  331. }
  332.  
  333. // Function to set up the screen
  334. void setupScreen(int& videoFlags)
  335. {
  336.     // Create a constant pointer to our SDL_VideoInfo details
  337.     const SDL_VideoInfo *videoInfo;
  338.  
  339.     // Initialise SDL - quit out if we fail
  340.     if (SDL_Init(SDL_INIT_VIDEO) < 0 )
  341.     {
  342.         fprintf( stderr, "Video initialization failed: %s\n", SDL_GetError() );
  343. 		quit(1);
  344.     }
  345.  
  346.     // Get our video information
  347.     videoInfo = SDL_GetVideoInfo();
  348.  
  349.     // Quit if we couldn't get any videoInfo values back from our SDL_GetVideoInfo call
  350.     if (!videoInfo)
  351. 	{
  352. 	    cerr << "Video query failed: " << SDL_GetError() << endl;
  353. 	    quit(1);
  354. 	}
  355.  
  356.     // Setup our SDL to use OpenGL with double buffering, a hardware palette and enable window resizing
  357.     videoFlags  = SDL_OPENGL;          // Enable OpenGL in SDL
  358.     videoFlags |= SDL_GL_DOUBLEBUFFER; // Enable double buffering
  359.     videoFlags |= SDL_HWPALETTE;       // Store the palette in hardware
  360.     videoFlags |= SDL_RESIZABLE;       // Enable window resizing
  361.  
  362.     // Check if our SDL surfaces can be stored in hardware memory or not
  363.     if (videoInfo->hw_available)
  364. 	{
  365. 		videoFlags |= SDL_HWSURFACE;
  366.     }
  367. 	else
  368. 	{
  369. 		videoFlags |= SDL_SWSURFACE;
  370. 	}
  371.  
  372.     // Do hardware video blits if possible
  373.     if (videoInfo->blit_hw)
  374. 	{
  375. 		videoFlags |= SDL_HWACCEL;
  376. 	}
  377.  
  378.     // Set up OpenGL for double buffering in SDL
  379.     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  380.  
  381.     // Get our SDL surface to draw to
  382.     surface = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, videoFlags);
  383.  
  384.     // Quit out if we can't get a SDL surface
  385.     if (!surface)
  386. 	{
  387. 	    cerr << "Video mode set failed: " << SDL_GetError() << endl;
  388. 	    quit(1);
  389. 	}
  390.  
  391. } // End of setupScreen function
  392.  
  393.  
  394. // Function to handle all SDL events appropriately
  395. void controlLoop(int& videoFlags, SDL_Event& event)
  396. {
  397. 	// Static var to keep track of whether we should now exit or not
  398.     static bool done = false;
  399.  
  400.     // Keep going until done is true...
  401.     while (!done)
  402. 	{
  403. 	    // Handle the events in the queue
  404. 	    while ( SDL_PollEvent(&event) )
  405. 		{
  406. 			// Depending on what event has occured, handle it appropriately
  407. 		    switch (event.type) {
  408.  
  409. 				// If the window is resized then call the appropriate resizeWindow function
  410. 				case SDL_VIDEORESIZE:
  411. 				    surface = SDL_SetVideoMode(event.resize.w, event.resize.h, 16, videoFlags );
  412. 				    if (!surface)
  413. 					{
  414. 					    cerr << "Could not get a surface after resize: " << SDL_GetError() << endl;
  415. 					    quit(1);
  416. 					}
  417. 				    resizeWindow(event.resize.w, event.resize.h);
  418. 				    break;
  419.  
  420. 				// If a key is pressed then handle the keypress appropriately
  421. 				case SDL_KEYDOWN:
  422. 				    handleKeyPress(&event.key.keysym);
  423. 				    break;
  424.  
  425. 				case SDL_QUIT:
  426. 				    done = true;
  427. 				    break;
  428.  
  429. 				// By default, do nothing...
  430. 				default:
  431. 					// Nothing to see here... move along.
  432. 				    break;
  433.  
  434. 			} // End of switch statement
  435.  
  436. 		} // End of while SDL_PollEvent condition
  437.  
  438. 	    // Call the drawScene function to draw our objects to the screen!
  439. 	    drawScene();
  440.  
  441. 		// Cheap-as-chips limit framerate to approximately 60fps
  442. 		SDL_Delay(16);
  443.  
  444. 	} // End of main while (!done) loop
  445.  
  446. } // End of controlLoop function
  447.  
  448.  
  449. // Our main function
  450. int main(int argc, char **argv)
  451. {
  452.     // Flags to pass to SDL_SetVideoMode - made global for use in setupScreen and controlLoop functions
  453.     int videoFlags;
  454.  
  455.     // SDL_Event type variable to keep track of events - used in our controlLoop function
  456.     SDL_Event event;
  457.  
  458.     // Set up our screen
  459.     setupScreen(videoFlags);
  460.  
  461.     // Set the window and tray caption
  462.     SDL_WM_SetCaption("Simple OpenGL Fireworks with Trails | r3dux.org", "Fireworks w/ Trails");
  463.  
  464.     // Initialise our variables
  465.     initVars();
  466.  
  467.     // Initialise OpenGL
  468.     initGL();
  469.  
  470.     // Resize the initial window
  471.     resizeWindow(SCREEN_WIDTH, SCREEN_HEIGHT);
  472.  
  473.     // Call our control loop - this the main loop that keeps on running until user quits program with Esc or by closing the display window
  474.     controlLoop(videoFlags, event);
  475.  
  476.     // Clean ourselves up and exit
  477.     quit(0);
  478.  
  479.     // We will never get here because our quit function will exit or us - but we'll put this return in for the sake of correctness!
  480.     return 0;
  481. }

GLFW Version (Fireworks class in separate files)

  1. #include 
  2. #include 
  3. #include          // Needed to seed the random number generator
  4.  
  5. //#include  // *** IMPORTANT: Uncomment for Win32 systems - This must come -BEFORE- gl.h in the include list! ***
  6.  
  7. #include     // Include OpenGL Framework library
  8.  
  9. #include "Firework.h"    // Include our fireworks class
  10.  
  11. // Uncomment for Windows systems and make sure to point at wherever you've place GLFW.lib, in this case its in "lib/glfw"
  12. // inside the folder where main.cpp lives (also stick any glfw .dlls in the same folder as the main.cpp)
  13. //#pragma comment(lib, "opengl32.lib")
  14. //#pragma comment(lib, "lib/glfw/GLFW.lib")
  15.  
  16. using namespace std;
  17.  
  18. GLint windowWidth  = 800; // Define our window width
  19. GLint windowHeight = 600; // Define our window height
  20. GLint frameCount   = 0;     // Keep track of how many frames we've drawn
  21. GLuint texture;                 // Identifier for our texture
  22.  
  23. const int FIREWORKS = 15; // Number of fireworks
  24.  
  25. // Create our array of fireworks
  26. Firework fw[FIREWORKS];
  27.  
  28. // Function to set some initial OpenGL state-machine properties
  29. void initGL()
  30. {
  31.     glfwSwapInterval(1); // Lock to vertical sync of monitor (normally 60Hz, so 60fps)
  32.  
  33.     // ----- Window and Projection Settings -----
  34.  
  35.     // Set the window title
  36.     glfwSetWindowTitle("GLFW Fireworks with Trails");
  37.  
  38.     // Setup our viewport to be the entire size of the window
  39.     glViewport(0, 0, (GLsizei)windowWidth, (GLsizei)windowHeight);
  40.  
  41.     // Change to the projection matrix, reset the matrix and set up orthagonal projection (i.e. 2D)
  42.     glMatrixMode(GL_PROJECTION);
  43.     glLoadIdentity();
  44.     glOrtho(0, windowWidth, windowHeight, 0, 0, 1); // Parameters: left, right, bottom, top, near, far
  45.  
  46.     //  Enable smooth shading (i.e. gouraud shading)
  47.     glShadeModel(GL_SMOOTH);
  48.  
  49.     // Set our clear colour to opaque black
  50.     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  51.  
  52.     // Disable depth testing (because we're working in 2D!)
  53.     glDisable(GL_DEPTH_TEST);
  54.  
  55.     // Enable blending (we need this to be able to use an alpha component)
  56.     glEnable(GL_BLEND);
  57.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  58.  
  59.     // Set the accumulation buffer clearing colour to opaque black
  60.     glClearAccum(0.0f, 0.0f, 0.0f, 1.0f);
  61.  
  62.     glEnable(GL_POINT_SMOOTH); // Smooth the points so that they're circular and not square
  63. }
  64.  
  65. // Function to draw our OpenGL scene
  66. void drawScene()
  67. {
  68.     // Take the contents of the current accumulation buffer and copy it to the colour buffer so that it entirely overwrites it
  69.     glAccum(GL_RETURN, 1.0f);
  70.  
  71.     // Clear the accumulation buffer (don't worry, we re-grab the screen into the accumulation buffer after drawing our current frame!)
  72.     glClear(GL_ACCUM_BUFFER_BIT);
  73.  
  74.     // Set ModelView matrix mode and reset to the default identity matrix
  75.     glMatrixMode(GL_MODELVIEW);
  76.     glLoadIdentity();
  77.  
  78.     // Displacement trick for exact pixelisation
  79.     glTranslatef(0.375, 0.375, 0);
  80.  
  81.     // Draw our fireworks
  82.     for (int loop = 0; loop < FIREWORKS; loop++)
  83.     {
  84.         for (int particleLoop = 0; particleLoop < FIREWORK_PARTICLES; particleLoop++)
  85.         {
  86.  
  87.             // Set the point size of the firework particles (this needs to be called BEFORE opening the glBegin(GL_POINTS) section!)
  88.             glPointSize(fw[loop].particleSize);
  89.  
  90.             glBegin(GL_POINTS);
  91.                 // Set colour to yellow on the way up, then whatever colour firework should be when exploded
  92.                 if (fw[loop].hasExploded == false)
  93.                 {
  94.                     glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
  95.                 }
  96.                 else
  97.                 {
  98.                     glColor4f(fw[loop].red, fw[loop].green, fw[loop].blue, fw[loop].alpha);
  99.                 }
  100.  
  101.                 // Draw the point
  102.                 glVertex2f(fw[loop].x[particleLoop], fw[loop].y[particleLoop]);
  103.             glEnd();
  104.         }
  105.  
  106.         // Move the firework appropriately depending on its explosion state
  107.         if (fw[loop].hasExploded == false)
  108.         {
  109.             fw[loop].move();
  110.         }
  111.         else
  112.         {
  113.             fw[loop].explode();
  114.         }
  115.     }
  116.  
  117.     // ----- Stop Drawing Stuff! ------
  118.  
  119.     // Take the contents of the current draw buffer and copy it to the accumulation buffer with each pixel modified by a factor
  120.     // The closer the factor is to 1.0f, the longer the trails... Don't exceed 1.0f - you get garbage!
  121.     glAccum(GL_ACCUM, 0.99f);
  122.  
  123.     glfwSwapBuffers(); // Swap the buffers to display the scene (so we don't have to watch it being drawn!)
  124.  
  125. } // End of drawScene function
  126.  
  127. // Our main function
  128. int main()
  129. {
  130.     srand((unsigned)time(NULL)); // Seed the random number generator
  131.  
  132.     // Define our buffer settings
  133.     int	redBits     = 8,   greenBits = 8,    blueBits    = 8;
  134.     int alphaBits  = 64, depthBits = 24,   stencilBits = 8;
  135.  
  136.     // Flag to keep our main loop running
  137.     bool running = true;
  138.  
  139.     // Initialise glfw
  140.     glfwInit();
  141.  
  142.     // Create a window
  143.     if(!glfwOpenWindow(windowWidth, windowHeight, redBits, greenBits, blueBits, alphaBits, depthBits, stencilBits, GLFW_WINDOW))
  144.     {
  145.         cout << "Failed to open window!" << endl;
  146.         glfwTerminate();
  147.         return 0;
  148.     }
  149.  
  150.     // Call our initGL function to set up our OpenGL options
  151.     initGL();
  152.  
  153.     while (running == true)
  154.     {
  155. 	// Draw our scene
  156. 	drawScene();
  157.  
  158. 	// Increase our frame counter
  159. 	frameCount++;
  160.  
  161. 	// Exit if ESC was pressed or the window was closed
  162. 	running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam(GLFW_OPENED);
  163.     }
  164.  
  165.     glfwTerminate();
  166.  
  167.     return 0;
  168. }

Firework.h

  1. #ifndef FIREWORK_H
  2. #define FIREWORK_H
  3.  
  4. #include 
  5. #include  // OpenGL headers
  6.  
  7. const GLint FIREWORK_PARTICLES = 70;
  8.  
  9. class Firework
  10. {
  11. 	public:
  12. 		// Object member properties
  13. 		GLfloat x[FIREWORK_PARTICLES];
  14. 		GLfloat y[FIREWORK_PARTICLES];
  15. 		GLfloat xSpeed[FIREWORK_PARTICLES];
  16. 		GLfloat ySpeed[FIREWORK_PARTICLES];
  17.  
  18. 		GLfloat red;
  19. 		GLfloat blue;
  20. 		GLfloat green;
  21. 		GLfloat alpha;
  22.  
  23. 		GLint framesUntilLaunch;
  24.  
  25. 		GLfloat particleSize;
  26. 		GLboolean hasExploded;
  27.  
  28. 		static const GLfloat baselineYSpeed;
  29. 		static const GLfloat maxYSpeed;
  30.  
  31. 		static const GLfloat GRAVITY;
  32.  
  33.  
  34. 		// Object member functions
  35. 		Firework(); // Constructor declaration
  36. 		void initialise();
  37. 		void move();
  38. 		void explode();
  39. };
  40.  
  41. #endif

Firework.cpp

  1. #include "Firework.h"
  2.  
  3. // Set our static (per class NOT per object!) variable values
  4. const GLfloat Firework::GRAVITY        = 0.05f;
  5. const GLfloat Firework::baselineYSpeed = -4.0f;
  6. const GLfloat Firework::maxYSpeed      = -4.0f;
  7.  
  8. // Constructor implementation
  9. Firework::Firework()
  10. {
  11. 	// We call a function to perform the constructor's job here so that we can re-initialise the same firework
  12. 	// later on without having to destroy the object and recreate it!
  13.     initialise();
  14. }
  15.  
  16. void Firework::initialise()
  17. {
  18.     // Pick an initial x location and random x/y speeds for each particle making up the firework
  19.     // Note: Each particle in the firework must have the exact same values for the firework to rise as a single point!
  20.     float xLoc      = ((float)rand() / (float)RAND_MAX) * 800.0f;
  21.     float xSpeedVal = -2 + ((float)rand() / (float)RAND_MAX) * 4.0f;
  22.     float ySpeedVal = baselineYSpeed + ((float)rand() / (float)RAND_MAX) * maxYSpeed;
  23. 	//cout << ySpeedVal << endl;
  24.  
  25.     // Set initial x/y location and speeds for each particle in the firework
  26.     for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
  27.     {
  28.         x[loop] = xLoc;
  29.         y[loop] = 610.0f; // Push the particle location down off the bottom of the screen
  30.         xSpeed[loop] = xSpeedVal;
  31.         ySpeed[loop] = ySpeedVal;
  32.     }
  33.  
  34.     // Assign a random colour and full alpha (i.e. particle is completely opaque)
  35.     red   = ((float)rand() / (float)RAND_MAX);
  36.     green = ((float)rand() / (float)RAND_MAX);
  37.     blue  = ((float)rand() / (float)RAND_MAX);
  38.     alpha = 1.0f;
  39.  
  40.     // Firework will launch after a random amount of frames between 0 and 400
  41.     framesUntilLaunch = ((int)rand() % 400);
  42.  
  43.     // Size of the particle (as thrown to glPointSize) - range is 1.0f to 4.0f
  44.     particleSize = 1.0f + ((float)rand() / (float)RAND_MAX) * 3.0f;
  45.  
  46.     // Flag to keep trackof whether the firework has exploded or not
  47.     hasExploded = false;
  48.  
  49.     //cout << "Initialised a firework." << endl;
  50. }
  51.  
  52. // Function to move a firework
  53. void Firework::move()
  54. {
  55.     for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
  56.     {
  57.         // Once the firework is ready to launch start moving the particles
  58.         if (framesUntilLaunch <= 0)
  59.         {
  60.             x[loop] += xSpeed[loop];
  61.  
  62.             y[loop] += ySpeed[loop];
  63.  
  64.             ySpeed[loop] += Firework::GRAVITY;
  65.         }
  66.     }
  67.     framesUntilLaunch--; // Decrease the launch countdown clock!
  68.  
  69.     // Once a fireworks speed turns positive (i.e. at top of arc) - blow it up!
  70.     if (ySpeed[0] > 0.0f)
  71.     {
  72.         for (int loop2 = 0; loop2 < FIREWORK_PARTICLES; loop2++)
  73.         {
  74.             // Set a random x and y speed beteen -4 and + 4
  75.             xSpeed[loop2] = -4 + (rand() / (float)RAND_MAX) * 8;
  76.             ySpeed[loop2] = -4 + (rand() / (float)RAND_MAX) * 8;
  77.         }
  78.  
  79.         // Set the firework hasExploded flag to true
  80.         hasExploded = true;
  81.  
  82.         //cout << "Boom!" << endl;
  83.     }
  84. }
  85.  
  86. // Function to make a firework explode
  87. void Firework::explode()
  88. {
  89.     for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
  90.     {
  91.         // Dampen the horizontal speed by 1% per frame
  92.         xSpeed[loop] *= 0.99;
  93.  
  94.         // Move the particle
  95.         x[loop] += xSpeed[loop];
  96.         y[loop] += ySpeed[loop];
  97.  
  98.         // Apply gravity to the particle's speed
  99.         ySpeed[loop] += Firework::GRAVITY;
  100.     }
  101.  
  102.     // Fade out the particles (alpha is stored per firework, not per particle)
  103.     if (alpha > 0.0f)
  104.     {
  105.         alpha -= 0.01f;
  106.     }
  107.     else // Once the alpha hits zero, then reset the firework
  108.     {
  109.         initialise();
  110.     }
  111. }

34 thoughts on “How To: Create a Simple Fireworks Effect in OpenGL and SDL”

    1. Hi Nikita,

      I’m not in the least bit angry – in fact, quite the opposite! I’m really glad that you found the code useful, and I put the source code up in the first place in the hope that it would be.

      Thanks for taking the time to post a comment, and for including me/this-post in your modified source. I hope that when you’ve finished it you’ll come back and post a link to the completed game.

      P.S. I used to play Nebulus back on the Spectrum when I was a kid (and then later on the Amiga) – great game! I hope you include the helloiamjmp cheat! =D

  1. Hi,
    Thanks for this great example. The high number of comments really helps to understand what is going on.
    I have one problem though, with the trails.

    At home I tried it at three different computer and none had any trails, not even when I just copied your code. However, now I tried it at work and here everything is as it should be, with nice trails behind the rockets.

    Is there any setting that controls this that I did not activate at home? The only obvious difference that I can make out between these PCs is the OS (Win7Home vs. Win7Pro) and of course some hardware (ATI Radeon card vs. nVidia Quadro FX).

    Thanks
    Frank

    1. Hi Frank,

      That’s pretty odd – try pasting the following code at the bottom of the setupScreen function to check that you’re getting an accumulation buffer created:

      // Check accumulation buffer bit-depths
      int AccRed, AccGreen, AccBlue, AccAlpha;
      SDL_GL_GetAttribute(SDL_GL_ACCUM_RED_SIZE,   &AccRed);   // Size of the accumulation buffer red component, in bits.
      SDL_GL_GetAttribute(SDL_GL_ACCUM_GREEN_SIZE, &AccGreen); // Size of the accumulation buffer green component, in bits.
      SDL_GL_GetAttribute(SDL_GL_ACCUM_BLUE_SIZE,  &AccBlue);  // Size of the accumulation buffer blue component, in bits.
      SDL_GL_GetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, &AccAlpha); // Size of the accumulation buffer alpha component, in bits.
       
      cout << "Accum. Red   : " << AccRed   << endl;
      cout << "Accum. Green : " << AccGreen << endl;
      cout << "Accum. Blue  : " << AccBlue  << endl;
      cout << "Accum. Alpha : " << AccAlpha << endl;

      When I add this code I get 16 bits each for Red, Green, Blue and Alpha – so try it on the machines where the trails don’t work to make sure you’re actually getting an accumulation buffer on ’em.

      Also, try the exact same executable that definitely works on your work machine on your home machines to rule out any code issues.

      Failing all that, I moved this code over from SDL to GLFW the other day but hadn’t got around to adding that code, but now that I have maybe you’ll have more luck with that =D

      Let me know how you get on =D

      1. Hi,
        I’ll add that code when I’m back at home later today.

        I used the very same executable on all machines (built at home, imagine my surprise when I started it here and it looked different).

        However, I think you’re right that I don’t have an accumulation buffer, as when I tried to change
        glClearAccum(0.0f, 0.0f, 0.0f, 1.0f);
        at home nothing changed, whereas here at work I get a nice colored background.

        Can it be that ATI does not support accumulation buffers on consumer cards? (I found http://forums.amd.com/game/messageview.cfm?catid=260&threadid=123808 which seam to have a similar problem).

        But I’ll try adding your code snippet just to be sure.

        Thanks
        Frank

  2. Hi,
    I tried the same executable on all machines – I emailed it yesterday to my work address just to give it a go, but was sure it will not show trails. Imagine my surprise when I started it and got the trails. All my home PCs have ATI Radeon cards.

    I just tried adding the code and I get all zeros.
    At work I spoke to our graphics guy and immediately after I mentioned that I have an ATI card he said he knows the problem.
    We use nVidia quadro cards at work because they have the best openGL support. According to him even the ATI FirePro cards have rather sub-par openGL support, never mind the consumer ones.
    I had a play with an updated card driver version, but it did not change anything.

    However, after knowing this I just made an experiment and undusted my old laptop from the medieval era, which has a nVidia geforce go card. It plays the fireworks with about 1fps (tops), but there are nice trails.

    So I fear I am stuck without trails, unless there is a way to get these effects without accumulation buffer?

    Thanks
    Frank

    1. Okay, so if you get all zeros for the accumulation buffer bits on the ATI cards you’re definitely not getting an accumulation buffer, hence no trails. Makes sense ;)

      As for other ways of creating the trails, you just need some kind of buffer that you can use for temporary storage. In essence, we just need somewhere to store a copy of what was on the screen so we can:
      – Clear the screen
      – Draw what used to be there but with a little less opacity
      – Draw the elements of our new frame on top of it

      Probably the best way to go about this would be to copy to and from a Frame Buffer Object, but FBOs can be tricky if you’ve not worked with them before (which I haven’t, although I’ll be learning them soon!).

      Another alternative might be to NOT clear the screen, but instead draw a full-screen quad with low opacity over the top of everything just before drawing your new frame… I thought this would work, but wasn’t 100% sure – but guess what? It does =D

      Here’s the important stuff to make it happen:

      // Function to set some initial OpenGL state-machine properties
      void initGL()
      {
          glfwSwapInterval(1); // Lock to vertical sync of monitor (normally 60Hz, so 60fps)
       
          // ----- Window and Projection Settings -----
       
          // Set the window title
          glfwSetWindowTitle("GLFW Fireworks with Trails");
       
          // Setup our viewport to be the entire size of the window
          glViewport(0, 0, (GLsizei)windowWidth, (GLsizei)windowHeight);
       
          // Change to the projection matrix, reset the matrix and set up orthagonal projection (i.e. 2D)
          glMatrixMode(GL_PROJECTION);
          glLoadIdentity();
          glOrtho(0, windowWidth, windowHeight, 0, 0, 1); // Parameters: left, right, bottom, top, near, far
       
          // Disable depth testing (because we're working in 2D!)
          glDisable(GL_DEPTH_TEST);
       
          // Enable blending (we need this to be able to use an alpha component)
          glEnable(GL_BLEND);
          glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
       
          glEnable(GL_POINT_SMOOTH); // Smooth the points so that they're circular and not square
       
          glPointSize(5.0f);
      }
       
      void drawBackground()
      {
      	// The alpha setting for this (the fourth parameter) is what gives us the trails. The lower the value, the longer the trails!
      	glColor4f(0.0f, 0.0f, 0.0f, 0.2f);
       
      	// Draw the background
      	// REMEMBER: Window co-ordinates have the origin at the top left.
      	glBegin(GL_QUADS);
                  glVertex2i(0,           windowHeight);
                  glVertex2i(windowWidth, windowHeight);
                  glVertex2i(windowWidth, 0);
                  glVertex2i(0,           0);
      	glEnd();
      }
       
      // Function to draw our OpenGL scene
      void drawScene()
      {
          // Set ModelView matrix mode and reset to the default identity matrix
          glMatrixMode(GL_MODELVIEW);
          glLoadIdentity();
       
          // Displacement trick for exact pixelisation
          glTranslatef(0.375, 0.375, 0);
       
          // Draw a translucent black quad over the entire scene
          drawBackground();
       
          // Draw our new firework frame...
          for (int loop = 0; loop < FIREWORKS; loop++)
          {
              for (int particleLoop = 0; particleLoop < FIREWORK_PARTICLES; particleLoop++)
              {
       
                  // Set the point size of the firework particles (this needs to be called BEFORE opening the glBegin(GL_POINTS) section!)
                  glPointSize(fw[loop].particleSize);
       
                  glBegin(GL_POINTS);
                      // Set colour to yellow on the way up, then whatever colour firework should be when exploded
                      if (fw[loop].hasExploded == false)
                      {
                          glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
                      }
                      else
                      {
                          glColor4f(fw[loop].red, fw[loop].green, fw[loop].blue, fw[loop].alpha);
                      }
       
                      // Draw the point
                      //glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
                      glVertex2f(fw[loop].x[particleLoop], fw[loop].y[particleLoop]);
                  glEnd();
              }
       
              // Move the firework appropriately depending on its explosion state
              if (fw[loop].hasExploded == false)
              {
                  fw[loop].move();
              }
              else
              {
                  fw[loop].explode();
              }
          }
       
          // ----- Stop Drawing Stuff! ------
       
          glfwSwapBuffers(); // Swap the buffers to display the scene (so we don't have to watch it being drawn!)
       
      } // End of drawScene function

      Another thing you might want to try is blurring the entire scene before drawing your new frame, which’ll give you an effect like this: Per-Pixel Collisions (uses blur filter on the stage instead of clearing it), although I’ve not used any blur filters in OpenGL yet, so you’d be on your own there.

      Anyways, hope this helps!

      1. Hi,
        with that modification it now works as it should, thanks.
        I think I’ll stay clear of FBOs for the moment, enough other things to understand first – this is my first experiment with openGL, so I still have a good way to go :)

        Thank you for your help!

        Frank

      1. Sounds good.. :) I’m start learning OpenGL ES too and I would greatly appreciate your help about opengl es and how to draw this nice particle effect.
        Your blog is already bookmarked and in my NetNewsWire month ago ;)
        Thank you .

  3. Hi there,

    I am trying to run your code but I got a bunch of errors. Some are for linker and the other is for checkGLError(“InitGL”);.

    I tried to insert all the lib and ddl but nothing.
    Can you help?

    Thank you

    1. There’s a couple of problems with the code as it turns out – I’ll clean it up and upload a self-contained Visual Studio 10 version this evening (Australian time).

      1. I fixed the code in the post, and here’s a completely refactored and improved C++ version for Visual Studio 2010. It doesn’t use GLEW or GLee at all – just GLFW, and comes with the libs to run from VS2010: Fireworks-Nov-2012.zip.

        Best wishes,
        r3dux

        (Didn’t get it done last night as I was down in Melbourne seeing the mighty Radiohead @ Rod Laver arena ;-) )

  4. Greetings. Attempting to utilize your firework program in order to create working explosions for my game senior project. Having trouble because, when compiling, it says that GLint and GLboolean are not valid types. Don’t know how to fix this. Please reply ASAP.

    1. The GLxxx types are just typedefs which can aid portability of programs between platforms, and they should be defined in GL.h (on my Windows machine this equates to C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include\gl\GL.h).

      So either include the file with:

      #include <gl/GL.h>

      or, you could just replace GLint with int, GLboolean with bool etc.

      1. Me again. It started giving me those errors with the header file because it wouldn’t properly read the members when including gl.h directly. I thought it would be fixed by instead changing it to include glfw.h in there, but instead it gave me a whole ton of linking errors in Fireworks-with-Trails.cpp.

  5. Hello again. For some reason, having trouble linking GLFW to the main program., even though I’m sure that I have it properly included in my Visual Studio 11 folder. Can you offer any help as to sorting this? I’d try using the GLFW walkthrough, but I have no complete idea what to do since I don’t use any of the compilers it mentions besides Visual Studio.

    1. To link in GLFW you need the header file (glfw.h) and the lib (GLFW.lib).

      The easiest way to link them is to just place both files in the same location as your main.cpp file, then add the lines:

      #include "glfw.h"

      and the opengl and glfw libraries with:

      #pragma comment(lib, "opengl32.lib")
      #pragma comment(lib, "GLFW.lib")

      So you’ll end up with something like this:

      #include <iostream>
       
      #include "glfw.h" // Include the GLFW header
       
      // Include the OpenGL and GLFW libraries
      #pragma comment(lib, "opengl32.lib")
      #pragma comment(lib, "GLFW.lib")
       
      int windowWidth  = 800;
      int windowHeight = 600;
       
      int main()
      {
      	// Initialise glfw
      	glfwInit();
       
      	int redBits     = 8, greenBits = 8,  blueBits    = 8;
      	int alphaBits   = 8, depthBits = 32, stencilBits = 8;
       
      	// Create a window
      	if(!glfwOpenWindow(windowWidth, windowHeight, redBits, greenBits, blueBits, alphaBits, depthBits, stencilBits, GLFW_WINDOW))
      	{
      		std::cout << "Failed to open window!" << std::endl;
      		glfwTerminate();
       
      		return 0;
      	}
      	else
      	{
      		std::cout << "-------- Window Properties --------" << std::endl;
      		std::cout << "Accelerated:      " << glfwGetWindowParam(GLFW_ACCELERATED)      << std::endl;
      		std::cout << "Red bits:         " << glfwGetWindowParam(GLFW_RED_BITS)         << std::endl;
      		std::cout << "Green bits:       " << glfwGetWindowParam(GLFW_GREEN_BITS)       << std::endl;
      		std::cout << "Blue bits:        " << glfwGetWindowParam(GLFW_BLUE_BITS)        << std::endl;
      		std::cout << "Alpha bits:       " << glfwGetWindowParam(GLFW_ALPHA_BITS)       << std::endl;
      		std::cout << "Depth bits:       " << glfwGetWindowParam(GLFW_DEPTH_BITS)       << std::endl;
      		std::cout << "Stencil bits:     " << glfwGetWindowParam(GLFW_STENCIL_BITS)     << std::endl;
      		std::cout << "Accum red bits:   " << glfwGetWindowParam(GLFW_ACCUM_RED_BITS)   << std::endl;
      		std::cout << "Accum green bits: " << glfwGetWindowParam(GLFW_ACCUM_GREEN_BITS) << std::endl;
      		std::cout << "Accum blue bits:  " << glfwGetWindowParam(GLFW_ACCUM_BLUE_BITS)  << std::endl;
      		std::cout << "Accum alpha bits: " << glfwGetWindowParam(GLFW_ACCUM_ALPHA_BITS) << std::endl;
      	}
       
      	// Set the window title
      	glfwSetWindowTitle("GLFW be linked, yo!");
       
      	system("pause");
       
      	return 0;
      }

      Instead of using the pragma directive you can add in the library name yourself by going to:
      Project | [your-project-name] properties, and then in the Configuration properties | Linker | Input | Additional Dependencies text box you can add in the name of the library/libraries you want linked in. But generally I find the pragma directive works fine.

      Cheers!

  6. I don’t think there’s an accumulation buffer available in OpenGL ES (or not in early versions) – but there are a few ways to get trails.

    The easiest would be to NOT clear the colour buffer between each frame drawn, and instead draw something like a 20% opacity black quad over the top of everything – this would give you 4 frames worth of trails as previous drawn things go 80/60/40/20/00 percent transparency. You’d still want to clear the depth buffer properly if working in 3D.

    Another way is to have a series of ‘rolling’ render buffers, and render each frame to a different buffer – so, for example, if you had 3 render buffers you render to 0 then 1 then 2 then 0 then 1 then 2… To get the trail effect you simply combine (average) each frame. There’s a good example of this in the OpenGL Superbible 4th Edition.

    Unfortunately I have no OpenGL ES code for any of this, and don’t use iOS at all!

  7. Dear Sir r3dux,

    I really like the algorithmic process you have incorporated into this wonderful fireworks display given the elegant physics you have demonstrated to achieve a dramatic and realistic particle system which mimics a fireworks phenomenon. I have a question since I cannot see it mentioned.

    What license is this source code licensed under. I would be very interested in using it as a congratulations background demo screen for one of my puzzles. However I would like your permission to do this and of course, I would be happy to include your name as a contributor of the solitaire game (author of the animation) on the credits screen.

    1. Hi Cero – I’m glad you liked the fireworks effect, there is no specific license on the code, which I would imagine places it into the public domain by default.

      Please feel free to use the code in whatever way you’d like for any purpose at all.

      You don’t have to give me a credit in your game, but if you’d like to that would be very nice of you.

      One thing you might like to fix is that the shape of the fireworks explosion is somewhat “square”, because the the direction for the firework particles is a random value between -4.0f and +4.0f (lines 75 & 76 in Firework.cpp). What you really need to do is normalise the vector made by the horizontal and vertical directions (i.e. so it has a ‘unit length’ of 1.0f) and then scale it up so the particles move at the speed you want (i.e. multiply by 4.0f in this case).

      If you’re having any trouble implementing this then I’m happy to help, but do have a crack at it – learning is fun =D

      Cheers,
      r3dux

  8. Hi r3dux,

    Thanks for the tutorial and the code. I was trying to implement your code on Xcode. however, there were some errors coming out. Mainly it is GLFW variables undeclared. I have included the GLFW files in Xcode (#include ). Do you know the reason and solution for this?

    The errors are:
    – Use of undeclared identifier ‘GLFW_Key_Exsc, GLFW_OPENED, GLFW_WINDOW
    – No matching function for call to glfwSwapBuffers

    1. Hi there,

      This article was written a while back when GLFW2 was the most recent version – but you’re probably using GLFW3 – and there are some changes. You can check them out here: http://www.glfw.org/docs/3.0/moving.html.

      In GLFW3, GLFW_KEY_ESC is now GLFW_KEY_ESCAPE. As for the rest, I’ve just uploaded a basecode project so you can take a look at how that’s put together and modify your own code accordingly. You can find the project here: GLFW3_Basecode_2016_06.zip.

      Cheers,
      r3dux

  9. I’m confused about how these files fit together… I’m working in Visual Studio. Is it possible to get a complete project folder – one that has a solution – so that I should be able to open it in Visual Studio and run it?

    1. Okay, I’ve put together a Code::Blocks version for Windows for you.

      You can find it here: Fireworks without accumulation buffer.zip.

      This is using fixed pipeline OpenGL (which we should not be using) and GLFW2 (which we should not be using) – there’s a working executable in the bin/Debug folder so at least you can see what it does in case you’re having difficulty compiling it (although the GLEW and GLFW [2] libs are included).

      Oh, and it crashes when you hit escape. Why? I’ve no idea. It’ll be GLFW2 related, but as mentioned – we shouldn’t use that.

      See if you can adapt it to Visual Studio from the above (i.e. make an attempt). If you’re really struggling I’ll do a VS version for you – but if I do all the work you don’t learn much, yeah?

          1. Hello!
            I recently downloaded ur awesome project. Could you please give me a hint to how to make particle getting bigger while going up in the air.Hope u notice me :)
            I use codeblocks version

            1. Because this code uses (old) fixed-pipeline OpenGL you can use glPointSize for this. See: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPointSize.xhtml.

              Each firework has a particleSize property (defined in Firework.h), and the particle size gets set in the constructor when a firework is instantiated.

              So what I’d probably do is modify the Firework::move method to be like this (there’s just one line added):

              void Firework::move()
              {
                  for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
                  {
                      // Once the firework is ready to launch start moving the particles
                      if (framesUntilLaunch <= 0)
                      {
                          particleSize += 0.003f; /*** NEW! Increase particle size as the fireworks travel upwards ***/
               
                          x[loop] += xSpeed[loop];
               
                         // etc...

              Then, because the main code sets the particle size before drawing each particle in the firework that new (i.e. increased) particle size will be set as the size to draw the particle, with the end result being that the particles increase in size as they travel upwards.

              Cheers!

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.