유저 인터랙션과 메타볼 효과를 합치면, Metaball Interaction

fecapark·2023년 10월 11일
54

토이프로젝트

목록 보기
2/2
post-thumbnail

프로젝트 링크(직접 해보세요!)

안녕하세요.

메타볼은 제가 정말 좋아하는 그래픽 모델입니다.
연속적이고, 부드럽지만 동시에 끈끈한 느낌을 주는 것이 인상적인데요.

하지만 이 메타볼을 웹 상에서 단순하게 렌더링하는 것은 많은 한계가 있습니다.

23/10/20 추가) 후술할 한계가 있는 줄 알았는데, 좀 더 시도해보니 아니었습니다.


메타볼의 한계

가장 간단한 방법은 svg filter 를 이용해서 구현하는 방법이 있지만,

아래와 같이 성능이 굉장히 좋지 않고,
메타볼 효과가 매우 잘 깨진다는 단점이 있습니다.

따라서 WebGL을 이용해서 구현한다면,
성능은 물론이고, 정확도도 동시에 챙길 수 있을거라 생각했습니다.


23/10/20 수정)

svg filter 를 이용하더라도 정확하고 효과적으로 렌더링하는 방법이 있습니다.

무엇보다 이 방법의 장점은 html 태그를 이용하여 렌더링하기 때문에,
애니메이션 구성에 있어서 손쉽게 GPU의 힘을 빌릴 수 있다는 최대 장점이 있습니다.

하지만, 구성하는 애니메이션에서 다뤄야할 오브젝트들이 많아진다면
성능적으로 WebGL을 사용하는 것이 대안이 될 수 있습니다.

const blur = 12;
const alpha = blur * 6;
const r = "1 0 0 0 0";
const g = "0 1 0 0 0";
const b = "0 0 1 0 0";
const a = `0 0 0 ${alpha} ${alpha / -2}`;

(이해를 위해) 왜 위와 같이 값을 설정했는가?

후술할 방법론을 읽어보면 알겠지만(먼저 후술한 방법론을 읽으시는 것을 추천드립니다),
r, g, b, afeColorMatrix 태그에 값으로 넣어줄건데 저렇게 설정한 이유는 다음과 같습니다.

r, g, b 는 각 픽셀의 RGB값을 그대로 사용하기 위해 저렇게 설정했습다.

문제는 a 인데, MDN의 feColorMatrix 문서에 따르면 필터처리 된 각 픽셀의 alpha 값이 도출되는 식을 저 값에 대입해보면 아래와 같이 나옵니다.

필터처리 된 픽셀의 투명도 값 = 블러처리 된 픽셀의 투명도 값 x alpha - alpha x 0.5

어차피 모든 투명도 값은 0~1 사이이므로,

0 <= 블러처리 된 픽셀의 투명도 값 <= 1

을 만족하고, 곧

0 <= 블러처리 된 픽셀의 투명도 값 x alpha <= alpha

-0.5 x alpha <= 필터처리 된 픽셀의 투명도 값 <= 0.5 x alpha

라는 범위가 나옵니다.
하지만, 필터처리된 픽셀의 투명도 값의 범위도 마찬가지로

0 <= 필터처리 된 픽셀의 투명도 값 <= 1

이므로, 음수 범위로 계산된 값들은 전부 투명도가 0이 되기에 화면에 그려지지 않습니다.
결과론적으로 후술할 alpha-thresholding 을 0.5를 기준으로 수행하는 것이죠.

또한, 기본적으로 설정해준 alpha 의 값이 굉장히 크기 때문에(블러처리에 대응하기 위해) 메타볼이 깨지거나 이상하게 렌더링 될 걱정도 없게 되고요.



이 값들을 이용하여 아래 svg filter 를 아래와 같이 만들어줍니다.

<svg style="position: absolute">
  <defs>
    <filter id="filter" colorInterpolationFilters="sRGB">
      <feGaussianBlur stdDeviation="${blur값}" />
      <feColorMatrix values="${r값} ${g값} ${b값} ${a값}" />
      <feComposite in='SourceGraphic' />
    </filter>
  </defs>
