본문 바로가기

Gen Art

[Three.js] case study: m-trust

https://www.m-trust.co.jp/

 

Intro.

최근에 재밌게 보고 있는 유투버 겸 개발자 (Yuri Artikuh) 스트리밍 영상들을 통해 이런 저런 기법들 배우는 게 많아서 영상 따라 공부하며 배운것들을 공유해보자고 한다.

 

 

영상에서 얻어간 것은 다음과 같다.

 

  1. 이미지를 어떻게 입자화 시킬 것인가?
  2. 입자들에 넣는 반짝이는 이펙트는 어떻게 구현한 것인가?
  3. 비디오와 Three.js 간 동기화 + 애니메이션 처리는 어떻게 할 것인가?
  4. 안보이는 입자 제거를 통해 gpu성능 최적화

 

 

1. 이미지를 어떻게 입자화 시키는가?

 

3가지 스텝으로 이루어진다.

 

먼저, geometry 와 material을 일반적인 Mesh에 연결시키는것이 아닌, Points 객체에 연결시킨다.

const geometry = new THREE.PlaneGeometry(480 * 1.77, 820 * 1.77, 480, 820);
const points = new THREE.Points(geometry, material);

 

다음으로, vertex shader에서 제공되는 gl_PointSize 값으로 입자의 크기를 조절할 수 있다.

  gl_PointSize = 2.

 

마지막으로 fragment shader에서 텍스쳐를 추출해, 색상값으로 사용하면 텍스쳐를 입자화 시킬 수 있다.

  vec4 tt = texture2D(t, vUv);

  vec4 finalTexture = tt;
  gl_FragColor = finalTexture;

 

 

2. 입자들에 넣는 반짝이는 이펙트는 어떻게 구현한 것인가?

 

이 이펙트에 이름이 있는 줄은 몰랐는데, Bloom 이펙트라고 한다.

 

Three.js에서 제공하는 BloomPath 후처리기를 이용하여 적용할 수 있다.

 

먼저 후처리 패스 형성을 위해 모듈들을 임포트 한다.

 

import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass";

 

후처리 파이프라인 형성을 한다. 이 글에서는 사실상 bloomStrength값 컨트롤로 구현할 수 있기에, threshold, radius는 생략 가능하다.

const renderScene = new RenderPass(scene, camera);
const bloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5,
  0.4,
  0.85
);
bloomPass.threshold = settings.bloomThreshold;
bloomPass.strength = settings.bloomStrength;
bloomPass.radius = settings.bloomRadius;

const composer = new EffectComposer(renderer);
composer.addPass(renderScene);
composer.addPass(bloomPass);

3. 비디오와 Three.js간 동기화를 어떻게 처리할 것인가?

 

동기화 후 각종 애니메이션을 구성하기 위해 gsap모듈을 사용하였다.

 

gsap모듈의 존재는 알고는 있었으나, 이번 공부를 통해 정말 그 효용성을 극히 체감할 수 있었다.

 

video 요소에 'ended' 이벤트 리스너를 달고, gsap모듈을 통해, 애니메이션 파이프라인을 구성한다.

 

const video = document.getElementById("video1");
video.addEventListener("ended", () => {
  gsap.to(video, {
    duration: 0.1,
    opacity: 0,
  });

  gsap.to(material.uniforms.distortion, {
    duration: 2,
    value: 3,
    ease: "power2.inOut",
  });
  gsap.to(bloomPass, {
    duration: 2,
    strength: 7,
    ease: "power2.in",
  });

  gsap.to(material.uniforms.progress, {
    duration: 1,
    value: 1,
    ease: "power2.in",
    delay: 1.5,
  });

  gsap.to(material.uniforms.distortion, {
    duration: 2,
    value: 0,
    delay: 2,
    ease: "power2.inOut",
  });
  gsap.to(bloomPass, {
    duration: 2,
    strength: 0,
    delay: 2,
    ease: "power2.out",
    onComplete: () => {
      video.currentTime = 0;
      video.play();
      gsap.to(video, {
        duration: 0.1,
        opacity: 1,
      });
    },
  });

 

여기서 텍스쳐 전환을 위해 progress라는 값을 추가하였다.

 

이 progress값이 텍스쳐간 전환을 위한 키인데, fragment shader에 이 값에 의존하여 각 텍스쳐를 mix했다.

 

  vec4 tt = texture2D(t, vUv);
  vec4 tt1 = texture2D(t1, vUv);

  vec4 finalTexture = mix(tt, tt1, progress);
  gl_FragColor = finalTexture;

 

4. 안보이는 입자 제거를 통해 gpu 최적화

 

인간의 눈이 그리 예민하지 않기에 보일랑 말랑 하는 입자들을 애당초 렌더링하지 않는다면 당연하지만 성능향상에 도움이 된다.

 

fragment shader에서 특정 임계값을 기준으로 discard 처리한다.

 

  if(gl_FragColor.r < 0.1 && gl_FragColor.b < 0.1 && gl_FragColor.g < 0.1){
    discard;
  }

'Gen Art' 카테고리의 다른 글

[WebGL] - case study : The Avener  (0) 2024.07.15
Shattering Text Animation 제작기  (2) 2024.06.06
[Javascript] 폭죽 이펙트 (fireworks)  (3) 2023.05.29