Easy volumetric explosion in Unity3D

When Unity3D realeased their Unity4 update, they realeased a tech demo along with it: The Butterfly Effect

Unity4 tech demo: The Butterfly Effect

After seeing that, it made me want to make a volumetric explosion like the one you can see at the end.
So that’s what I did.

No DirectX11

Now they made that with directx 11, and my graphics card doesn’t support that, so I can’t really do the same.
So instead I tried to develop a simple way to create something that looks similar, without using any fancy stuff.

Here’s what the end result looks like:

explosions  Capture72
(ignore the weird scene-setup please)

Now I’m not saying the explosion I made looks as good as the one in the butterfly effect,
but it is volumetric, fairly inexpensive, doesn’t use alpha blending (so no sorting issues), and it writes correctly to the depth buffer.

And I like it, it looks kinda cartoony, and maybe too round, but I think it can work.

So How does it work?

In essence, it’s a sphere with displacement maps that interpolate over time,
And the color (and emission) is in relation to the extrusion amount (displacement), by using a ramp texture.

How to make it:

I’ll explain it step by step, but keep in mind that I assume Unity3D and basic game development and programming knowledge. (as well as basic Unity shader knowledge)

Step 1, making a sphere

I started of in Blender, but I’m sure most 3D modelling tools can do the same.

First create a sphere, with enough subdivisions for the displacement to work.
I used an Icosphere, instead of the default UV sphere, because vertices are more evenly spread across the surface.

Unwrap it, and make sure all vertices are within bounds.
Default automatic unwrapping should be sufficient, all 3D modelling tools should be able to unwrap a sphere properly.

Step 2, creating the displacement maps

Apply a material on the sphere with a couple of procedural textures (grayscale).
Bake these into a texture.
Make 2 more variations of these procedural textures and bake these as well.

Using Gimp or Photoshop (or anything simmilar), combine these grayscale textures into 1 texture, by placing each into a different channel (RGB), creating something like this:
You could make a 4th texture and place it into the Alpha channel if you want, but I didn’t bother.

Step 3, creating the ramp texture

My ramp texture looks like this:

The ramp texture is used to determine the colors of the explosion, the alpha value is used for emission.
The left pixels are used for the areas of the explosion that are only slightly extruded, the right pixels are used for the areas that are most extruded.

Step 4, creating the shader (partially)

The shader I made is a surface shader, that way the inemissive parts are properly lit, but If you don’t want that, you can write it as a simple vertex/fragment shader.

Start off with 4 properties:
_RampTex (“Color Ramp”, 2D) = “white” {}
_DispTex (“Displacement Texture”, 2D) = “gray” {}
_Displacement (“Displacement”, Range(0, 1.0)) = 0.1
_ChannelFactor (“ChannelFactor (r,g,b)”, Vector) = (1,0,0)

In the Vertex shader we offset the vertices based on a texture lookup using the displacement texture.
The color we get from this contains the displacement value according to all 3 displacement textures (RGB).
By using the float3 property I called _ChannelFactor (that is set in a script each frame, see lower), we determine the actual displacement, by taking the sum of the 3 values multiplied by the corresponding RGB value.

In the Surface shader, we do the same texture lookup,
using that value as the UV.x (UV.y can be anything), we do another lookup using the ramp texture.
We assign that color as the Albedo color, and the color multiplied by the alpha as Emission.
We could also assign the color in the Vertex shader, but then the colors would be interpolated between the vertices, whereas if we calculate the color in the surface/fragment shader, we get a correcter result.
But assigning it there is faster, as it would discard the need to do the displacement texture lookup in the surface shader again, so decide yourself what’s more important.

Step 5, animating the displacement

Now create a script, attached to the explosion, with a single public variable, the loopduration,
in the update loop we will determine the ChannelFactor, and assign it to the shader.
I used 3 sin functions (1 for each channel), and made sure their sum is 1 (though this is not necessary).

float r = Mathf.Sin((Time.time / loopduration) * (2 * Mathf.PI)) * 0.5f + 0.25f;
float g = Mathf.Sin((Time.time / loopduration + 0.33333333f) * 2 * Mathf.PI) * 0.5f + 0.25f;
float b = Mathf.Sin((Time.time / loopduration + 0.66666667f) * 2 * Mathf.PI) * 0.5f + 0.25f;
float correction = 1 / (r + g + b);
r *= correction;
g *= correction;
b *= correction;
renderer.material.SetVector("_ChannelFactor", new Vector4(r,g,b,0));

It should look something like this now:


Step 6, animating the explosion

Now the explosion loops endlessly, but we want it to grow and change color over time, and it needs to “fade out” somehow.

Add 2 more properties to the shader: the minimum value and maximum value for the ramp texture.
They determine the range of the ramp texture to use, by animating this (in an AnimationClip, or in code), you can change the look of the explosion over time.

Add another property to the shader: the clipping value.
The clipping value determines at what displacement amount to clip (cut holes in the mesh), by animating this (in an AnimationClip, or in code), you can fade out the explosion over time.
Unfortunately, this is a sharp cut, but because the displacement is still animated, the holes constantly change shape, making it not that big of an issue (IMO).

To make the explosion grow, simply animate it’s scale in an AnimationClip or in code.


Step 7, finishing touches

Add a point light inside the explosion (and animate this aswell).

You can also place a smoke particle system inside it, so that when the explosion starts clipping away, it looks like it fades into smoke.

I also made the shader 2-sided (clipping off), so that when the camera is inside the shell, you still see the explosion.

