Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Twist The Reality with feDisplacementMap #16

Open
9am opened this issue Oct 25, 2023 · 1 comment
Open

Twist The Reality with feDisplacementMap #16

9am opened this issue Oct 25, 2023 · 1 comment
Assignees
Labels
filters Filters svg SVG

Comments

@9am
Copy link
Owner

9am commented Oct 25, 2023

joy

hits

@9am
Copy link
Owner Author

9am commented Oct 25, 2023

Table of contents


Preface

Inspired by the famous album cover of Joy Division made by Peter Saville, I wanted to build an SVG filter that turns the source(image, text) into a bunch of 'contour' lines. That's where feDisplacementMap kicks in. It's one of my favorite filter primitives. In this article, I'll show you things we can do with it.

Unknown Pleasures
Album cover by Peter Saville


𐄡


Super power of feDisplacementMap

Most of the filter elements in SVG work like Pixel shaders, just do simple pixel in, pixel out. But feDisplacementMap offers the ability to re-arrange the position of pixels.

The SVG filter primitive uses the pixel values from the image from in2 to spatially displace the image from in.<feDisplacementMap in="" in2="" />

The formula for the transformation looks like this:

P'(x,y) ← P(x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5))


displacementmap

How the position displaces depends on the color channel of the 'Map' input, yeah, we can control source's pixel position by the color of the Map. Consider we have a green crossline as source, if we want to move the area in the center, we'll use the alpha channel, a map with alpha != 1 rectangle placed in the center will do the job. The distance offset of the pixel could be controlled by scale and the value offset from '0.5' of the color channel. The direction is controlled by xChannelSelector or yChannelSelector.

how-works [![Edit how-fedisplacementmap-works](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/how-fedisplacementmap-works-tmcqkj?fontsize=14&hidenavigation=1&theme=dark)

With that, we'll build the contour filter to show the alpha value of the 'source', this is what we'll build.

final contour


𐄡


Contour filter


1. Create the stripes

With the help of <feImage> and <feTile>, we can easily create a horizontal stripe pattern that fills the source target.

<div style="filter: url(#contour)"></div>
<svg>
  <defs>
    <polygon
      id="line"
      points="0,0 1000,0"
      fill="none"
      stroke="black"
      stroke-width="4px"
    />
    <filter
      id="contour"
      color-interpolation-filters="sRGB"
      x="0"
      y="0"
      width="100%"
      height="100%"
    >
      <feImage href="#line" width="1%" height="2%" />
      <feTile result="TILE" />
    </filter>
  </defs>
</svg>
    
s1

2. Prepare the map

We have our source, which is the stripes. Now let's prepare the map. Add a rgba(127, 127, 127, 0.5) background to make an identity base map. Put some text into the <div>, make it rgba(127, 256, 127, 1), and merge it with the stripes to debug. The alpha channel will be used to displace the stripe pixels.


<svg>
...
      <feMerge>
        <feMergeNode in="SourceGraphic" />
        <feMergeNode in="TILE" />
      </feMerge>
...
</svg>
<div><p>Twist The Reality !</p></div>


s2

3. Bend the lines

Now using what we know about <feDiplacementMap>, move the pixel's y under the text up with yChannalSelector='A', x stay the same since the R channel never changed.

Remove the map, we can see the shape of the contour. But the stripes just shift position, not curved like a contour. Let's make it better by smoothing the alpha.

<svg>
...
      <feDisplacementMap
        in2="SourceGraphic"
        in="TILE"
        scale="10"
        xChannelSelector="R"
        yChannelSelector="A"
        result="OUTPUT"
      />
...
</svg>
        
s3 1 s3 2

4. Smooth the contour

Apply a <feGaussianBlur> to make the alpha spread around, which can create a natural linear gradient for the text. The contour will be curved after this step.

We can control the y-offset by increasing the scale of <feDisplacementMap>, and adjust the curve scope by increasing the stdDeviation of blur.

<svg>
...
    <filter
      id="contour"
      color-interpolation-filters="sRGB"
      x="0"
      y="0"
      width="100%"
      height="100%"
    >
    <feGaussianBlur
      stdDeviation=".8"
      result="BLUR"
    />
...
</svg>
        
s4 1 s4 2

Throw more vertical stripes, and a linear-gradient to show alpha change.

demo-1

Codepen


𐄡


Global Magnifier filter

After the contour, I thought <feDisplacementMap> also could be used to make a Magnifier filter. Check it out:

demo-2

Codepen

I'll not explain the details, but here are some keys to take away:

  1. Due to security reasons, a <feImage> can not be used as map directly, one way is inline SVG directly into href.
  2. Apply the filter with backdrop-filter, so the SourceGraphic will be anything under the target. In this way, the "Magnifier" can show everything on the web page.
  3. Use R and B channel to create an "identity" map, then add a grey to transparent radial-gradient circle in the center which twist the edge of the circle. The type of Magnifier can be controlled by the <stop> position.

𐄡


Closing thoughts

There is so much fun playing with SVG filters, they're like built-in shaders that offer limited parameters, but with a little bit of innovation, they can be powerful and maybe it's the simplest way to tweak pixels on a web page. I'm considering writing a series to dig more for each of them. Thanks for reading, see you next time.



@9am 🕘

@9am 9am self-assigned this Oct 25, 2023
@9am 9am added svg SVG filters Filters labels Oct 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
filters Filters svg SVG
Projects
None yet
Development

No branches or pull requests

1 participant