SVG "Filter" 에 대한 정리 - 4

조경석·2023년 2월 26일
0
post-thumbnail

썸네일 출처 : https://fecolormatrix.com/

<feColorMatrix>

직관성은 좀 떨어지는 편이지만, 가장 자유롭게 색상을 조정할 수 있는 <feColorMatrix>에 대해 정리해보자.

지난 글에서 Figma의 SVG 내보내기 형식의 그림자를 적용하는 방식을 정리하면서 간단하게 알아보았지만, 좀 더 상세하게 정리해볼 예정이다.

<feColorMatrix>는 <feComponentTransfer>와 유사한 픽셀별 색상을 조정하는 역할을 수행한다.
요소에 지정된 type과 values 특성값을 기준으로, 각 픽셀별 색상의 채널(RGBA)별로 행렬 곱셈한 값의 합을 계산해 다시 적용한다.

특징적인 부분만 비교해보면 아래와 같은 차이점이 있다.

<feComponentTransfer>
하위 요소인 <feFuncR, G, B, A> 필요
<feFuncR, G, B, A>별로 type을 지정할 수 있음
type="table"을 통해 상대적 리매핑, type="discrete"를 통한 고정값 리매핑이 가능

<feColorMatrix>
<feColorMatrix> 단일 요소
<feComponentTransfer>에만 타입 지정
각 채널에 행렬 곱셈을 적용하므로 색상이 선형적으로만 변경됨

위 차이로 인해, 지난 글에서 알아봤듯이 비선형적인 색상 조정이나 임계처리와 같은 효과를 필요로 하는 경우, <feComponentTransfer>를 사용하는것이 적절하다고 할 수 있다.

type

<feColorMatrix>는 자체적으로 type 특성값을 가지며, 기본값은 matrix다.
matrix 외의 타입은 몇가지 일반적인 효과를 적용하기 위한 함수에 가까워, 내부적으로는 행렬 곱셈을 적용하는 것은 동일하다.

matrix

<feColorMatrix type="matrix" values="r1 r2 r3 r4 r5 g1 g2 g3 g4 g5 b1 b2 b3 b4 b5 a1 a2 a3 a4 a5 "/>

<feColorMatrix>의 기본값으로, values의 값으로 20개의 숫자가 나열된 배열을 받아 5개의 값을 가진 RGBA 4개의 채널로 분할하여
아래의 식처럼 5개의 값을 해당 픽셀의 기존 R,G,B,A 채널값과 곱한 값으로 색상을 다시 지정한다.

R' = r1*R + r2*G + r3*B + r4*A + r5
G' = g1*R + g2*G + g3*B + g4*A + g5
B' = b1*R + b2*G + b3*B + b4*A + b5
A' = a1*R + a2*G + a3*B + a4*A + a5

언제나처럼 예제를 보자.
아래는 색상을 조절하지 않고 원본을 그대로 유지하는 <feColorMatrix> 예제이다.

<!-- 한 줄로 쓴 경우 -->
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 "/>
<!-- 위와 동일한 코드 -->
<feColorMatrix
	type="matrix"
	values="1 0 0 0 0
            0 1 0 0 0
            0 0 1 0 0
            0 0 0 1 0 "/>
<!--	    R G B A shift -->

채널별 수식으로 표현하면 아래와 같다.

R' = 1*R + 0*G + 0*B + 0*A + 0 = R
G' = 0*R + 1*G + 0*B + 0*A + 0 = G
B' = 0*R + 0*G + 1*B + 0*A + 0 = B
A' = 0*R + 0*G + 0*B + 1*A + 0 = A

위 예제와 곱셈을 적용하는 방식 때문에 values의 배열값을 0부터 1 사이의 값으로 생각하기 쉽지만, 어떤 값을 적용해도 상관없다.

saturate

<feColorMatrix type="saturate" values="[number]"/>

간단하게 채도를 조절하는 <feColorMatrix> type으로 CSS의 saturate() 필터와 유사하며, W3C의 Filter Functions 문서에도 saturate()의 equivalent(대응)으로 설명되어 있다.

실제로 적용되는 행렬곱 계산식은 아래와 같다.

R' = (0.213 + (0.787 * values))*R + (0.715 - (0.715 * values))*G + (0.072 - (0.072 * values))*B + 0*A
G' = (0.213 - (0.213 * values))*R + (0.715 + (0.285 * values))*G + (0.072 - (0.072 * values))*B + 0*A
B' = (0.213 - (0.213 * values))*R + (0.715 - (0.715 * values))*G + (0.072 + (0.928 * values))*B + 0*A
A' = 0*R + 0*G + 0*B + 1*A

