Dissertation: Smoke Simulation & Rendering
ROLE
Independent C++ & Opengl Graphical Artefact
DESCRIPTION
For my final year dissertation I'm researched the performance difference between two rendering methods of a fluid dynamic smoke simulation.
Along with a 12-page dissertation, I developed a graphical artefact to evidence my findings. This was made using C++ and Opengl.

Simulation & Rendering

Smoke Simulation
I've implemented a standard fluid dynamic smoke simulation. Which centres around several grids, tracking the density and velocities at each point in space, then running them through several functions to create the natural smoke movement. Each grid is a dynamically allocated flat float array using pointers to track and pass through functions.
Other than the advection and diffusion I'm utilising a method called vorticity confinement to add turbulent features to the edges of the smoke.
Saving and Compression
To effectively compare the performance of rendering I had to cut out the simulation generation from each frame's computations, thus I stored the simulations and later read them into the rendering functions.
Although it's only nesseccary to store the smoke's density grid, directly storing each frame would create insanely large files. So I created an algorithm to compress the data for space efficiency, it works similarly to git by only storing changes. To do this, each frame, I split the 3D grid into many blocks and would only store a block if it changed from the previous frame. To read this file, it starts by reading the header, giving simulation information and the size of each frame's header in bytes. The frame's header is a series of long ints, which's bits represent each block and if they're flagged, it reads that block into the simulation.
This component has been the most rewarding to make so far. Learning to maximise use the low level mechanisms of bit manipulation and buffers to create custom file formats and achieving visible improvements, as using my method, storing 120 frames of a 128x128x128 simulation, reduced file size from 1GB to 50MB.
Ray Casting Rendering
This renderer uses a bounding box to represent the whole simulation and employs volume sampling to estimate the colour of the smoke at each fragment.
A 3D texture is constructed from the smoke's density grid and passed to the shaders along with the camera position. Using these two inputs, rays can be calculated from the camera to each fragment then into the simulation. The ray steps through the smoke sampling the texture at each point, accumulating density, to finally calculate the colour and opacity.
Voxel Rendering
This renderer uses a 3D grid of instanced voxels to represent the smoke. Each voxel directly represents a single cell in the simulation, changing it's opacity to portray different densities.
A Vertex buffer is used to send the simulation's density grid to the voxels, and everything else is interpreted from the instance id.
This method is effective on a 128x128x128 grid, rendering the 20 million voxels at 60fps.