Using a Bloom or Glow post effect also helps sell the effect.

And that’s it, all done!

Full shader code:

Shader "Custom/Explosion" 
		_RampTex ("Color Ramp", 2D) = "white" {}
		_DispTex ("Displacement Texture", 2D) = "gray" {}
		_Displacement ("Displacement", Range(0, 1.0)) = 0.1
		_ChannelFactor ("ChannelFactor (r,g,b)", Vector) = (1,0,0)
		_Range ("Range (min,max)", Vector) = (0,0.5,0)
		_ClipRange ("ClipRange [0,1]", float) = 0.8

		Tags { "RenderType"="Opaque" }
		Cull Off
		LOD 300

		#pragma surface surf Lambert vertex:disp nolightmap
		#pragma target 3.0
		#pragma glsl

		sampler2D _DispTex;
		float _Displacement;
		float3 _ChannelFactor;
		float2 _Range;
		float _ClipRange;

		struct Input 
			float2 uv_DispTex;

		void disp (inout appdata_full v)
			float3 dcolor = tex2Dlod (_DispTex, float4(v.texcoord.xy,0,0));
			float d = (dcolor.r*_ChannelFactor.r + dcolor.g*_ChannelFactor.g + dcolor.b*_ChannelFactor.b);
			v.vertex.xyz += v.normal * d * _Displacement;

		sampler2D _RampTex;

		void surf (Input IN, inout SurfaceOutput o) 
			float3 dcolor = tex2D (_DispTex, IN.uv_DispTex);
			float d = (dcolor.r*_ChannelFactor.r + dcolor.g*_ChannelFactor.g + dcolor.b*_ChannelFactor.b) * (_Range.y-_Range.x) + _Range.x;
			clip (_ClipRange-d);
			half4 c = tex2D (_RampTex, float2(d,0.5));
			o.Albedo = c.rgb;
			o.Emission = c.rgb*c.a;
	FallBack "Diffuse"
About these ads

28 thoughts on “Easy volumetric explosion in Unity3D

  1. Hi Steven, can you share the full code for the surface shader. Its my first time coding shaders and i cant get it to pick from the ChannelFactor. Please
    FYI, this is definately not an Easy Volumetric Explosion, I have the eyebags to proove it.

  2. haha, it’s easy compared to the volumetric explosion they used in the butterfly effect clip, as it’s it’s just a combination of fairly simple shader stuff.
    If you’re new to shaders however, then it’s not so easy I suppose (especially in Unity).
    In my post I assumed the readers to have some Unity surface shader knowledge.
    Alright, I’ll add the full shader code to the post.

  3. Hello Steven, this method is just awesome, thank you
    Anyway, i’m facing a problem.
    I’m using Unity 4 and after i created a shader with your code,
    i saw there is no slot for ramp texture on inspector
    so where do i have to put the ramp texture?
    the shader itself working perfectly, but without no ramp texture
    the explosion become grayscaled
    can you help me with this problem?
    it will be glad to me if you may send a Unity package to my email below:

    Kind regards,

  4. Ah, woops, the shader code I posted at the bottom of the post used a different name for the ramp texture than in the rest of the post (It was called “Base (RGB)” instead of “Color Ramp”).
    I fixed it now.
    In case you’re not familliar with writing shaders in Unity; the “Properties”-part of the shader defines what can be set in the editor.
    The line _RampTex (“Color Ramp”, 2D) = “white” {} defines a texture slot (2D) that is called “_RampTex” internally and displayed in the editor as “Color Ramp“, with the default texture beeing a full white texture (= “white” {}).

  5. Hi Steve,

    Just a quick thanks for this post – very useful. I have managed to recreate your volumetric explosion effect and added a few lighting effects to further enhance for a small game/POC I was working on.


  6. Pingback: Getting started with Unity 3D, and a small game prototype | Hobbyist coder

  7. If I remember correctly, it’s a fairly basic particle system using one of the default smoke textures with an alpha blended material.
    The particles fade in fast and out slowly, and they scale a little up over their lifetime. They also have a random rotation and a slow random rotational speed.
    The only other important thing, I think, is that you have to time them properly so they are only visible (more or less) when the explosion starts clipping away. (you don’t want to see the smoke when the explosion is still completely fiery)

  8. No homo but i love you XD i have been looking for a shader like this for like ages. I will credit you in my game you are great XD :)

  9. Hey, first of all im very begineer at the shader stuffs. I did all of your steps, but when i apply the material into my blender model at unity, the sphere is totally black colored… May i ask you to can you make a unitypackage which include the model, material, and the final explosion?

  10. strange, are you sure the shader is correct? (you can compare with the one at the bottom of the post). And the material’s parameters are set properly?

    I’ll see if I can find some time to make a unitypackage and upload it somewhere.

    The reason I hadn’t yet was because I thought this way (without having the final version available) caused people to make it themselves, allowing them to understand it better and allowing them to make changes where they see fit, causing them to learn from it and create something unique, rather than just using the one I made without understanding how it works.

  11. Great article! I am just sad to not have a render as good as yours, I guess you kept some stuff secret and I understand. But could you tell me about the sphere, how many triangles does it have ? I am wondering if my poor rendering comes from the fact I don’t have enough polygons.

  12. Hey Steve, thanks for this cool little tip, however I’m not getting any displacement :/ Do you know why this could be? I’m not getting any compiler errors. Also please could you post the script, preferably in C# just because I’m not sure if I’ve done it right. Many thanks in advance.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s