This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


MIND


This article assumes you're familiar with VBScript and HTML
Download the code (5KB)

Procedural Textures Bring Nature to Internet Explorer 4.0
Charles Mirho 

Based on noise and turbulence theory, procedural textures in Microsoft Internet Explorer 4.0 enable realistic, natural animations by combining a single bitmap with mathematical equations.
People like to watch things move. Animation is the technique of creating the illusion of motion. One approach is frame animation, in which a series of bitmaps are sequenced rapidly to provide the illusion of motion. Another technique is palette animation, in which the color palette of a single bitmap is manipulated over time to reveal interesting patterns that only become visible as the colors change.
      Both techniques have limitations. With frame or cell animation on the Internet, each bitmap in an animation sequence (or at least the changes between consecutive bitmaps) must be downloaded before the animation can be sequenced in the browser. Furthermore, once the frames are created, the animation follows a predictable sequence from start to finish and then repeats itself. Palette animation has a similar limitation. Although the palette can be cycled in different fashions to vary the effect, the range of possible effects is limited by the color content of the bitmap.
      Natural phenomena such as fire, water, wind, smoke, and clouds look best when endowed with an aspect of randomness. A wisp of smoke just doesn't look right unless it dissipates into random particles, and ripples on water should cause random reflections.
      Enter procedural textures, a new feature in Microsoft® Internet Explorer 4.0. Procedural textures enable realistic, natural animations using a single bitmap (the texture) combined with one or more mathematical equations (the procedure). The position and color of individual pixels are manipulated by the equations to provide the illusion of motion within the boundaries of the bitmap.
      Using a single bitmap makes animations download faster. Unlike conventional animations, the effects generated procedurally can be nonrepeating because of the randomness created by the equations. The effects mimic nature because of their basis in noise and turbulence theory, which provide a good model for certain natural phenomena.

How Procedural Effects Work
      To generate natural-looking procedural textures you need a function that breaks up the monotony of regular patterns. Noise functions provide this functionality. A pure, unfiltered noise function will provide totally random, white noise, like the static on an unused television frequency. Procedural texture noise functions incorporate this randomness in a controlled way.
      Procedural effects work by applying one or more noise functions to the u (column) and v (row) coordinates of the pixels in a bitmap. For some effects, one noise function alters the u coordinate and another is used to alter the v coordinate of the pixels. Another noise function may be applied to modulate the color of the pixels, creating color changes within the motion effects.

Creating Procedural Textures
      When creating procedural textures, the most important considerations are the design of the bitmap and the definition of the parameters passed to the procedure. Designing the bitmap is an artistic endeavor, whereas the procedure is a technical design. The most important procedural parameter is the number of harmonics in the noise function used to generate the effect. The more harmonics, the smoother and more natural the effect. However, more harmonics means greater computational complexity, and thus a slower effect.
      Another important procedural parameter is the coordinate within the bitmap on which you'll center the effect. In a large bitmap, the effect can be centered over only a small portion of the pixels, localizing the effect to a subregion of the bitmap. For example, in a video game that displays a large bitmap of a dungeon, the effect can be centered over the head of a torch on the wall to create a flame in that limited region of the bitmap. Other parameters to the noise function are scale factors for stretching the effect in both the u and v directions, and time factors for endowing the effect with time-variant behaviors.
      Figure 1 shows the HTML and VBScript code used to create a procedural texture that can be rendered in Internet Explorer 4.0. Let's skip the standard stuff—the HTML headers and standard tags—and move right to the good stuff. The first thing to look at is the <IMG> tag that defines the effect. The tag assigns a logical name to the effect (in this case, theImg). Next, the src
