RSL
Flames and Opacity


return to main index



Introduction

This tutorial develops a self-luminescent surface shader that looks somewhat like a flame. The shader begins as a simple color ramp shader - listing 1.


Listing 1


surface
ramp(float   Kfb = 1;
     color   top = color(1,0,0),
             lower = color(1,1,0))
{
color    surfcolor = mix(top, lower, t);
  
Oi = Os;
Ci = Oi * Cs * surfcolor * Kfb;
}


line

Hard Edged Flames

Using noise(p), where p is a transformed copy of the surface point P, a simple test can be applied to determine whether the 't' location of a micro-polygon is above or below a pre-defined noise value.


flame0   flame1   flame2


Listing 2


surface
flame(float  Kfb = 1,
             freq = 6;
     color   top = color(1,0,0),
             lower = color(1,1,0);
      string spacename = "shader")
{
point p = transform(spacename, P);
color surfcolor = mix(top, lower, t);
  
if(t > noise(p * freq))
    Oi = Os;
else
    Oi = 0;
  
Ci = Oi * Cs * surfcolor * Kfb;
}


Soft Edged Flames

Although the if-else statement is simple it causes aliasing. The problem stems from the way that opacity is assigned a value - its on the basis of a binary decision - on/off, true/false.

To soften the edges of the flames their opacity must be reduced gradually from the true Os surface opacity to zero. The if-else statements must be removed and replaced by the smoothstep() function. When converting an if-else "into" a smoothstep it is generally best to take the first part of the if statement and divide it into two values ie.

       if(t > noise(p * freq))
          /\
         /  \
        /    \
    t-0.1,   t+0.1

The two new values become the first parameters to the smoothstep function. The second part of the if-else statement becomes the final parameter to smoothstep ie.

    if(t > noise(p * freq))
             |
             |________________________
                                      |
    Oi = 1 - smoothstep(t-0.1,t+0.1,noise(p * freq));

Subtracting the value of smoothstep from 1.0 reverses the "dirction" of the flames. The animation shown below demonstrates the effects of softening the edges of the flame.



The final version of this shader uses a parameter called "soft" instead of an explicit value of 0.1.


Extensions

The techniques shown in this tutorial have only been applied to opacity. The color of the flame was created using the mix() function - effectively a simple color ramp. However, the color of the flames could either be created using noise or they could be based on the color ramp modified by a small "noisey" value.

Flames often show sparkle effects as a result of dust and other small particles passing through the flame. Bright white sparkles could be added to the flame by generating a high frequency noise() value. For example, the last line of shader code ie.

    Ci = Oi * Cs * surfcolor * Kfb;

might be modified as,

    if(high_frequency_noise > 0.7)
        surfcolor = color(1,1,1);
    Ci = Oi * Cs * surfcolor * Kfb;

How high_frequency_noise is calculated is left as a problem for the reader - refer to the tutorial " RSL: Writing Displacement Shaders".






© 2002- Malcolm Kesson. All rights reserved.