Search code examples
svg-filters

SVG feTurbulence too transparent output


I have a feTurbulence filter generating clouds in SVG format. I can't adjust the opacity to be less transparent. The clouds are against a blue sky and some white stars. Through the clouds, the stars can be seen! since the clouds are not opaque or blue underneath. This is the svg:

    <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" clip-rule="evenodd" image-
rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 
0 1800 300">
    <defs>
    <filter id="a" filterUnits="userSpaceOnUse">
    <feTurbulence type="fractalNoise" seed="3" baseFrequency="0.05" numOctaves="6" result="noise1"/>
    <feGaussianBlur in="SourceGraphic" stdDeviation="20"/>
    <feDisplacementMap in="blur1" scale="40" in2="noise1" result="cloud1"/>
    <feMerge>
    <feMergeNode in="cloud1"/>
    </feMerge>
    </filter>
    </defs>
    <g fill="#FFF" stroke="#FFF" stroke-width="0" filter="url(#a)">
    <rect x="500" y="45" width="350" height="12"/>
    <rect x="960" y="65" width="300" height="18"/>
    </g>
    </svg>

I have tried to set the opacity to 1 (both to the filter or the objects), to use feMerge, feComposite, and some other advices given by Gemini. So far, nothing works. I need to make the clouds to NOT display the stars behind them, everything which is a cloud should have just blue underneath (white clouds against blue background, not white clouds against blue background with white dots in it).


Solution

  • The answer is to generate a opaque mask first (same color as the background), identical or a bit smaller than the cloud, using feColorMatrix. Then, in the same svg, generate the cloud normally. For the above svg, this is the complete svg:

    <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 1800 300">
    <defs>
    <filter id="a" filterUnits="userSpaceOnUse">
    <feTurbulence type="fractalNoise" seed="3" baseFrequency="0.05" numOctaves="6" result="noise1"/>
    <feGaussianBlur in="SourceGraphic" stdDeviation="10"/>
    <feDisplacementMap in="blur1" scale="40" in2="noise1" result="cloud1"/>
    <feMerge>
    <feMergeNode in="cloud1"/>
    </feMerge>
      <feColorMatrix values="
        0.05  0  0   0   0
        0  0.06  0   0   0
        0  0  0.65   0   0
    1  1  1   1   0 "/>
    </filter>
    <filter id="b" filterUnits="userSpaceOnUse">
    <feTurbulence type="fractalNoise" seed="3" baseFrequency="0.05" numOctaves="6" result="noise1"/>
    <feGaussianBlur in="SourceGraphic" stdDeviation="20"/>
    <feDisplacementMap in="blur1" scale="40" in2="noise1" result="cloud1"/>
    <feMerge>
    <feMergeNode in="cloud1"/>
    </feMerge>
    </filter>
    </defs>
    <g fill="#FFF" stroke="#FFF" stroke-width="0" filter="url(#a)">
    <rect x="500" y="45" width="350" height="12"/>
    <rect x="960" y="65" width="300" height="18"/>
    </g>
    <g fill="#FFF" stroke="#FFF" stroke-width="0" filter="url(#b)">
    <rect x="500" y="45" width="350" height="12"/>
    <rect x="960" y="65" width="300" height="18"/>
    </g>
    </svg>
    

    Filter "#a" makes the cloud mask, filter "#b" the same cloud but without the mask. The opaque mask has "stdDeviation" smaller, because it has to be smaller than the cloud, to allow for some overlapping (at the edges of the cloud, the stars should be seen).

    The 0.05, 0.06 and 0.65 values of the matrix are to match my background (#44c). The four "1"s are the opacity settings for R G B and A.

    As far as I have tried, it's not possible to succeed without this mask complication, using feColorMatrix alone, because the cloud look is achieved using transparency (to look thin, vaporous, gas like).