Realtime voxelbased volumetric
smoke grenade

Download & GitHub

What is this project?

For my specialization at The Game Assembly, I decided to re-create the Volumetric Smoke Grenade from the game Counter-Strike 2 by Valve Software.

We were given 7 weeks for our work, and supposed to work at 50% speed (around 4 hours per day).

This work is based off the YouTube video by the Graphics Programmer Acerola. The goal was to recreate and study the contents of the video in my own DX11 engine, with the stretch goal being to improve on the work further, making it even more faithful to the original Valve implementation. I also heavily referred to the sources provided in the video description, first and foremost.

I've had the idea for this project for a long time. I've been interested in Volumetrics ever since a good friend of mine showed how the clouds looked like in Red Dead Redemption 2. Ever since I've been sure I wanted to do some kind of Volumetric thing, but more than all, I wanted my work to look cool! I also weighed doing Godrays for a while, but settled on this since it's more interactive and gameplay adjacent.

What is a Voxel?

Voxels are most known for being used in Minecraft, but they're really neat for making simplified versions of detailed environments.

In this case, Voxels are the backbone of the smoke's propagation logic. Having the smoke check against a very complex environment would be super expensive- so I simplify what to check against for huge performance gains. Instead of checking against a scene with what-could-be potentially millions of vertices, I check against a grid of Axis-Aligned Bounding-Boxes, our grid of voxels. The grid marks each cell as either empty, filled, or obstructed. It additionally has the added bonus of making it simpler to calculate the propagation of the volume.

What is Propagation?

Propagation refers to how the smoke interacts with the environment. Counter-Strike 2 has pretty advanced propagation logic, enabling the smoke to navigate around scene geometry and behave realistically, like a real smoke volume would. For instance, in a confined space with small openings, like a vent, the smoke expands outward, filling the area naturally, while also retaining its ellipsoid shape in open areas.

The propagation logic is calculated through a Compute Shader. The logic that drives the smoke expansion is just the flood-fill algorithm, tied to an interpolating function.

When smoke is created, I set a seed voxel with a certain fuel value. Each voxel checks its neighbours for any empty cells, while ignoring cells occupied by scene geometry, and fills the available space the following frame. This is repeated until the smoke is outside its allowed range, or until it runs out of fuel, while smoothly interpolating the result to get that smooth growth effect. 

Doing this results in the smoke growing from its core seed point and convincingly "climbing" around obstacles dynamically.

What is Mesh-Voxelization?

Mesh Voxelization is the process of turning level into voxels, marking the level geometry as impassable in the voxel grid. This is a very expensive operation, which is why this information is baked beforehand. The separating axis theorem is used to check every triangle in the scene against every voxel in the grid to see if they overlap each other. If they do, that voxel is marked as obstructed. This is also done through a Compute Shader, allowing it to be done in parallel for multiple voxels at once, speeding up the calculation time.

What is Raymarching?

We use raymarching to traverse the rays projected from the camera, stepping through the smoke volume to sample the density at even intervals, while checking we haven't exceeded the depth for that pixel in the scene.

For each step, I check the voxel the sample position to get the remaining fuel value. This is what we use to calculate the density, which is then used to determine the volume’s transmittance; how much light passes through it. The transmittance is modelled using Beer's Law, where light diminishes based on the distance it travels through the volume and the density of said volume.

For a more accurate result, this approximation is refined with a Riemann sum. This involves multiplying each sample’s density by the ray step size and summing the results, yielding the total transmittance. This process effectively integrates the discrete samples into a continuous measure. The transmittance is used to calculate an alpha mask that is blended into the final image. 

To compute the colour and shadows of our smoke, the transmittance is calculated from each sample point to the directional light source. The sun transmittance is multiplied by our base transmittance and sun color to get the smoke's shaded albedo texture. 

The Density of our Volume
The Transmittance of our Volume (Our alpha)
The Albedo & Shadows of our Volume (our color)

What is Worley Noise?