Figure 2: Flame
Figure 2: Flame
property of the effect is set to the name of the seed bitmap, template.bmp (see Figure 2). This is not the same as setting the seed bitmap for the image. A mask bitmap is specified for use in conjunction with the effect. (I'll explain why I might want to use a mask later.) However, the name of the mask bitmap is the same as the name of the seed bitmap. As a result, the effect is applied to the entire seed bitmap. If I had specified a mask bitmap different from the seed bitmap, then the dimensions of the mask would be used to determine which area of the seed bitmap to subject to the effect. For example, if the seed bitmap were a picture of a dungeon, then the mask bitmap could have been specified as the rectangular region surrounding the head of a flame in the larger picture, and only that portion of the seed bitmap would receive the effect.
      Next, I define the effect by assigning a noise function to the image style property. The noise function is defined as follows:

 filter:IntelAdditive(Harmonics=6, 
                      NoiseScale=11, 
                      NoiseOffset=-13,
                      ScaleX=2, 
                      ScaleY=1, 
                      TimeX=-1, 
                      TimeY=0,
                      enabled=1);
      The effect is additive, meaning it adds pixels to the seed bitmap instead of distorting the pixels that are already there. The noise function has six levels of harmonic complexity; it will be computationally intensive but very smooth and natural-looking (the more harmonics, the fewer jagged edges in the effect). A NoiseScale of 11 is assigned, which causes the flame to jump around quite a bit. Smaller noise scales would produce a more docile flame, and larger noise scales create violent, lashing flames because they pump up the power of the noise function.
      The NoiseOffset property is a little tricky. Essentially, this property gives you some control over the color of the effect. Large positive values for NoiseOffset (up to 256) cause the lighter colors of the seed bitmap to receive emphasis in the effect. Large negative values (down to -256) emphasize the darker colors. I chose -13 here, indicating a slight bias for the darker colors.
      Next, I scale the effect by a factor of 2 in the X direction, and by 1 in the Y direction. Essentially, I'm telling Internet Explorer to stretch the effect in the X dimension by twice the scale of the Y dimension. This produces a more vertically layered flame effect to the eye. I want the effect to appear to slowly ripple across the flame from right to left, so I use a TimeX value of -1, causing the flame to ripple horizontally. I don't want to do the same thing in the Y dimension, so TimeY is set to 0. Finally, the Enabled value is set to 1, indicating that the effect is enabled to run when the page loads.
      As you can see from this example, much of the effect's design depends upon the seed bitmap and the definition of the filter values. The outcome often depends on trial and error, changing the parameters to achieve the optimal visual effect, but the results are worth the effort.
      Moving back to the top of the Web page, the first script code is the window_onload event procedure, which is executed when the Web page loads. This is the place to do one-time initializations. First, I set up the offscreen bitmap buffer. I tell Internet Explorer to use the same pixel depth for the offscreen buffer as is used for the display. This way when the effect is rendered offscreen and then moved on-screen, it will look correct.

 screen.bufferDepth = -1 
      Next, I set the bitmap seed for the effect. The bitmap is seeded here instead of in the tag itself because Internet Explorer expects it to be done in the onload event. By setting the property here, an OnFilterChange event is generated which causes the effect to begin to execute (see Figure 3). You will see how this works in a moment.
Figure 3: Event procedure executes the effect
Figure 3: Event procedure executes the effect

      Next, I define an event procedure to be called whenever any of the effect properties are changed. The purpose of this event procedure is simply to rerender the effect each time one of the properties changes. Just as with other tags, an effect's properties aren't static when set in the tag; with Dynamic HTML, the effects can be changed on-the-fly while the code is running.

 sub theImg_OnFilterChange   
 call SetTimeout( "OnPokeEffect", 15 )  
 end sub
      The event procedure is called theImg_OnFilterChange, and it calls only a single procedure: SetTimeout. First, the SetTimeout call specifies a procedure for Internet Explorer to invoke after the timeout interval expires, in this case 15 milliseconds. The procedure it calls is OnPokeEffect. So a change in the effect properties schedules a call to OnPokeEffect after 15 milliseconds. No big deal.
      Here's the fun part: the OnPokeEffect procedure simply tinkers with the appropriate effect property, in this case the NoiseScale. It doesn't change the property—it just reads it in and writes it back. The result is that another OnFilterChange event is generated, which schedules another call to OnPokeEffect, and so on. What is going on here? It turns out that Internet Explorer will only update the effect on the screen when an effect property is written—not necessarily changed. So to cause the flame to flicker—or the clouds to float by, or the water to ripple—I must keep tinkering with some of the effect's properties so Internet Explorer thinks it needs to update the display.

 sub OnPokeEffect   
 n = theImg.filters.item("IntelAdditive").NoiseScale   
 theImg.filters.item("IntelAdditive").NoiseScale = n
 end sub
      Why not just call OnPokeEffect directly from the OnFilterChange event procedure? If I did this, I would have no control over how fast the effect ran. It would run more quickly on fast machines than on slow ones. Scheduling the call to OnPokeEffect every 15 milliseconds, guarantees that the effect will run at the same speed on all hardware.
      A more complete VBScript implementation would also include procedures to disable the effect when the window was hidden, and reenable it when the window was displayed again. This would save machine cycles. The effect can be disabled simply by setting the Enabled value to false.
      Internet Explorer 4.0 supports built-in effects for fire, clouds, and water, with no seed bitmap required. I simply specify the same header and scripting code. The only change needed in the script is in the window_onload subroutine. I also change the <IMG> tag to the following:

 <IMG ID=theImg 
 width=200; height=100;
 style="filter:IntelAdditive(GenerateSeed=1); ">
Because I'm using a built-in effect, I don't need to specify a seed bitmap, mask, or any of the effect values. Instead, I just specify the dimensions I need for the effect and a seed value of 1 (for fire), 2 (for water), or 3 ( for clouds). Bingo! Instant nature in my Web pages.

 sub window_onload
 screen.bufferDepth = -1
 onPokeEffect
 end sub
      I do need to make a small change to the VBScript when using built-in effects. Because no seed bitmap is used, I cannot count on a property change to generate the first call to OnFilterChange. Instead, I must call OnPokeEffect directly from the onload event procedure. This gets the ball rolling, and subsequent calls to OnFilterChange are generated by the property diddling in OnPokeEffect.

Turbulence
      Even cooler natural effects are possible using turbulence functions, which are generated by summing several noise functions, each computed at different scales of detail. By adding turbulence to a smoothly varying sine wave, I can simulate the texture of marble. By adding turbulence to a sawtooth function, I can simulate tree bark. Adding turbulence to an opacity map and fiddling with its texture coordinates creates realistic clouds.
      Turbulence theory is beyond the scope of this article, but you should be aware that turbulence effects are available for the day when you become a hardcore procedural texturalist.

Distortion
      Distortion effects let you squash, stretch, mutate, and otherwise abuse your bitmaps. Distortion is created by applying two noise functions to your bitmap pixels: one for the u coordinates and another for the v coordinates. Applying these functions causes a relocation of pixels which distorts the appearance of the image. Figure 4 shows a template code for creating distortion effects in Internet Explorer 4.0.
      The script for the distortion effect is virtually identical to the script for the flame effect. The big difference comes in the definition of the tag. You might recognize some of the style properties: the number of harmonics set to 3 for the noise functions, and the effect is enabled so that it'll run when the page is loaded. The properties for distortion effects are very different from the properties for additive effects, and are worth a separate look.
      NoiseScaleU and NoiseScaleV are the magnitude of the noise function applied in the U(X) and V(Y) dimensions, respectively. These values affect the relative magnitude of the horizontal and vertical distortion. In this case, both values are set to 6, indicating equal distortion in the X and Y dimensions.
      ScaleUX and ScaleVX determine the scale factors to apply to the noise function that controls the horizontal distortion. Remember, distortion results from the application of two noise functions, one in the horizontal axis and one in the vertical axis. Each noise function, in turn, has X and Y dimensions. ScaleUY and ScaleVY provide similar values for the vertical noise function.
Figure 5: Seed
Figure 5: Seed
      ScaleUT and ScaleVT provide the time translation values for the two noise functions. This indicates how fast the distortion appears to occur. In this case, the horizontal distortion occurs faster than the vertical, so the image appears to collapse inward from the sides. Figure 5 shows the seed bitmap, and Figure 6 illustrates the distortion effect on it.

Figure 6: Distortion effect on seed bitmap
Figure 6: Distortion effect on seed bitmap Test It!

Other Tricks
      Different techniques may be combined with procedural textures to create composite effects. For example, by specifying a transparency color, the effect can be blended with the background of the bitmap. Only pixels that are the transparent color are animated by the effect; other (background) pixels in the bitmap are left alone.

Figure 7: Mask bitmap
Figure 7: Mask bitmap

Figure 8: Mask with flame effect
Figure 8: Mask with flame effect

      Mask bitmaps may be employed to superimpose effects from one bitmap onto another. Combining masks with transparency makes for very interesting possibilities. For example, a mask with a blue background and black block lettering (like the one shown in Figure 7) can be combined with a source bitmap for flames and a black transparency color. The result is an effect in which the black letters of the mask bitmap are animated with the flame effect, as shown in Figure 8. The blue background is left unchanged.

From the January 1998 issue of Microsoft Interactive Developer.