SVG "Filter" 에 대한 정리 - 2

조경석·2023년 2월 3일
0

지난 글에 이어, 드디어 개별 filter primitive 요소를 하나하나 짚어보자.
이번 글에서는 필터의 위치, 병합을 조절하기 위한 기초적인 filter primitive 요소부터 정리할 예정이다.

<feOffset>

가장 단순한 <feOffset>부터 알아보자.
말 그대로 in으로 참조한 요소의 위치 Offset을 지정한다.
dx, dy로 위치값을 조정할 수 있으며, 이 자체로 원본 요소를 다시 그려낸다.

<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
   <defs>
      <filter id="testOffset" filterUnits="userSpaceOnUse" x="0" y="0" width="100%" height="100%">
         <feOffset dx="20" dy="10"/>
      </filter>
   </defs>
   <!-- SVG 영역을 표시하는 청록색 배경 -->
   <rect width="100%" height="100%" fill="cyan"/>
   <!-- 필터가 적용된 50% 너비, 높이의 중앙에 배치된 사각형 -->
   <rect filter="url(#testOffset)" fill="green" x="25%" y="25%" width="50%" height="50%"/>
   <!-- 비교를 위해 원래 도형 영역을 표시하는 검은 사각형 -->
   <rect stroke-width="1" stroke="black" fill="transparent" x="25%" y="25%" width="50%" height="50%"/>
</svg>

위 예제에서 <feOffset>은 필터의 첫번째 filter primitive이므로, in은 생략될 시 SourceGraphic이 된다.
primitiveUnits 역시 생략되어 있으므로 기본값인 userSpaceOnUse에 의해 dx, dy의 값은 절대값(픽셀)로 해석되어 원래 위치(검은 사각형)으로부터 x축 방향으로 20픽셀, y축 방향으로 10px 이동한 형태로 다시 그려졌다.

<feImage>

<feImage>는 일단 쓸 일은 많지 않지만, 꽤 신기한 성질을 가진다.
주요 기능은 xlink:href 특성의 경로를 통해 외부 이미지 소스를 가져오는 것이고, 별도의 result를 지정하지 않아도 대상 이미지 그 자체로 필터 영역에 렌더링된다.
대상 이미지의 형식은 벡터, 래스터를 구분하지 않고, 형식에 상관없이 필터 영역에 맞춰 리사이징된 래스터 이미지가 된다.

위에서 설명한 filter region을 지정하지 않은 경우, 대상 요소와 중심 정렬되는데, 기준을 알 수 없는 애매한 사이즈로 리사이징된다. 반드시 x, y, width, height와 같은 filter region 특성값을 filter에 지정하여 사용해야 한다.

외부 이미지 소스와 필터가 적용될 대상 요소의 가로 세로 비율이 항상 동일하다고 보장할 수 없으므로, <image>와 동일하게 preserveAspectRatio 특성을 지정할 수 있다.

단일 이미지를 텍스쳐로 활용해 여러 도형 영역에 렌더링하는데 사용할 수 있으며, xlink:href를 사용하기 때문에 xlink의 네임스페이스가 먼저 선언되어야 한다.

<div style="text-align:center">
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" viewBox="0 0 200 200">
     <defs>
        <filter id="test" x="-.5" y="-1.5" width="3" height="3">
           <feImage xlink:href="https://velog.velcdn.com/images/whrudtjr/post/51677ad9-8236-49f4-bdc9-3e843c7ca5d4/image.svg"/>
        </filter>
     </defs>
     <rect filter="url(#test)" width="100%" height="100%"/>
  </svg>
</div>

위 이미지가 지난 글의 필터 영역 예제 SVG를 <feImage>로 확대하여 가져온 SVG 예제이다.

딱 봐도 픽셀 퍼펙트하게 렌더링된 벡터 이미지가 아닌 픽셀 경계선이 흐려진 래스터 이미지로 보이는데, 이는 <feImage>는 href로 가져올 이미지 원본의 크기를 기준으로 필터 영역에 맞춰 리사이징하기 때문이다.

<feTile>

