RasterTek Fire Shader        

Description


The RasterTek web site contains a tutorial for a fire shader in DX10.
I've implemented this tutorial in Ogre using NVIDIA's cg language. It works just fine for me in OpenGL.
The tutorial page contains images which you can use for the three texture bitmaps required to make this work.
Download them and add them to your resource directory with the cg and material files.

Usage


Create an object and assign the Fire material to it. Here's an example of doing that in code.

::Ogre::Entity* Entity = mSceneMgr->createEntity( "FireEntity", ::Ogre::SceneManager::PT_CUBE );
         ::Ogre::MaterialPtr material( Ogre::MaterialManager::getSingleton().getByName( "Fire" ) );
         if ( ! material.isNull() )
         {
            Entity->setMaterial( material );

            ::Ogre::SceneNode* FireNode = mSceneMgr->getRootSceneNode()->createChildSceneNode( "FireNode" );
            ::Ogre::SceneNode* FireScaleNode = FireNode->createChildSceneNode( "FireScaleNode" );
            // scale the box to 1.5 units
            ::Ogre::AxisAlignedBox BoundingBox = Entity->getBoundingBox();
            float Scale = 1.50f / BoundingBox.getSize().x;
            FireScaleNode->attachObject( Entity );
            FireScaleNode->setScale( Scale, Scale, Scale );
            // put it some place where we can see it
            FireNode->setPosition( TargetPosition );
         }


As per usual you must place the material and cg files in your ogre resources.

fire.cg

void FireVertexShader(
   float4 position : POSITION,
   float3 normal   : NORMAL,
   float2 tex : TEXCOORD0,

   out float4 outPosition   :   POSITION,
   out float2 outTex        :   TEXCOORD0,
   out float2 outTexCoords1 :   TEXCOORD1,
   out float2 outTexCoords2 :   TEXCOORD2,
   out float2 outTexCoords3 :   TEXCOORD3,

   uniform float4x4 worldViewProjMatrix,
   // The frameTime variable is updated each frame so the shader has access to an incremental time that is used for scrolling the different noise textures.
   uniform float frameTime,
   // The scrollSpeeds variable is a 3 float array that contains three different scrolling speeds.
   // The x value is the scroll speed for the first noise texture.
   //  The y value is the scroll speed for the second noise texture.
   //  The z value is the scroll speed for the third noise texture.
   uniform float3 scrollSpeeds,
   // The scales variable is a 3 float array that contains three different scales (or octaves) for the three different noise textures.
   // The x, y, and z are generally set to 1, 2, and 3. This makes the first noise texture a single tile.
   // It also makes the second noise texture tiled twice in both directions. And finally it makes the third noise texture tiled three times in both directions.
   uniform float3 scales
)
{
   // scale to match original
   frameTime *= 1000;

   // Change the position vector to be 4 units for proper matrix calculations.
   position.w = 1.0f;

   outPosition = mul( worldViewProjMatrix, position );

   // Store the texture coordinates for the pixel shader.
   outTex = tex;

   // Compute texture coordinates for first noise texture using the first scale and upward scrolling speed values.
   outTexCoords1 = ( tex * scales.x );
   outTexCoords1.y = outTexCoords1.y + ( frameTime * scrollSpeeds.x );

   // Compute texture coordinates for second noise texture using the second scale and upward scrolling speed values.
   outTexCoords2 = ( tex * scales.y );
   outTexCoords2.y = outTexCoords2.y + ( frameTime * scrollSpeeds.y );

   // Compute texture coordinates for third noise texture using the third scale and upward scrolling speed values.
   outTexCoords3 = ( tex * scales.z );
   outTexCoords3.y = outTexCoords3.y + ( frameTime * scrollSpeeds.z );
}