A Type of Voronoi Noise that is generated to give the smoke even more detail and a more cloud-like appearance. Worley noise is generated by randomly distributing feature points across a texture, in our case a 3D one to represent the volume, and for every pixel give it a value between 0 and 1 depending on how far away it is from a feature point. This type of noise is used in Horizon Zero Dawn for their clouds, and there's a SIGGRAPH presentation describing how they used it! 

What did I learn?

I learned a lot from this project- lots of it not even having anything to do with the task itself but rather the management surrounding this project. Setting up a schedule, following it, and adapting it when things unexpectedly change was very insightful to me. It was super refreshing to be in complete control over the pacing for once, and I feel I did pretty okay!

Regarding the work itself, I learned how to use Compute Shaders, a deeper understanding of how DX11 resources are to be used, and how the basics of volumetric clouds work. I also learned a lot about Voxels and you can very efficiently break them down into just a single unsigned integer array, making do with the bare minimum amount of information you require from them for the sake of efficiency. It also reminded me to always check your performance on different hardware, having only ran the raymarching code on our school computers, which have some great hardware. If I'd ran it elsewhere too, I'd have spent a bit more time profiling my code and making it more performant.

What could be improved?

Quite a bit! This project was one of the most fun things I've ever done at TGA, yet there's still so much I wish I had the time to implement! What's finished right now is far from perfect, with many different ideas still circling around in my head on how to improve things. Here are a few:

  • Smoke propagation having a “per-smoke” fuel, rather than just fuel per voxel.
  • Support for Multiple smoke instances at once.
  • Networking support, making sure the smoke looks identical from different clients (e.g in a competitive setting)
  • Adding Curl noise or Bitangent Noise for more realistic smoke visuals.
  • Support for Bullet holes (SDFs, decals).
  • Switching to Delta tracking instead of Raymarching.
  • A Deeper and more thorough understanding of volumetrics.
  • Instantiation of the grid per-smoke instead of one per-map to save memory.
  • Performance Optimizations, more profiling on worse hardware.
  • More efficient compute shader setup and data sharing, cleaner code.
  • Loading the Mesh-Voxelization from file, necessary for larger scenes.
  • Further artistic control to change the visuals of the smoke.

What can you do with this?

What's the Summary?

This blog was mainly focused on being an entertaining summarizing read, so if you want any more technical details, please check out the repository link to see the source code or feel free to contact me with any questions! There were a lot of fun problems I left out due to yapping constraints. You can reach me @ lolgube on X and Discord, or through the contact button on this page. I'd love to hear anything you've got to say! :]

Game Projects @ TGA (2023-Current)

At The Game Assembly, we students partake in 8 total Group Projects and 14 individual Courses.

Projects 1-2: Unity, C#.
Projects 3-4: TGE, C++ DX11.
Projects 5-8: Titan Engine (Custom built by us), C++ DX11.

Applied Graphics Programming - Part 1

How my Scene looked at the end of the first half of the course.

This is, in my opinion, the most noteworthy individual course at TGA. The applied graphics course, or TGP for short, is split into two parts.

The goal of the first part is to create a Window, Set up a Projection Camera, and then to generate terrain and successfully create a plane reflection shader. The idea is to become familiar with Shaders, DirectX, and Graphics Programming in this first part. 

This course *really* sparked my love for Shaders and Graphics Programming. Getting immediate visual feedback on your code, and working with more than just pure data-structures and backends is the most fun I've had coding. Shaders are so cool!! 

Applied Graphics Programming - Part 2

How my Scene looked at the end of the second half of the course. (chaotic, I know)

The goal of the second part of the course is to delve further into more advanced graphical concepts.

Efficiently culling and rendering Spot and Point Lights, various Post-Processing effects like Bloom and Tonemapping, Shadow Mapping, Deferred Rendering (Gbuffer), Bone-Driven Animations, and Instancing.

This lines up with when we start working in our own custom engine- laying the foundation for setting up proper deferred rendering on our own. From here on, aspirations to implement every kind of graphics technique ever documented truly started.

