svg滤镜详解之feTurbulence噪声滤镜

1,163 阅读4分钟

  想要彻底理解和使用该滤镜还是需要好好研究一下的,但是如果真正理解和会使用这个滤镜,那么就可以生成一些例如云彩,火焰,大理石纹理,等绚烂图案。本篇文章我们一起研究一下这个滤镜,在文章的后面再分享一些该滤镜的特效源码。

滤镜原理和语法

  噪声来源于计算机图形学,例如我们在游戏中使用的火焰效果,就是基于噪声函数实现的。现实中常见的噪声就是电视没有台的时候出现的“雪花点“。在计算机图像学中常见的噪声有6种,这里面就包含svg使用的噪声:Perlin噪声
  关于该噪声的原理稍微研究了一下:可以理解为随机点的曲线差值。对于一维的效果如下:

3f068d01b0f64071c92a9412a4b60a5.png 对于随机生成的点,用曲线进行差值。对于二维的,可以理解成在一维基础上的扩展。(毕竟我们不是做计算机图形学的,不必研究的很清楚,大概懂得原理就行了)我们知道了这个滤镜的大概原理和作用,那就介绍一下这个滤镜的语法。
feTurbulence滤镜有5个属性:

  • baseFrequency(默认值:0 0)可以接受两个参数噪声频率,值越大,噪声点越小,越密集
  • numOctaves(默认值:1)这是噪声叠加,能使噪声细节更加丰富
  • seed (默认值:0)随机值的入参
  • stitchTiles(默认值:noStitch)
  • type (默认值:turbulence)或者fractalNoise,后者更为平滑。

只看语法是效果不大的,毕竟纸上得来空觉浅吗。下面我们对各个属性都分别测验一下。

看如下代码:

<svg width="500" height="500" viewBox="0 0 500 500">
    <defs>
        <filter id="turbu_filter">
            <feTurbulence type="turbulence" 
                baseFrequency="0 0"
                numOctaves="1"
                stitchTiles="noStitch" 
                seed="3" >
                <animate attributeName="baseFrequency" attributeType="XML" from="0 0" to="1 1" dur="6s" repeatCount="indefinite" /> 
            </feTurbulence> 
        </filter>
    </defs>
    <rect x="0" y="0" width="300" height="200" style="filter:url(#turbu_filter);" />
</svg>

这是噪声频率(baseFrequency)从0到1的变化,效果图如下: 录屏_选择区域_20221213200842.gif

numOctaves,噪声叠加就是在噪声的边缘进行叠加。值越大叠加的效果也不明显。看代码:

<svg width="500" height="500" viewBox="0 0 500 500">
    <defs>
        <filter id="turbu_filter1">
            <feTurbulence type="turbulence" 
                baseFrequency="0.1 0.1"
                numOctaves="1"
                stitchTiles="noStitch" 
                seed="3" >
                <animate attributeName="numOctaves" attributeType="XML" keyTimes="0; 0.2; 0.4; 0.6; 0.8; 1" values="1;2;3;4;5;6;" begin="0s"  dur="3s" repeatCount="indefinite" /> 
            </feTurbulence> 
        </filter>
    </defs>
    <rect x="0" y="0" width="300" height="200" style="filter:url(#turbu_filter1);" />
</svg>

效果如下:

录屏_选择区域_20221218092927.gif

当叠加的值大于3的时候图像变化已经不明显了。

seed是随机数输入值,这个变化并不是连续的。看如下代码:

<svg width="500" height="500" viewBox="0 0 500 500">
    <defs>
        <filter id="turbu_filter3">
            <feTurbulence type="turbulence" 
                baseFrequency="0.2 0.2"
                numOctaves="1"
                stitchTiles="noStitch" 
                seed="1" >
                <animate attributeName="seed" attributeType="XML" from="1" to="10" dur="6s" repeatCount="indefinite" /> 
            </feTurbulence> 
        </filter>
    </defs>
    <rect x="0" y="0" width="300" height="200" style="filter:url(#turbu_filter3);" />
</svg>

效果如下:

录屏_选择区域_20221218094906.gif

另外两个就不做单独的介绍了。我感觉可以看一w3c的例子还是比较经典的:

截图_选择区域_20221223104820.png

下面介绍几个经典的案例:

噪声滤镜实例

蓝天白云效果

先看效果:

录屏_选择区域_20221223105121.gif 我老婆说了感觉哪里不对,不像是白云,说明还是有需要改进的地方。想法是通过噪声点对椭圆的白云进行像素偏移,再给椭圆增加动画就行了。源码如下:

