Learn how to use the feTurbulence powerful SVG filter primitive to create your own textures and distortion effects.
Sara Soueidan, a freelance UI / UX developer of the interface and author of many technical articles living in Lebanon, offers a series of articles on the SVG filters and consists of the following articles:
feTurbulence is one of the most powerful primitive SVG filters. The specification defines this primitive as follows:
This filter primitive creates an image using the Perlin turbulence function. It allows the synthesis of artificial textures like clouds or marble. [...]
The resulting image will fill the entire sub-region of the filter primitive for this filter primitive.
In other words, the feTurbulence filter primitive generates and displays Perlin noise. This type of noise is useful for simulating several natural phenomena, such as clouds, fire and smoke, and generating complex textures, such as marble or granite. And as feFlood , primitive feTurbulence fills the filtering area with new content.
In this article, we will look at how to create noise using feTurbulence and how this noise can be used to distort images and text, as we did with the feDisplacementMap texture in the previous article. Then we look at how the generated noise can be used in conjunction with the light effects of the SVG to create a simple rough paper texture.
But first, let's review the feTurbulence and its attributes and see how each of them affects the noise generated.
When I intended to write this series, I decided to avoid gross technical details about the filter primitives as much as possible. That is why we will not go into the technical details of the functions used to generate Perlin noise .
After reading about the function underlying noise generation, I discovered that this does not help me at all when I set up a primitive for an experiment. In the end, we work with a random noise generator. Thus, in most cases, you will find that texture creation will be a topic for experimentation and tuning until you get the desired result. Over time, it will become a little easier to predict what the texture might look like.
I found that playing with the feTurbulence primitive and visualizing its attributes was the best way to learn about them and helped me understand what each of these attributes does. So we use a visual approach to understand feTurbulence , through several interactive demonstrations.
Now feTurbulence generates noise using the Perlin turbulence function . It has 5 basic attributes that control the function and, therefore, its visual result:
We will look at how each of these attributes affects the visual result, without going into the technical details of the function. You will find that in most cases you only need to worry about three of these attributes: type , baseFrequency and numOctaves .
To generate noise, only the baseFrequency attribute (base frequency) is required. baseFrequency affects the size (or scale) and the granularity of the generated noise.
The effect of the base frequency is best understood when it is rendered and animated. That's why I created the following demo version. Using the slider, you can change the value of the base frequency used and see how it affects the generated noise in real time. You will notice that as the value of the baseFrequency attribute increases or decreases, the generated pattern remains solid, becoming smaller or larger, respectively, and looks as if it is scaled and leaves the source in the upper left corner .
Lowering the baseFrequency values, such as 0.001, generate larger patterns, while increasing the values, 0.5+, create smaller patterns. Values ​​start at 0 (no frequency == no pattern) and higher. Negative values ​​are not allowed. As Michael Mullany notes , “values ​​ranging from 0.02 to 0.2 are useful starting points for most textures.”
Note that the noise generated does not have a background color. This means that if you remove the white background color on the SVG, you can see the dark body background through noise.
The baseFrequency attribute also takes two values. If you specify two values, the first one will be used for the base frequency on the X axis, and the second one will correspond to the Y axis. By providing two different values, you can generate vertical or horizontal noise, which can be used to implement some fantastic effects, as we will see in the next section.
Again play with the baseFrequency values ​​in this demo and notice how it changes along the X and Y axes if you give it different values. The demo starts with a nice horizontal noise. The x-baseFrequency value in 0.01 is relatively small, which makes the horizontal pattern large (as it was stretched out). If you reduce it even more, for example, to 0.001, you will see that the horizontal pattern will become more like lines. Try it.
As its name suggests, the type attribute is used to indicate the type of noise produced by the primitive feTurbulence . There are two types:
fractalNoise creates more nebulous and smooth patterns; this is a suitable base for creating gaseous textures like clouds. turbulence produces more lines that mimic pulsations, and are thus suitable as the basis for liquid textures.
Fig_1. Turbulence type noise on the left and fractal noise on the right.
Change the value of the type attribute in the following demo to see how the pattern created changes:
numOctaves is abbreviated from “number of octaves,” which represent the level of noise detail.
In music, the octave is the difference in tones between two notes, when one has twice the frequency than the other. Thus, the higher the octave, the higher the frequency. In feTurbulence , the greater the number of octaves, the more details you can see in the noise that it creates. By default, the generated noise is one octave, which means that the default value for the numOctaves attribute is 1.
Drag the slider in the following demo to see the effect of increasing the number of octaves on the generated texture:
You will notice that starting with numOctaves = "5", the effect of adding octaves becomes almost imperceptible.
The grain, as defined in the specification , is the “starting number for the pseudo-random number generator”. In other words, it provides a different seed for the random function used to generate our random noise.
Visually, you will see that this influences where and how “pulsation lines” are generated. It is also better understood when you see how this affects the noise generated in two adjacent rectangles.
When the same initial value is used for two adjacent rectangles, the function used to create noise through two rectangles is continuous, and this will be visually reflected by the continuity of the “ripple lines” along the edges of the two rectangles.
Pic_2. The continuity of a function that generates random noise can be seen along the edges of two rectangles using the same initial value.
Play around with the seed attribute value in the next demo, see how it affects the noise generated, and notice that the noise is continuous around the edges of two rectangles that use the same initial value.
stitchTiles can be used to create a stitching effect between noise “tiles”. The effect of this attribute is very similar to the seed effect, which means that it is most obvious when you have two adjacent areas (or “tiles”) of noise.
As the specification mentions, sometimes the result of noise generation will show clear discontinuities at the edges of the tile. You can tell the browser to try to smooth the results so that the two tiles appear to be “stitched" together. I really like the fact that the attribute and its effect is compared to the stitching.
By default, no attempt is made to achieve smooth transitions at the boundary of sheets containing the turbulence function, since the default value for stitchTiles is noStitch . If you want to create a stitching effect, you can change the value to stitch .
To compare the result of stitchTiles with the result of the seed , I applied the same seed value to the noise generated in the two rectangles in the next demo. You can already see that the noise seems continuous between them. Switch the stitchTiles option to “ on ” by changing its value to stitch to see how the noise changes placement at the edges.
As I mentioned earlier, with only three attributes, you will most likely use type , baseFrequency, and numOctaves . So we will focus on these three, moving forward.
This is where the fun begins. And as we begin to use the generated noise. After all, simply filling the filtering area with noise is useless in itself.
In the previous article, we used feDisplacementMap to align a piece of text with the texture of the external image. And we mentioned that feDisplacementMap uses color information from one image to distort another. The image used as the displacement map can be any. This means that it can be an external image or an image generated in SVG, for example, a gradient image or pattern, ... well, or a noise texture.
In other words, the noise that we generate with feTurbulence can also be used to distort content if used in conjunction with feDisplacementMap . In the following example, we use the output from feTurbulence to offset the image along with the feDisplacementMap . I use the horizontal noise model, by providing two different values ​​for the baseFrequency attribute, similar to what we did earlier.
<svg viewBox="0 0 180 100"> <filter id="noise" x="0%" y="0%" width="100%" height="100%"> <feTurbulence baseFrequency="0.01 0.4" result="NOISE" numOctaves="2" /> <feDisplacementMap in="SourceGraphic" in2="NOISE" scale="20" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap> </filter> <image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#noise)"></image> </svg>
The intensity with which turbulence distorts the image is indicated in the sale attribute on feDisplacementMap . I used a lot of importance to make the effect look more dramatic.
Now, based on this simple application, we can discover many more possibilities for combining these facts:
Just less than two years ago, Adrien Denat wrote the correct article in which he experimented with a similar effect applied to the HTML buttons. We are going to break and recreate the following button click effect:
We start by creating a noise texture. This is the state in which the button is distorted, and then, as soon as we get it, we will animate the initial state of the button to that distorted state and back on click.
Our goal here is to distort the button horizontally. Those. we will use and slightly adjust the horizontal noise from the previous demo version. Its distortion effect on the image is even too strong, so for a start I will dial the code by changing the value of turbulence from (0.01 0.4) to (0 0.2):
<filter id='noise' x='0%' y='0%' width='100%' height='100%'> <feTurbulence type="turbulence" baseFrequency="0 0.2" result="NOISE" numOctaves="2" /> <feDisplacementMap in="SourceGraphic" in2="NOISE" scale="30" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap> </filter>
The effect is a little better, but the button is still distorted more than we would like:
We want the distortion to be less dramatic. Keep in mind that we can instantly reduce the effect of noise by switching the type of noise from the default turbulence to a smoother fractalNoise . As soon as we do this, we will see that the distortion effect will also be smoothed out:
It looks much better.
Now that we have a distortion effect with which we are satisfied, we will start our demo with a filter that initially does almost nothing:
<filter id='noise' x='0%' y='0%' width='100%' height='100%'> <feTurbulence type="fractalNoise" baseFrequency="0 0.000001" result="NOISE" numOctaves="2" /> <feDisplacementMap in="SourceGraphic" in2="NOISE" scale="30" xChannelSelector="R" yChannelSelector="R"></feDisplacementMap> </filter>
We are going to apply this filter to our button in CSS:
button { -webkit-filter: url(#noise); filter: url(#noise); }
At this point, the button still doesn’t look distorted.
Next, we are going to use Adrien's code , though a slightly modified version that uses GSAP to animate the value of the baseFrequency attribute to (0 0.2) and back inside the feTurbulence primitive on the click:
var bt = document.querySelectorAll('.button')[0], turbVal = { val: 0.000001 }, turb = document.querySelectorAll('#noise feTurbulence')[0], btTl = new TimelineLite({ paused: true, onUpdate: function() { turb.setAttribute('baseFrequency', '0 ' + turbVal.val); } }); btTl.to(turbVal, 0.2, { val: 0.2 }) .to(turbVal, 0.2, { val: 0.000001 }); bt.addEventListener('click', function() { btTl.restart(); });
And this is actually all that is needed. You can play with the demo here :
At the time of this writing, the demo works in Chrome and Firefox. These are errors in the current version of Safari, but the issue will be resolved in the next version, as the Safari Tech Preview shows the demo works fine.
Although it does not work in MS Edge, the button is not distorted at all, which means that the lack of support does not affect the ability to use it. This is great because you can still use this effect as an enhancement . If the effect is not supported, the button will look and behave like a normal button with no effect.
Adrian's article includes several more button distortion effects that use the same principles that we just looked at and that are definitely worth checking out and dismantling. There are one or two good things everyone needs to learn.
One of my favorite examples of using feTurbulence is the wavy text effect from Lukas Bebber. In his demo, Lucas uses several feTurbulence functions:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"> <defs> <filter id="squiggly-0"> <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="0" /> <feDisplacementMap id="displacement" in="SourceGraphic" in2="noise" scale="6" /> </filter> <filter id="squiggly-1"> <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="1" /> <feDisplacementMap in="SourceGraphic" in2="noise" scale="8" /> </filter> <filter id="squiggly-2"> <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="2" /> <feDisplacementMap in="SourceGraphic" in2="noise" scale="6" /> </filter> <filter id="squiggly-3"> <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="3" /> <feDisplacementMap in="SourceGraphic" in2="noise" scale="8" /> </filter> <filter id="squiggly-4"> <feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="4" /> <feDisplacementMap in="SourceGraphic" in2="noise" scale="6" /> </filter> </defs> </svg>
... and applying them through CSS to a fragment of HTML text using CSS animation, animates from one to another:
@keyframes squiggly-anim { 0% { -webkit-filter: url("#squiggly-0"); filter: url("#squiggly-0"); } 25% { -webkit-filter: url("#squiggly-1"); filter: url("#squiggly-1"); } 50% { -webkit-filter: url("#squiggly-2"); filter: url("#squiggly-2"); } 75% { -webkit-filter: url("#squiggly-3"); filter: url("#squiggly-3"); } 100% { -webkit-filter: url("#squiggly-4"); filter: url("#squiggly-4"); } }
... thus creating a wavy effect.
Again, the text used is real, i.e. it is available for searching, selecting, accessing and editing (using the contenteditable attribute). Check out the live demo , but be careful, because This demo is resource intensive and you may not open Codepen on your mobile phone.
So, some useful conclusions from this section:
Primitive feTurbulence is very rarely, if ever, used alone. It is almost always used by other filter primitives to achieve individual effects.
In this section, we used it as the displacement map in feDisplacementMap . Let's see what else you can do with it.
Another useful way to use the noise generated by feTurbulence is to imitate the natural texture. If you have ever used Noise generation plugins in After Effects , you may have already come across this functionality and examples of this.
Fig_7. Examples of textures created in After Effects using the Fractal Noise plugin. ( Source )
feTurbulence generates noise (random values) for each of the components R, G, B and A. You can change the values ​​of each of these components to get different variations of noise. In order to model the texture, we usually need to do just that: adjust the R / G / B / A components (cancel the components, saturate others, etc.) to get the desired result. In other cases, all we have to do is shed some light on it. Literally.
In this section, we will examine the effect of rough paper texture created by Michael Mallani. In order to create this texture, we need to illuminate the texture of the noise generated by feTurbulence using the SVG lighting sources.
SVG conveniently provides several primitives that can be used to illuminate objects or images.
There are two filter primitives that are used to indicate the type of light you want:
Both primitives illuminate an object or image using the alpha channel of this image as a relief map. Transparent values ​​remain flat, while opaque values ​​rise to form peaks that are lit more prominently.
In other words, the light source filter uses an alpha input channel to obtain depth information: areas with more opacity rise to the observer, and areas with less opacity move away from him. This means that the alpha value of the pixel in the input data is used as the height of this pixel in the z-dimension, and the filter uses this height to calculate a virtual surface that will reflect a certain amount of light from the light source. This is quite a powerful thing!
Both types of light accept an attribute called surfaceScale , which is practically a multiplier of the z-index. As this value increases, the “slopes” of the surface texture become steeper.
“Because feTurbulence generates an alpha channel, full of noise values ​​from 0 to 1, it forms a good variable Z-surface that creates glare when we shine on it.” —Michall Mallani
After deciding on the type of light you need to choose a light source. There are three types of light sources in SVG:
Each of these three light sources has its own attributes that are used to tune the light it generates by specifying the location of the source in 3D space. Attribute descriptions are beyond the scope of this article, but you can learn more about them in this specification .
To create and apply a lighting effect, you must embed the light source in the type of lighting. So you start by choosing the type of lighting you want, and then choose the source from which it will come. And then, finally, you need to specify the color of your lighting. The lighting-color property is used to define the color of the light source for feDiffuseLighting and feSpecularLighting .
Having considered the basics of light sources, we now turn to our example.
For rough paper texture we will use sunlight. This means that we will use white diffused lighting that comes from a remote source. Translated into code, our light looks like this:
<feDiffuseLighting lighting-color="white" surfaceScale="2" in=".." result=".."> <feDistantLight azimuth="45" elevation="60" /> </feDiffuseLighting>
Attributes azimuth and elevation determine the position of the light source in three-dimensional space. There is an article by Raphael Pons , which is simply amazing in explaining these two concepts in a simple, easy-to-understand manner, along with beautiful and handy illustrations that help in explaining. I highly recommend viewing it.
Now that we have light, we need to create our noise in order to illuminate it with this light. We will break the demo into stages to find out how it was created.
We need to start somewhere, and we’ll start by generating random, basic noise as the basis of our texture:
<feTurbulence baseFrequency='0.04' result='noise' />
Our noise looks like this:
Then we will shed our light on him, and then take it from there:
<feTurbulence baseFrequency='0.04' result='noise' /> <feDiffuseLighting in='noise' lighting-color='white' surfaceScale='2'> <feDistantLight azimuth='45' elevation='60' /> </feDiffuseLighting>
The bright highlight of our noise gives us the following texture:
This is not the result of the texture we are looking for. The first thing we notice here is the presence of many sharp lines in the texture. We want to get rid of them, because there are no sharp lines on the surface of the paper. We need to smooth out these lines. This can be done by changing the type of generated noise to fractalNoise :
<feTurbulence type="fractalNoise" baseFrequency='0.04' result='noise' /> <feDiffuseLighting in='noise' lighting-color='white' surfaceScale='2'> <feDistantLight azimuth='45' elevation='60' /> </feDiffuseLighting>
This removes all these sharp aligned edges from our texture:
Now we are one step closer to our rough paper texture.
However, the above texture is not sufficiently rough. She lacks the necessary “roughness”. Increasing the number of small parts in it should make it more coarse. To do this, we will increase the value of numOctaves . We found out that about 5 is a great number to get the required level of roughness:
<feTurbulence type="fractalNoise" baseFrequency='0.04' numOctaves="5" result='noise' /> <feDiffuseLighting in='noise' lighting-color='white' surfaceScale='2'> <feDistantLight azimuth='45' elevation='60' /> </feDiffuseLighting>
And our paper texture now looks like this:
Fine!
You can play with the demo here:
This demo works in all major browsers, including MS Edge.
If you want, you can adjust the effect a bit further by playing with the source and distance of the light. For example, reducing the elevation of the light source from 60 to 40 should increase the contrast between small hills in the texture. Then the texture will look something like this:
I highly recommend playing with the values ​​of the attributes of the light source and noise and see how they affect the resulting texture.
Primitive feTurbulence is one of the most interesting and powerful SVG operations. In combination with other primitives and animation, it is able to create really interesting and attractive effects, textures and interactions.
I assume that feTurbulence is one of those filters that you would like to experiment with or parse other people's code to learn more about. However, I believe that I guess how the texture will look after some time. And since we can do so much with only one texture, if we use it with other primitives, there are almost countless various effects that can be created with its help. I strongly recommend that you look at the work of other people and disassemble it in order to study better.
Yoksel is experimenting with Codepen SVG filters after my conversation about SVG filters a few months ago. So you can find quite a few effects to parse and study in his Codepen profile.
Fig_13. One of the latest Yoksel experiments with SVG filters using feTurbulence .
I hope this article has inspired you and opened a new door of your imagination, so that you can see what can be done with SVG filters.
In the last article of this series, I will share some additional resources and tools that will help you move forward with SVG filters and start building your own. Stay with us.
Source: https://habr.com/ru/post/441234/
All Articles