Group Project 7 - B.O.O.M

B.O.O.M is a first person shooter.

I was responsible for setting up instancing to improve our performance, and fixing graphical bugs, as well as adding graphical debug-tools. I added annotations through RenderDoc so that each Instanced-Render could easily identify which VS and PS it used, as well as naming each pass in a scope for ease of debugging. It helped a lot!

Group Project 6 - Spite: Decrepit Depths

Spite: Decrepit Depths is a Diablo-like game.

I was responsible for continuing the setting up of our Graphics-Pipeline, mostly focusing on refactorings for usability.
I also implemented a VFX system that can load in Shaders from Visual Studio Shader Designer straight into our engine.
I also implemented UI (including fonts & text) through DirectXTK.

Group Project 5 - Titan Engine

This project was focused on setting up the foundation for our custom game enigne called Titan Engine.

I also spent a fair amount of time investigating the .HDA file-format from Houdini and if it was possible to integrate into our engine for ease of use. This sadly had to be de-prioritized.

Group Project 4 - Rocking Rodents

Rocking Rodents is a 3D isometric game based on the souls-like (according to steam lol) 'TUNIC'. 

For Rocking Rodents, I was responsible for the Player and their State Machine. This was really fun! Managing states through Enter() and Exit() Methods, as well as implementing the ideas presented in the book "Game Programming Patterns". Most of the time, state machines in these game projects boil down to just a switch-case with enums, but I found it really fun to try to do it "properly" for once! I learned a lot.

This also included a "Camera Lock-On" feature, making the player smoothly transition between animations & states, as well as rotate according to the player "wish-direction". I studied the movement from Tunic including what button presses transitioned to what states, and made sure to implement that as faithfully as I could.
Itch.Io Link

Group Project 3 - Ghost In The Shield 2028

Ghost In The Shield 2028 is a 2D platformer.

This project went through a pretty radical transition, originally starting out as a rhythm-platformer. 

I mostly worked on UI and the Sprite Animation system for this project, making sure our spritesheets were animating properly. I also supplied some Judge Dredd Comics to our artists :^)

Group Project 2 - Soulmates (Google Award Winner!)

Soulmates is a Mobile Puzzle-Game that won an award at a Google Hosted Event! Solve various puzzles to reunite two lost lovers and show the world that love always wins!

For Soulmates, I was responsible for optimizing Unity for our target platform (Mobile), and investigating + setting up cheap pre-baked lighting. Our final solution ended up being a combination of light-probes, blob-shadows, lightmaps, baked GI, and various shaders to fake e.g spotlights and the abyss-depth. This was in collaboration with our wonderful procedural artists. We also made sure to use static batching for the level geometry.

I was also responsible for all of the UI, the Tweening library, the refraction/reflection gameplay element, and the little dynamic animation where the characters look at each other if they're in view or close enough to each other, using inverse kinematics! The latter added a lot of charm for very little work! I also made some mirrors that ended up being unused.

Around the end of the project, we were invited out to Google's Stockholm Office to showcase our games to a list of guest judges from various game companies! (Google, Funcom, King, Resolution Games, Star Stable)
Google Play Link

Sources for winning the award & the event actually existing:
Source1, Source2

Group Project 1 - Beaver Fever

Beaver Fever is a Subway Surfers-like Runner Game.

Beaver Fever was my first game project at The Game Assembly, and it was really fun! Brainstorming and trying to generate ideas from group members, creating an environment where anyone can come with cross-disciplinary feedback without having to worry about stepping on anyone's toes. I tried my best to set a good vibe for the group and make it crystal-clear that we're all working towards the same goal. 

I was mainly responsible for the UI & Misc Fixes, e.g animating the different pickups, adding Death-Quotes, and fixing bugs. I also made the level transition effect.

Game Projects @ LBS (2020-2023)

LBS is a Upper Secondary School in Stockholm.
I studied there as a Game Developer, learning the basics of programming through C# and Unity.
We also had quite a few game projects!

Interstellar Taxi

