Thursday, September 26, 2013

Unity Dust Particle Shader

You can now buy our particle shader on the Unity asset store for $5!

NOTE: If you're seeing WHITE SQUARES, please try adding this line: 
AlphaTest Greater .01
under the line: 
Tags {"RenderType"="Transparent" "Queue" = "Transparent" "IgnoreProjector" = "True"}
See below for more info. I'll release an update to the shader soon with this in there by default. Sorry for the confusion!

We're using Lou's painting as inspiration for our first test shot. I started developing a shader for Unity's particle system to create the close up floaties. The tricky part here was that I had no way of accessing particle ids in the shader, which meant that I couldn't randomize the particles in the shader. I wanted to randomize opacity, color and flashes. I originally tried creating a perlin noise texture look up in screen space based on the particle's screen space position to get a random value for that particle. That works for our purposes since our camera is static. I didn't get very good results with this and instead varied the opacity in the particle system by manually setting Alpha to be 0 or 255 in Color over Lifetime. The varying opacity was used to drive a super bright emission and the color naturally looked random because of the different opacities.  

o.Emission = lerp(0,_Emission, smoothstep(_FlashSpeed,1,o.Alpha));

 My dust motes are made up of two particle systems: Large blurry dust and micro dust.

Large Blurry Dust
For the coloration, I wanted to create a "photographic" look. I looked a lot at images like these I found on google:

Even though the overall mote looks circular, it is actually a cutout of an imperfect circle texture I painted. This just adds a subtle randomness to all of them as they slowly spawn and rotate (I think anyway).

    o.Alpha = o.Alpha*tex2D(_Shape,IN.uv_Noise.xy).r;
//otherwise make a circle
o.Alpha = 0;

I wrote a function to create a bright yellow ring near the edge of the particle that fades toward the center. As it fades, the color turns bluish.

float dist = distance(IN.uv_Noise.xy, float2(.5,.5));
    o.Alpha = o.Alpha*max(1.0*pow(dist/(.5-_Blur),2),(1-_Transparency));
    //add a shadow color as the particle fades
    o.Albedo = lerp(_ShadowColor.rgb, o.Albedo,o.Alpha);


Finally, I have a noise on top that creates some pink speckles for a low resolution pixelated look.

o.Albedo = lerp(o.Albedo,_Speckles.rgb,tex2D(_Noise,(IN.uv_Noise.xy*.1+_Time*.01)).r);

As as bonus, I created a custom lighting model for these dust motes which simulates light scattering through them.

half4 LightingParticle(SurfaceOutputCustom s, half3 lightDir, half3 viewDir, half atten) { 
    half4 c;  
    c.rgb = s.Albedo;
    c.a =lerp(0,s.Alpha, 1-saturate(dot(normalize(viewDir), lightDir)));
    return c;

NOTE the lighting model does not work with multiple lights and can result in the particles looking blown out, or white. Since the particles are alpha mapped billboards, they will look like white squares. There are a couple ways to fix this. One is to add "alpha" to the pragma surface line. This basically overrides the lighting model (for whatever reason):

#pragma surface surf Particle alpha

If you want to preserve the ability to have a light source create a scattering effect, add the following line:
AlphaTest Greater .01
under the line: 
Tags {"RenderType"="Transparent" "Queue" = "Transparent" "IgnoreProjector" = "True"}
I believe the first light that was created in the scene is the only light that can create the scattering effect.

Micro Dust
This one was much simpler. Basically white and the cutout is an irregular polygon texture.

And here is my result:
If nothing loads, watch with Vimeo or try installing Unity Player .


Anonymous said...

Beautiful! Would you mind sharing the unity file? Or making a tutorial?

Nancy said...

Thank you so much! I'd be happy to answer any specific questions if you shoot me an email or comment here. Unfortunately I don't have much time right now to write up a tutorial. It's not very complex, but if you are new to shader writing I can see how it might be.

Nancy said...

I had some time today to clean up and post some code. Hope that helps!