<svg width="1500" height="500" viewBox="0 0 1500 500" style="background-color: blue;">
    <defs>
        <filter id="turbu_filter333">
            <feTurbulence 
                result="tirld"
                type="turbulence" 
                baseFrequency="0.005"
                numOctaves="3"
                stitchTiles="noStitch" 
                seed="0" >
            </feTurbulence>
            <feDisplacementMap in="SourceGraphic" in2="tirld" scale="100" xChannelSelector="B" yChannelSelector="R"></feDisplacementMap>
        </filter>
        <radialGradient id="radia_gra" cx="50%" cy="20%" r="50%" fx="50%" fy="50%">
            <stop offset="0%" stop-color="rgba(255,255,255,1)"></stop>
            <stop offset="70%" stop-color="rgba(255,255,255,0.8)"></stop>
            <stop offset="100%" stop-color="rgba(255,255,255,0)"></stop>
        </radialGradient>
    </defs>
    <!-- <rect x="0" y="0" width="100%" height="100%"  style="fill:url(#radia_gra);filter:url(#turbu_filter333);"/> -->
    <g x="0" y="0" width="100%" height="100%" style="filter:url(#turbu_filter333);">
        <ellipse cx="300" cy="200" rx="150" ry="50" fill="url(#radia_gra)">
            <animate attributeName="cx" id="an1" from="300" to="100" begin="0s;an2.end" dur="3s" />
            <animate attributeName="cx" id="an2" from="100" to="300" begin="an1.end" dur="3s"/>
            <animate attributeName="cy" id="an3" from="200" to="0" begin="0s;an4.end" dur="3s"/>
            <animate attributeName="cy" id="an4" from="0" to="200" begin="an3.end" dur="3s"/>
        </ellipse>
        <ellipse cx="-300" cy="400" rx="550" ry="150" fill="url(#radia_gra)">
            <animate attributeName="cx" from="-300" to="1600" begin="0s" dur="15s" repeatCount="indefinite"/>
        </ellipse>
    </g>
</svg>

大家可以在我做的基础上进行改进,欢迎大家留言。

褶皱纸张效果

效果如下:

截图_选择区域_20221223111318.png

截图出来后效果不太明显。大家可以看源码在本地尝试:

<svg width="1500" height="500" viewBox="0 0 1500 500">
    <defs>
        <filter id="turbu_filter">
            <feTurbulence 
                result="tirld"
                type="turbulence" 
                baseFrequency="0.1"
                numOctaves="2"
                stitchTiles="noStitch" 
                seed="1" >

            </feTurbulence>
            <feSpecularLighting
                in="tirld"
                lighting-color="#f2fde2" 
                surfaceScale="5"
                specularConstant="1"
                specularExponent="2"
                result="fe_ligt"
                >
                <feDistantLight azimuth="45" elevation="15"/>
            </feSpecularLighting>
        </filter>
    </defs>
    <rect x="0" y="0" width="100%" height="100%" style="filter:url(#turbu_filter);"/>
</svg>

就是在波纹上增加白色的光照,将文字写到纸张上能增加年代感。特别是艺术字的话。

水波纹效果

这种效果是最常见的滤镜应用,网上资源比较多,并且在现实项目中很少能用得到。看一下我这实现的效果,实际上需要两张图片,一张是原图片,一张是水的图片放到原图片上,加上滤镜效果。

录屏_选择区域_20221223152530.gif 我的截图有点随意,做出的效果有瑕疵。大家知道原理可以自行优化。 主要代码如下:

<svg width="500" height="300">
    <defs>
       <filter id="filter-ripple">
        <feTurbulence id="smoke_turbulence" result="smoke" type="fractalNoise" baseFrequency=".03" numOctaves="2" />
        <feDisplacementMap in="SourceGraphic" in2="smoke"  scale="20" xChannelSelector="B" yChannelSelector="R"/>
      </filter>    
    </defs>
    <image xlink:href="../img/fj.png" x="0" y="0" width="500" height="300"></image>
    <image xlink:href="../img/shuibo.png" x="0" y="200" width="500" height="100" style="filter:url(#filter-ripple)"></image>
</svg>

为噪声滤镜添加动画就可以了。

烟雾文字

如图所示: 录屏_选择区域_20221223145735.gif

该效果需要借助css和js帮忙。首先是通过css做出渐变字体,然后用js修改噪音大小。渐变字体用到css3的-webkit-background-clip:text。接下来就没什么难的了。

结束语

svg噪声滤镜是个神奇的滤镜,可以制造出很多绚烂的效果。上面简单的例子只是抛砖引玉。
行文到此,基本所有的svg滤镜已经介绍完了。关于svg的dom操作不再介绍,项目中很少用到。谢谢观赏。