</svg>
  • 원래는 feComponentTransferfeFuncA 태그를 이용하여 렌더링하는 방법을 사용하였지만, feColorMatrix 태그 하나로 변경하였습니다.

  • feComposite 은 그라데이션 효과를 위해 사용했습니다. 없어도 됩니다!

.container {
	filter: url(#filter);
}

filter 태그에서 적었던 id 값을 이용하여 만든 svg filter를 원하는 컨테이너에 적용해줍니다.


WebGL로 메타볼 만들기

저는 WebGL로 PIXI.js를 이용했고,
PIXI.js의 filter를 이용하여 메타볼을 구현했습니다.

메타볼 효과는 두 가지 filter들을 합쳐 간단하게 만들어낼 수 있습니다.


1. BlurFilter

https://pixijs.com/examples/filters-basic/blur

화면에 가우시안 블러 효과를 제공합니다.
CSS의 filter: blur() 함수와 동일한 효과를 얻습니다!


2. Alpha-Threshold Filter

투명도에 따라 원하는 임계처리(thresholding)를 제공합니다.

그런데 문제는, PIXI.js에 이런 필터는 없습니다.

따라서 제가 직접 GLSL을 이용하여 커스텀 filter를 제작해주었습니다.

import { Filter } from "pixi.js";

export function useAlphaThresholdFilter({
  threshold, r, g, b
}: {
  threshold: number;
  r: number;
  g: number;
  b: number;
}) {
  const filter = new Filter(
    undefined,
    `
      precision mediump float;
      varying vec2 vTextureCoord;
      uniform sampler2D uSampler;

      uniform float threshold, r, g, b;
      
      void main() {
          vec4 color = texture2D(uSampler, vTextureCoord);
          vec3 maskColor = vec3(r, g, b) / 255.0;

          if (color.a > threshold) {
            gl_FragColor = vec4(maskColor, 1.0);
          } else {
            gl_FragColor = vec4(vec3(0.0), 0.0);
          }
      }
    `,
    { threshold, r, g, b }
  );
  
  return filter;
}

Filter들 적용하기 + 방법론

이 filter들을 아래와 같은 방법으로 적용해주었습니다.

  1. 원들을 화면에 렌더링한다.
  2. 화면 전체를 BlurFilter로 blur 처리를 해준다.
    (이때 블러 처리가 되었다면, 각 원의 바깥 쪽으로 갈수록 기존 색상의 opacity가 낮아집니다.)
  3. 화면 전체에 Alpha-Threshold Filter를 적용한다.
    3-1. 일정 opacity 이하로 내려온 값들은 화면에 렌더링하지 않는다.
    3-2. 그게 아니라면 모두 opacity를 1로 적용한다.

3번 코드의 내용이 커스텀 filter 코드의 내용입니다!

이 filter들을 적용하면 아래와 같은 효과를 얻을 수 있습니다.

가장 핵심은 blur 처리를 했을 때,
기존 색상의 opacity가 외곽으로 갈수록 낮아진다는 것 인데요.

그렇기 때문에 여러 원들이 겹칠 때 겹치는 부분은 opacity가 더 높아지므로 alpha-thresholding을 적용하면 부드럽게 떨어지는 윤곽선을 만들어낼 수 있는 트릭인거죠.


WebGL로 만들었을 때 장점

svg filter 로 메타볼을 만들었을 때 가장 큰 문제였던 정확도 문제가 말끔하게 해결됩니다.

게다가 성능도 많이 잡아먹지 않습니다!


그래서?

원래는 단순히 여기까지 만들었는데,

blur를 얼마나 해줄지,
alpha-thresholding의 임계치를 얼마로 해줄지에 따라
아래처럼 서로 다른 메타볼 효과가 구현되는 것을 확인할 수 있었는데요.

여기서 끝내기엔 아쉽죠.

그래서 메타볼 파라미터를 커스터마이징 할 수 있는 컨트롤러를 만들어
다양한 메타볼 효과를 구현할 수 있게 프로젝트를 구성했습니다.

직접 프로젝트를 들어가서 체험해보세요!

감사합니다!

profile
중괄호로 뭐든지 만듭니다.

2개의 댓글

comment-user-thumbnail
2023년 10월 16일

오.. 신기하군요.

답글 달기
comment-user-thumbnail
2023년 10월 17일

나중에 한번 참고해볼게요

답글 달기

관련 채용 정보