void FireFragmentShader(
   float2 uv       : TEXCOORD0,
   float3 position : TEXCOORD1,
   float3 normal   : TEXCOORD2,

   out float4 outColor : COLOR,

   float4 outPosition     : POSITION,
   float2 inputTex        : TEXCOORD0,
   float2 inputTexCoords1 : TEXCOORD1,
   float2 inputTexCoords2 : TEXCOORD2,
   float2 inputTexCoords3 : TEXCOORD3,

   uniform float2 distortion1     : TEXCOORD4,
   uniform float2 distortion2     : TEXCOORD5,
   uniform float2 distortion3     : TEXCOORD6,
   uniform float2 distortion      : TEXCOORD7,

   uniform sampler2D noiseTexture : TEXUNIT0,
   uniform sampler2D fireTexture  : TEXUNIT1,
   uniform sampler2D alphaTexture : TEXUNIT2
)
{
   // Sample the same noise texture using the three different texture coordinates to get three different noise scales.
   //float4 noise1 = noiseTexture.Sample( SampleType, inputTexCoords1 );
   //float4 noise2 = noiseTexture.Sample( SampleType, inputTexCoords2 );
   //float4 noise3 = noiseTexture.Sample( SampleType, inputTexCoords3 );
   float4 noise1 = tex2D( noiseTexture, inputTexCoords1 );
   float4 noise2 = tex2D( noiseTexture, inputTexCoords2 );
   float4 noise3 = tex2D( noiseTexture, inputTexCoords3 );

   // Move the noise from the ( 0, 1 ) range to the ( -1, +1 ) range.
   noise1 = ( noise1 - 0.5f ) * 2.0f;
   noise2 = ( noise2 - 0.5f ) * 2.0f;
   noise3 = ( noise3 - 0.5f ) * 2.0f;

   // Distort the three noise x and y coordinates by the three different distortion x and y values.
   noise1.xy *= distortion1;
   noise2.xy *= distortion2;
   noise3.xy *= distortion3;

   // Combine all three distorted noise results into a single noise result.
   float2 finalNoise = noise1 + noise2 + noise3;

   // Perturb the input texture Y coordinates by the distortion scale and bias values.
   // The perturbation gets stronger as you move up the texture which creates the flame flickering at the top effect.
   float perturb = ( ( 1.0f - inputTex.y ) * distortion.x ) + distortion.y;

   // Now create the perturbed and distorted texture sampling coordinates that will be used to sample the fire color texture.
   float2 noiseCoords;
   noiseCoords.x = ( finalNoise.x * perturb ) + inputTex.x;
   noiseCoords.y = ( finalNoise.y * perturb ) + inputTex.y;

   // Sample the color from the fire texture using the perturbed and distorted texture sampling coordinates.
   // Use the clamping sample state instead of the wrap sample state to prevent flames wrapping around.
   //outColor = fireTexture.Sample( SampleType2, noiseCoords.xy );
   outColor = tex2D( fireTexture, clamp( noiseCoords, 0, 1 ) );

   // Sample the alpha value from the alpha texture using the perturbed and distorted texture sampling coordinates.
   // This will be used for transparency of the fire.
   // Use the clamping sample state instead of the wrap sample state to prevent flames wrapping around.
   //float4 alphaColor = alphaTexture.Sample( SampleType2, noiseCoords.xy );
   float alphaColor = tex2D( alphaTexture, clamp( noiseCoords, 0, 1 ) );

   // Set the alpha blending of the fire to the perturbed and distorted alpha texture value.
   outColor.a = alphaColor;
}

fire.material



Note: See http://www.ogre3d.org/docs/manual/manual_18.html for a list of profiles

vertex_program FireVertexShader cg
{
   source Fire.cg
   entry_point FireVertexShader
   profiles vs_1_1 arbvp1
}

fragment_program FireFragmentShader cg
{
   source Fire.cg
   entry_point FireFragmentShader
   profiles ps_2_0 arbfp1
}

material Fire
{
   technique
   {
      pass
      {
         scene_blend alpha_blend
         cull_hardware none
         cull_software none
         depth_write off
         depth_check on

         // Texture unit 0
         texture_unit noiseTexture
         {
             texture noiseTexture.gif
         }
         // Texture unit 1 (this is a multitexture pass)
         texture_unit fireTexture
         {
             texture fireTexture.gif
         }
         texture_unit alphaTexture
         {
             texture alphaTexture.gif
         }

         vertex_program_ref FireVertexShader
         {
            // worldviewproj_matrix: The current world, view and projection matrices concatenated.
            // This is implicitly a matrix4x4. a 4x4 matrix takes up 4 indexes (4 byte parameters)
            param_named_auto worldViewProjMatrix worldviewproj_matrix
            param_named_auto frameTime costime_0_1 1
            param_named scrollSpeeds float3 1 2 3
            param_named scales float3 1 2 3
         }

         fragment_program_ref FireFragmentShader
         {
            param_named distortion1 float2 0.1 0.2
            param_named distortion2 float2 0.1 0.3
            param_named distortion3 float2 0.1 0.1
            param_named distortion float2 0.8 0.5  // distortionScale and distortionBias in original
         }
      }
   }
}


Enjoy! uzik