이름에서 알 수 있듯이 in으로 참조한 요소를 반복하여 가득 채우는 filter primitive 요소이다.
SVG의 <pattern>과 유사하지만, result를 통해 반복시킨 결과를 다른 filter primitive 요소에서 참조할 수 있다는 차이점이 있다.

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" viewBox="0 0 200 200">
   <defs>
      <filter id="testTile" x="0" y="0" width="100%" height="100%">
         <feImage xlink:href="https://velog.velcdn.com/images/whrudtjr/post/51677ad9-8236-49f4-bdc9-3e843c7ca5d4/image.svg" result="image" width="100" height="100"/>
         <feTile in="image" width="150" height="150"/>
      </filter>
   </defs>
   <rect fill="cyan" width="100%" height="100%"/>
   <rect filter="url(#testTile)" width="100%" height="100%"/>
</svg>

위 <feImage> 예제에서 사용했던 <feImage>를 in으로 참조한 <feTile> SVG를 적용한 예제이다.
<feImage>의 너비, 높이를 100px로 지정해 해당 크기의 타일이 반복되고,
<feTile>의 너비, 높이를 150px로 지정해 타일이 일부 잘려 표시된 것을 볼 수 있다.
즉, in으로 참조하는 대상의 원래 크기에 맞춰 자기 자신의 filter primitive 영역을 가득 채우는 방식이다.

<feMerge>

<feMerge>는 말 그대로 개별 filter primitive들을 병합하는 요소이다.
몇 안되는 하위 요소가 필수적인 filter primitive으로, 개별 filter primitive를 in으로 지정하는 <feMergeNode>를 내부에 포함하여 사용한다.

내부의 <feMergeNode>들은 SVG의 쌓는 순서를 따르며, 첫번째 filter primitive거나 다른 filter primitive의 뒤에 따라올 수 없으므로 in을 생략할 수 없다.

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" viewBox="0 0 200 200">
   <defs>
      <filter id="feMerge" x="0" y="0" width="1" height=".75">
         <feImage xlink:href="https://velog.velcdn.com/images/whrudtjr/post/51677ad9-8236-49f4-bdc9-3e843c7ca5d4/image.svg" x="25" y="25" width="100" height="100" result="image"/>
         <feMerge>
            <feMergeNode in="SourceGraphic"/>
            <feMergeNode in="image"/>
         </feMerge>
      </filter>
   </defs>
   <rect width="100%" height="100%" fill="cyan"/>
   <rect filter="url(#feMerge)" width="100%" height="100%" fill="green"/>
</svg>

위에서 먼저 알아본 <feImage>는 필터가 적용된 요소(SourceGraphic)는 objectBoundingBox를 위한 영역 기준으로만 쓰이고, 다시 렌더링하지 않는다.
위 예제에서는, <feMerge>를 통해 SourceGraphic 위에 image(<feImage>의 result)를 병합했다.

필터를 적용한 원래 초록색 <rect>는 너비, 높이가 100%이지만 <feMerge> 역시 filter 요소이므로, filter에 지정한 height=".75"로 인해 아래쪽이 잘려 배경색인 파란색 <rect>가 표시된 것을 볼 수 있다.

<feFlood>

<feFlood>는 가장 많이 사용되는 filter primitive로, 사각형의 색상 영역을 만든다.
이를 filter subregion이라고 하며, 일반적으로 다른 필터에서 이를 사용해 색상을 합성하거나 영역을 지정하는 마스크 역할을 수행한다.

filter subregion의 색상을 지정하는 flood-color와 투명도를 지정하는 flood-opacity를 특성으로 지정할 수 있으며, 둘 모두 생략 시 불투명한 검은색으로 채워진다.

<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
   <defs>
      <filter id="testFlood" filterUnits="userSpaceOnUse" x="0" y="0" width="100%" height="100%">
         <feFlood x="30" y="30" width="80" height="80" flood-color="blue"/>
      </filter>
   </defs>
   <!-- SVG 영역을 표시하는 청록색 배경 -->
   <rect width="100%" height="100%" fill="cyan"/>
   <!-- 필터가 적용된 50% 너비, 높이의 중앙에 배치된 사각형 -->
   <rect filter="url(#testFlood)" fill="green" x="25%" y="25%" width="50%" height="50%"/>
   <!-- 비교를 위해 원래 도형 영역을 표시하는 검은 사각형 -->
   <rect stroke-width="1" stroke="black" fill="transparent" x="25%" y="25%" width="50%" height="50%"/>