Interstellar Taxi is a game we made for an International GameJam our school was a part of, where we together with students from other countries formed groups to make a game in about a week's time. 

I made the Main Menu Ship that follows your cursor, the Crazy-Taxi style arrow, and some of the gameplay. I also wrote a bunch of silly lines for the passengers to say. I basically worked on a little bit of everything.
Itch.io

Strange Aeons

Strange Aeons is a First Person Fishing-Game turned FPS. 

This game was our most ambitious, by far, but was limited by time and a little bit of internal drama sprinkled on top. Sadly, the game remains unfinished. The main gimmick was that it was supposed to start out as a normal-ish fishing game, with you rowing your boat and slowly filling up your bucket in this very atmospheric and dread-filled sea. After a while, you hear rumblings and eventually manage to awaken the one and only Kraken/Cthulu, very lovecraftian. This is where the game takes a turn and turns into a giant boss battle instead, giving you nothing but an old rusty harpoon to defend yourself with. This was a hard game to make in under two weeks! It taught me a lot about scope, and making the best of what you have, and especially about getting an early gameplay loop up and running early.

I was mainly responsible for the boat physics and the UI. I think I did the fish-bucket and Harpoon too, as well as the lantern and Unity post processing yet again.

For a more "complete" look at the gameplay, see this link.

Klara

Klara is a First Person Horror game inspired by absolutely everything! This game was made with 2 artists and 3 programmers, built in Unity.

Klara ended up being one of my favorite projects ever, since I ended up having so much creative influence over the project. Inspired by DOOM in certain ways, and Resident Evil in others, we did the best we could with the resources we had and found interesting ways to make something visually unique. 

It taught me a lot about what you can do with very little- and it looks damn good for the amount of knowledge I had at the time too! I also got pretty creative with some things since our artist was already burdened with having to animate the gun and main bad guy, so if you take a look at the flashlight you'll see it's just a bunch of Unity cubes slapped onto each other with whatever texture I could re-use on it, animated as well. Looks super convincing!

I was responsible for the direction, the main menu (fully!), the Unity side of the Visuals (pixel filter, post processing, lighting etc), the flashlight, some parts of the player movement, and the ending and pickups. 

Astronova

Astronova is a classic space Shoot'em'up, featuring a cool boss! 

The base for this game was originally made by me, turning this game project into us improving the art, adding new features, and polishing what was already there. The addition of a boss with multiple unique phases was one of my favourite things about the project. so cool!

Nya Stan

This was the first game I ever made! Nya Stan is a top down action-game with 3 levels, made in Unity in a couple of weeks, part time.

We were a group of about 4 programmers and 2 artists, all being completely new to programming and game development. 

I was responsible for the Level Design and the Unity-Side of the visuals.

This is me!

I love games.

At 9 years old, I convinced my mom to get me Portal 2 for the Xbox 360. I thought the company that made the game, Valve Software, were the coolest guys alive, and I wanted to be just like them. I scrolled through their job listings and picked the one that sounded the coolest, and basically had my life path decided ever since.

I was going to work in games. I found a strong passion for coding at a later age and always felt it was nice having such a steadfast direction as to what I wanted to do in life.

I love the process of game development. Iterating on a game based on feedback over and over again until you have something that you can genuinely tell yourself "hey, what we made is actually pretty good". That's what I want more than anything. To get a chance to improve my skillset enough so that I'll be able to inspire people through the games I helped create, and above all, to make games I can say I'm proud of having helped create.

I find it hard to articulate how much I actually value said creative process, because to me, games are a form of art, just like movies or animation, and should be valued as such. A digital, dynamic, and modular form of self-expression. I want to help make the future of games inspired by what personally captivates me, films, stories, and experiences I love, while also embracing concepts different from what I personally enjoy, since they have still have such a strong appeal to a different group of people.

I want to make games for the players, ensuring everyone finds something meaningful in what help I create, just like how meaningful Portal 2 was to me.

"Wow, being a game developer sure sounds cool"