Shockwave VFX

Mentorship project in collaboration with Krill Interactive

ART & Gameplay

Shader Shockwave

The following videos show the nodes setup used for the main shockwave shape. It consists of textures with polar coordinates + UV Distortion * SDF Mask.

Polar coordinates setup

Distortion setup

SDF Mask

Curve Atlas


I used a curve atlas so that I could have multiple colours available in one asset, and select between them parametrically in the material. This is beneficial vs. creating a separate texture for every variation, since I don’t need to go into Photoshop to create a new gradient texture every time. This way it’s more procedural and artist-friendly.



6-Point Lighting


This method allows for more volumetric smoke, even though it is still a sprite (quad). Instead of using 6 actual lights, I used a channel-packed mask representing the six directions (left, right, up, down, into, outo), previously rendered in Houdini with the actual 6-light setup.

Shader 6-Point Lighting

Niagara Systems

Optimization

Niagara System, CPD & NDC

For optimizing the Niagara System I ended up trying two different methods: Spawn Groups and Visibility Tags. Both have the same objective: to reduce the number of emitters. Without this approach, I would have used 6 different emitters. As you can see from the video above, I used 4. “Base Alpha” + “Circle Glow” + “Shockwave” were all spawned from the same emitter. Since each emitter carries a CPU cost, this reduction in the nº of emitters improves performance.

CPD


Custom Primitive Data was implemented through a single Blueprint and a simple shader, allowing the three cubes to share one material while still showing different colours. When the player overlaps a cube, its colour index is passed through the Blueprint and used to drive the NS colour. The setup consists of one parent Blueprint with three child Blueprints. This reduces the number of draw calls from three to one, while the main benefit is improved workflow and modularity.

NDC

Niagara Data Channels allow data to be shared between Blueprints and Niagara without spawning a new Niagara System actor for every interaction. In this setup, the Blueprint sends the impact position and hit normal to the NDC, which stores the data and makes it available to the Niagara System. Niagara then reads this information and spawns the corresponding particles at the correct location and normal. This approach reduces the need to repeatedly spawn Niagara actors, limiting the number of system calls and improving performance (see videos below for comparison).


With NDC, I combined multiple individual Niagara systems into a single shared system. This reduced the number of Niagara calls and significantly lowered CPU overhead, particularly on the Render Thread. In the left video (without NDC), the call count reaches 66, where as in the right (with NDC), it only reaches 5 calls. As a result, the game can handle multiple shockwaves more efficiently, without sacrificing their visual style.