</svg>

<feFlood>를 통해 원래 도형과 관계없는 파란색 사각형의 filter subregion을 그린 예제이다.
<feImage>와 마찬가지로 원래 도형은 사라지며, primitiveUnits의 기본값은 userSpaceOnUse이므로 좌표값 역시 기존 도형에 영향받지 않는 절대값(픽셀)을 사용하고 있다.
이와 달리 기존 도형(SourceGraphic)을 렌더링하려는 경우, <feMerge>나 아래의 <feBlend>를 사용해야 한다.

<feBlend>

지난 SVG 내보내기 글에서 Drop Shadow를 설명하면서 <feBlend>에 대해 간단하게 정리했는데, 좀 더 자세하게 정리해보자.

<feBlend>는 블렌드 모드를 적용하기 위한 filter primitive 요소이다.
블렌드를 적용해 그릴(위쪽) 요소를 지정하는 in,
블렌드를 적용하기 위한 배경색(아래쪽) 요소를 지정하는 in2,
블렌드 모드를 지정할 mode를 주요 특성값으로 가진다.

일반적인 그래픽 툴에서 사용하는 블렌드 모드는 적용한 레이어 아래의 모든 레이어의 '보이는' 픽셀의 색상을 기준으로 적용한다.
하지만 <feBlend>는 반드시 in2(대상)으로 지정할 수 있는(SourceGraphic, result) 요소에 대해서만 적용할 수 있다.
즉 하위 레이어의 모든 요소에 대한 블렌드를 적용하고 싶은 경우, CSS의 mix-blend-mode를 사용하는 것이 낫다.

그렇기 때문에 보통 블렌드를 적용한 배경색상을 지정한 <feFlood>와 함께 사용하며, 일반적으로 drop shadow를 만드는 데 사용된다.

<svg xmlns="http://www.w3.org/2000/svg" width="370" height="200" viewBox="0 0 370 200">
   <defs>
      <filter id="test" x="0" y="0" width="100%" height="100%" filterUnits="userSpaceOnUse">
         <feFlood x="30" y="30" width="100" height="100" flood-color="blue" flood-opacity="1" result="flood1"/>
         <feBlend in="SourceGraphic" in2="flood1" mode="luminosity"/>
      </filter>
   </defs>
   <rect width="100%" height="100%" fill="cyan"/>
   <rect filter="url(#test)" fill="green" x="70" y="70" width="100" height="100"/>
   <rect fill="blue" x="200" y="30" width="100" height="100"/>
   <rect style="mix-blend-mode:luminosity" fill="green" x="240" y="70" width="100" height="100"/>
</svg>


왼쪽이 <feBlend>를 사용해 Luminosity를 적용한 예제로, 파란색 사각형은 <feFlood>로 생성되어 <feBlend>와 같이 초록색 <rect>에 filter로 적용되어 있다.
그로 인해 <feBlend>의 in2인 <feFlood> 영역만큼만 Luminosity가 적용되어, 초록색 사각형의 파란색 사각형과 겹친 부분만 블렌드 모드가 적용된 것을 볼 수 있다.
여기서 역으로 응용하면, <feMerge>가 없이도 필터 영역을 벗어나지만 않으면 필터가 적용된 원래 도형을 그릴 수 있다는 뜻이 된다.

오른쪽은 초록색 <rect>에 CSS의 mix-blend-mode:luminosity를 적용하고, 그 아래에 파란색 <rect>를 배치해둔 것이다.
아래의 파란색 <rect>와 청록색 배경까지 블렌더 모드의 배경색으로 적용되었다.

쉬어가기

다른 필터와 조합되어 가장 많이 사용되는 filter primitive 요소에 대해서 먼저 정리했다.
이 요소들에 대한 이해를 기반으로 drop shadow나 outer glow와 같이 일반적으로 사용되는 필터 조합에 대한 적용방식을 이해할 수 있게 되었다.
이제 다음 글에서는 RGBA 채널을 주로 사용하는 색상 조절 필터에 대해서 정리해보겠다.

0개의 댓글