s h a d e r  w r i t i n g:     f i r e


The purpose of this project was to write a surface shader that when used in conjunction with the previously written (water) displacement shader would give a simple object the appearance of being an animated flame. As with the previous assignments, the emphasize was on the vital need to carefully observe and document a natural phenomena before creating a digital version of it.

Final Render

The Final Render.

My Shader

float remap(float input, minval, maxval)
{
float  clamped = clamp(input, minval , maxval);
float  result = (clamped - minval)/(maxval - minval);
return result;
}
  
surface
fire_final(float    Kfb = 1,
                    frmulti = 1,
                    rim_width = .9,
                    freq = 8,
                    seperation = .5,
                    height = .05,
                    offset = 0,
                    alpha1Min = .3,
                    alpha1Max = .5,
                    alpha2Min = .4,
                    alpha2Max = .6;
            color     top = 1,
                    side = color(1.0, 0.580, 0.094), 
                    bottom = 0;
            string    firespace = "shader";)
{
  
normal nn = normalize(N);
normal nf = faceforward(nn, I);
vector V = normalize(-I);
point pp = transform(firespace,P);
float facingRatio = V.nf;
float invFacingRatio = 1 - facingRatio;
color bottom2 = bottom * .08;
  
float tt = (t + (noise(pp*freq + offset)-seperation)*height);
  
float alphaBlend1 = smoothstep(alpha1Min, alpha1Min+alpha1Max, tt);
float alphaBlend2 = smoothstep(alpha2Min, alpha2Min+alpha2Max, t);
  
color edge = mix(top, side, facingRatio - .2);
  
color topEdge = mix(top, edge, alphaBlend2) * Kfb;
  
  
  
color    surfcolor = mix(bottom2,topEdge, alphaBlend1);
  
float facingRatioReFix = frmulti * invFacingRatio + .05;
  
float facingRatioFix = mix(facingRatioReFix, alphaBlend1, alphaBlend1) ;
float edgeBlur = smoothstep(1.0 - rim_width, 1.0, facingRatio);
  
  
float result = remap(facingRatioFix, 0, .5);
  
  
Oi = edgeBlur * result;
  
Ci = Oi * surfcolor;
  
Oi = 0;
}
My setup for the final render was done rather simply. I took the concept laid out in the flames and opacity tutorial on Fundza. The idea is a simple one, take multiple planes stacked up on each other and have 3d noise edit certain parts of the shader itself. In my case I took it a few steps farther. If you look at this document we are introduced to a few interesting ideas. One of which is having color add upon itself. If you use the technique they introduce you will notice your colors will become very blown out, but should you multiply those colors by a scalar value you can scale down(with a large amount of control) the colors so when they add back up it will be selectively blown out. While in the technical document they do not pre-multiply the opacity to the color, I found that for my setup it helped my overall look to premultiply. I should note: with my playblast I only have one sphere showing in order for you to see the point light and joint setup inside.

Within my scene I have two lights total, a point light to simulate the lighter flame light and a directional light to very slightly brighten up the scene. The flame itself is, though not shown in the playblast, 20 spheres scaled slightly inward and moved very slightly downward using the duplicate special tool. The scale downward and move down gives my final model a glow that can be adjusted without having to do anything special in post.

If you look at the shader itself there is a good bit of artistic controls built into it. This comes from what I learned from my ST project as well as not exactly knowing the actual size and UVs of my final fire. Actually, within the development phase of the rig and final animation my flame changed 4 times in order to match it more accurately to my reference. I employed two facing ratio tricks to achieve my final image, an edge blur based off of a regular facing ratio lookup, and a cut-out from the center using the inverse facing ratio.

My Shader in Layers

Within the top row you will see the progression building to the final opacity. In the row below that is the build up of the color. Each full image will tell what variable name they correspond to, excluding of course the final opacity, color, and combination of the both.

The Rig and My Expressions

pointLightShape1.intensity = (joint4.translateX*14);
  
  
vector $pos = `xform -q -ws -translation pSphere44.vtx[381]`;
vector $pos2 = `xform -q -ws -translation pSphere30.vtx[381]`;
  
vector $vec = $pos2 - $pos;
vector $pos3 = $pos - 1.4*$vec;
  
pointLight1.translateX = $pos3.x;
pointLight1.translateY = $pos3.y;
pointLight1.translateZ = $pos3.z;
float $blah = clamp(-.1,1,cos(time*2+1.75))*.1;
joint4.translateX = abs(sin(time*40))*$blah+.2;
  
float $phase = cos(time*2)*3+5;
joint2.rotateZ = abs(sin(time*.75+$phase)*3);
lighter3:RenderManShader1.Kfb = lighter3:setRange1.outValueX;
  
lighter3:RenderManShader1.offset = sin(time*2)
The entire animation was done through expressions. This was a personal choice not one of necessity. Though it did make a few things easier to achieve in my final render. I noticed in my reference that as the flame seemed to vibrate the intensity changed in a relative way to the total area of the bright white/yellow part of the flame. I hoped to somehow replicate this. I ended up using a scalar multiplier on the actual translation of that last joint. So as it rose and fell it would pass those values to be multiplied by 14 and become the final intensity value. As to the position of the point light, I used a trick I taught myself through the course of the quarter in MEL scripting. I simply find the position of two points in space, make a vector out of them and do some vector math in order to achieve a final position.

If you look in the next code box you can see how I animated the joints themselves. I created a complex trig wave based on time. The functions give me the oscillation that you see in the final render.

The final code box is the simplest of all. The first line of code is modifying the scalar value I mentioned earlier. This was to give the illusion that the flame was flickering in intensity not only its light contribution to the scene. The final line of expression was a personal artistic touch I added in. If you look at the bottom lip of the white/yellow portion of the flame, you can see a bit of noise that gives the illusion of varying fuel density. The entirety of that bottom lip was done based on object space 3d noise.

Reference

Reference.