R 0.213, G 0.715, B 0.072를 기준으로 values가 1인 경우 각 채널값이 1이 되도록 설정되어 있다.
음수를 인수로 받을 수 없는 saturate()와 달리, 위 계산식을 적용하므로 모든 실수를 values로 받을 수 있어, 음수를 지정하면 색조가 뒤집히는 효과가 적용된다.


위 예제는 각각 saturate(.5)와 values=".5"를 설정했으며,
미묘하게 SVG 필터의 명도가 좀 더 높게 나온다.

hueRotate

<feColorMatrix type="hueRotate" values="[deg]"/>

HSL 색상표의 Hue(색조) 색상환을 기준으로 values로 받은 각도값만큼 색조 값을 변경한다.

이미지 출처 : https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl

CSS의 hue-rotate() 필터와 유사하며, W3C의 Filter Functions 문서에도 hue-rotate()의 equivalent(대응)으로 설명되어 있다.
각도 값을 values로 받는것도 동일하지만, hue-rotate()와는 달리 단위(deg)를 표기하지 않는 실수만을 값으로 지정할 수 있다.

실제로 적용되는 행렬곱 계산식은 아래와 같다.
너무 식이 길어 줄바꿈을 적용했다.

R' = ( 0.213 + ( 0.787 * cos(values)) + (-0.213 * sin(values)))*R + 
	 ( 0.715 + (-0.715 * cos(values)) + (-0.715 * sin(values)))*G +
     ( 0.072 + (-0.072 * cos(values)) + ( 0.928 * sin(values)))*B
     
G' = ( 0.213 + (-0.213 * cos(values)) + ( 0.143 * sin(values)))*R + 
	 ( 0.715 + ( 0.285 * cos(values)) + ( 0.140 * sin(values)))*G +
     ( 0.072 + (-0.072 * cos(values)) + (-0.283 * sin(values)))*B
     
B' = ( 0.213 + (-0.213 * cos(values)) + (-0.787 * sin(values)))*R + 
	 ( 0.715 + (-0.715 * cos(values)) + ( 0.715 * sin(values)))*G +
     ( 0.072 + ( 0.928 * cos(values)) + ( 0.072 * sin(values)))*B
     
A' = 1*A

R 0.213, G 0.715, B 0.072를 기준으로 하는 것은 type="saturate"와 동일하고,
cos(0deg) = 1이고, sin(0deg) = 0이므로 360*n°인 경우 각 채널값이 1이 되도록 설정되어 있다.

위 예제는 각각 hue-rotate(180deg)와 values="180"를 설정했으며,
역시나 미묘하게 SVG 필터의 명도가 좀 더 높게 나온다.

luminanceToAlpha

<feColorMatrix type="luminanceToAlpha"/>

말 그대로 각 픽셀의 luminance를 알파로 변환한다.
단순하게 변환만 하는 type이므로 <feColorMatrix>의 type 중 유일하게 values 값을 받지 않는다.

이름은 luminance(휘도)이지만, 여기서의 luminance는 영상 기술에서 사용하는 Luminance key(루마 키)를 의미한다.
쉽게 말하자면 각 픽셀의 검은색과의 명도차이를 알파로 변환한다고 생각하면 된다.
원래 검은색이었던(어두운) 영역은 투명해지며, 원래 흰색이었던(밝았던) 영역은 불투명한 검은색이 된다.
루마 키와 유사하게 보통 마스킹을 위한 필터 효과로 사용할 수 있다.

단순하게 변환만 하는 type이므로 <feColorMatrix>의 type 중 유일하게 values 값을 받지 않는다.

실제로 적용되는 행렬곱 계산식은 아래와 같다.

R' = 0*R + 0*G + 0*B + 0*A + 0 = 0
G' = 0*R + 0*G + 0*B + 0*A + 0 = 0
B' = 0*R + 0*G + 0*B + 0*A + 0 = 0
A' = 0.2125*R + 0.7154*G + 0.0721*B + 0*A + 0

R 0.213, G 0.715, B 0.072를 기준으로 하는 것은 동일하지만 반올림 자릿수가 다르게 표시되어 있는데, 실제로 계산값이 다른지 문서상으로만 그렇게 표기했는지는 확인하지 못했다.

A 채널 계산에서 RGB값에 상수를 곱하고 기존 A값에는 0을 곱하는 부분을 주목하자.
A를 제외한 각 채널의 색상에서만 광도를 계산하므로, 실제 이미지의 Alpha값은 무시하는 특성이 있으며,
모든 색상값은 광도 참조로만 사용하고 모든 채널의 색상을 0으로 변환하므로 투명도만 적용된 검은색 이미지로 표시된다.

위 예제의 이미지는 의도적으로 중앙에 50% 투명도가 적용되어 있으며, td의 배경색을 빨간색으로 지정해두었다.
오른쪽 luminanceToAlpha 결과에서는 50% 투명도 영역의 경계선이 보이지 않는것을 확인할 수 있다.

0개의 댓글