티스토리 뷰

https://ljy1011.tistory.com/224

 

[React.js] generator(제네레이터) 를 활용해 3D 에니메이션 만들기! with react three fiber/ framer-motion / react

포트폴리오 제작중, 3d text mesh의 역동적인 움직임을 중심으로 나를 표현 하는 3d 에니메이션을 만들어 보고 싶다는 생각이 들었다. 그래서 three.js 관련된 라이브러리를 사용해 구현해 보기로 하

ljy1011.tistory.com

이어서.

 

이번에는 react-trhee-fiber 를 활용해 3d text geometry를 생성해 보도록 하겠다.

 

설치 라이브러리


npm i three three-stdlib @react-three/fiber @react-three/drei @react-three/rapier framer-motion framer-motion-3d

 

1. three

  • 설명: WebGL 기반 3D 그래픽을 쉽게 구현할 수 있는 JavaScript 라이브러리.
  • 용도: 3D 모델링, 애니메이션, 조명, 텍스처 맵핑 등 3D 그래픽의 기본적인 빌딩 블록 제공.

2. three-stdlib

  • 설명: Three.js의 추가 유틸리티 모음.
  • 용도: OrbitControls, GLTFLoader 같은 자주 쓰이는 툴과 확장을 제공.
  • 참고: 이 패키지는 Three.js의 확장 기능이 점점 자체 패키지로 분리되는 경향이 있어, 필요한 유틸리티를 제공함.

3. @react-three/fiber

  • 설명: React에서 Three.js를 효율적으로 사용할 수 있도록 돕는 React Renderer.
  • 용도: React 컴포넌트 기반으로 Three.js 오브젝트와 씬을 선언적으로 생성하고 관리.

4. @react-three/drei

  • 설명: React Three Fiber를 위한 확장 컴포넌트 모음.
  • 용도: OrbitControls, Text, Sky, Environment 등 추가적인 유용한 3D 구성 요소를 쉽게 사용할 수 있음.

5. @react-three/rapier

  • 설명: Rapier(빠르고 경량의 물리 엔진)를 React Three Fiber와 통합하기 위한 라이브러리.
  • 용도: 충돌 감지, 중력, 강체(Rigid Body) 등 물리 엔진 기능을 구현.

6. framer-motion

  • 설명: React 애니메이션 라이브러리로 간단하면서 강력한 애니메이션 구현 가능.
  • 용도: 모션 컴포넌트 및 키프레임 기반 애니메이션을 활용해 자연스러운 UI 동작 구현.

7. framer-motion-3d

  • 설명: Framer Motion의 기능을 Three.js 오브젝트에도 확장한 라이브러리.
  • 용도: React Three Fiber 환경에서 3D 오브젝트의 애니메이션을 간단히 작성.

 

 

구현


 

1. 주요 라이브러리와 설정

import { extend, Object3DNode, useFrame, useThree } from "@react-three/fiber";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
import myFont from "../Roboto_Bold.json";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry";
import { useEffect, useRef, useState } from "react";

extend({ TextGeometry });
  • @react-three/fiber: React에서 Three.js를 효율적으로 사용할 수 있도록 돕는 라이브러리.
  • FontLoader: Three.js에서 3D 텍스트를 생성하기 위해 사용하는 글꼴 로더.
  • TextGeometry: 3D 텍스트를 생성할 때 사용하는 기하학 객체. three.js 로 부터 Import 한다.
  • extend: TextGeometry를 React Three Fiber에서 사용할 수 있도록 확장.

 폰트 파일(Roboto_Bold.json)을 FontLoader를 통해 로드한 후, 이를 3D 텍스트의 글꼴로 사용한다.

 

// Add types to ThreeElements elements so primitives pick up on it
declare module "@react-three/fiber" {
  interface ThreeElements {
    textGeometry: Object3DNode<TextGeometry, typeof TextGeometry>;
  }
}

 

Three.js에서 제공하는 TextGeometry는 기본적으로 React Three Fiber에 포함되지 않으므로, 이를 사용하려면 추가적인 타입 선언이 필요하다. declare module을 설정해 주자.



2. TextGemetry component 생성

// @ts-ignore
const font = new FontLoader().parse(myFont);

const Impossible = () => {
  return (
    <group dispose={null} position={[-6.5, -0.5, 2.5]}>
      <mesh receiveShadow castShadow rotation={[0, 0, -0.1]}>
        <textGeometry
          args={["I", { font: font, size: 2.5, height: 1 }]}
        ></textGeometry>
        <meshStandardMaterial roughness={0.5} color="#ff0000" />
      </mesh>
      <mesh
        receiveShadow
        castShadow
        rotation={[0, 0, -0.1]}
        position={[1.2, 0, 0]}
      >
        <textGeometry
          args={["mpossible", { font: font, size: 2, height: 1 }]}
        ></textGeometry>
        <meshStandardMaterial roughness={0.5} color="#ff0000" />
      </mesh>
    </group>
  );
};

 

 
  • 구성 요소:
    • group: 여러 개의 3D 객체를 하나로 묶는 컨테이너. group에 position을 설정하면 해당 위치에 객체가 생성된다. 
    • mesh: 3D 텍스트를 포함하는 요소로, textGeometry와 meshStandardMaterial을 사용.
    • receiveShadow: 해당 객체가 그림자를 받을지 여부를 결정.
    • castShadow: 해당 객체자 그림자를 생성할지 여부를 결정
  • 텍스트 설정:
    • args: ["텍스트 내용", { font, size, height }] 형태로 3D 텍스트의 속성 설정.
      • font: 폰트 객체 (Roboto_Bold.json 사용).
      • size: 텍스트 크기.
      • height: 깊이(3D 텍스트의 두께).
    • 색상 및 재질:
      • meshStandardMaterial: 표준 재질로, roughness와 color 속성 설정.

 

3. 조명 설정하기

Three.js에서 조명은 3D 씬의 조도와 분위기를 설정하고 객체의 깊이와 입체감을 표현하는 데 필수적이다. 적절한 조명을 사용하면 장면의 사실감과 미적 표현을 극대화할 수 있다.

text에 조명을 주기 위해 3가지 조명을 적용해 보았다.

 

Three.js Lights 


- AmbientLight (환경광)

  • 설명: 씬 전체에 균일한 조명을 비춘다.
  • 특징:
    • 특정 방향성이 없음
    • 그림자를 생성하지 않음
    • 씬의 기본 밝기를 설정하는 데 유용
<ambientLight intensity={0.1} />

 

- DirectionalLight (직사광)

  • 설명: 태양과 같은 무한한 거리에서 한 방향으로 빛을 비춘다.
  • 특징:
    • 특정 방향으로 평행한 빛을 방출
    • 그림자를 생성할 수 있음
    • 빛의 세기가 거리와 무관하게 동일
function DirectionalLight() {
  const directionalLightRef = useRef();
  useHelper(directionalLightRef, PointLightHelper, 1, "cyan");
  
  const config = useControls("DirecLights", {
    color: "#ffffff",
    intensity: { value: 5, min: 0, max: 10, step: 0.1 },
    position: { value: [0, 5, 10] },
  });
  return (
    <directionalLight
      ref={directionalLightRef}
      transition={{ duration: 1 }}
      {...config}
    />
  );
}

 

- SpotLight (스포트라이트)

  • 설명: 특정 방향으로 빛을 집중시켜 원뿔 모양으로 비춘다.
  • 특징:
    • 거리와 각도에 따라 빛이 감소
    • 그림자를 생성 가능
    • 원뿔 모양의 빛이 강조된 연출에 적합
function SpotLight() {
  const spotLightRef = useRef();
  useHelper(spotLightRef, PointLightHelper, 1, "cyan");

  const config = useControls("SpotLights", {
    color: "#ffffff",
    intensity: { value: 300, min: 0, max: 5000, step: 0.1 },
    position: { value: [0, 20, 12] },
    angle: { value: 1, min: 0, max: 5, step: 0.1 },
  });
  return (
    <spotLight
      ref={spotLightRef}
      transition={{ duration: 1 }}
      shadow-mapSize-width={2048}
      shadow-mapSize-height={2048}
      shadow-bias={-0.00001}
      castShadow
      {...config}
    />
  );
}

 

 

그림자를 투영할 planeGeometry 설정


{/* 그림자 바닥 */}
<mesh
  receiveShadow
  renderOrder={1000}
  position={[0, 0, 0]}
  rotation={[-Math.PI / 2, 0, 0]}
>
  <planeGeometry args={[20, 20]} />
  <motion.shadowMaterial transparent opacity={0.15} />
</mesh>

 

1. <mesh>

  • 역할: Three.js에서 물체를 정의하는 기본 구성 요소로, 기하학(geometry)과 재질(material)을 결합하여 화면에 렌더링
  • 속성:
    • receiveShadow: 해당 물체가 그림자를 받을 수 있게 활성화.
    • renderOrder={1000}: 렌더링 순서를 명시적으로 지정.
      • 숫자가 높을수록 후순위에 렌더링됩니다.
      • 그림자가 올바르게 보이도록 순서를 조정할 때 사용.
    • position={[0, 0, 0]}: 평면의 위치를 원점에 배치.
    • rotation={[-Math.PI / 2, 0, 0]}: 평면을 X축 기준으로 90도 회전하여 바닥처럼 놓이게 설정.

2. <planeGeometry>

  • 역할: 2D 평면 기하 구조를 정의.
  • 속성:
    • args={[20, 20]}: 평면의 크기를 설정.
      • 여기서는 폭 20, 높이 20의 큰 정사각형 평면을 생성.

3. <motion.shadowMaterial>

  • 역할: 그림자가 투영될 때 사용되는 특수한 재질로, 그림자만 표시하고 투명한 영역은 배경과 혼합
  • 속성:
    • transparent: 재질의 투명도 활성화.
      • 그림자가 있는 영역만 보이게 합니다.
    • opacity={0.15}: 투명도 설정.
      • 값이 낮을수록 그림자가 옅게 보입니다(15% 불투명).

전체 역할

  1. 바닥 평면 생성:
    • planeGeometry로 바닥 모양을 생성.
    • rotation을 통해 평면을 아래로 눕힘.
  2. 그림자 렌더링:
    • shadowMaterial을 사용해 그림자가 투명하게 투영될 수 있도록 설정.
    • opacity로 그림자의 강도를 조절.
  3. 렌더링 우선순위 조정:
    • renderOrder로 바닥 평면이 그림자보다 먼저 렌더링되도록 보장.

 

DirectionalLight 만 적용

 

SpotLight 만 적용

 

DirectionalLight & SpotLight 동시 적용

 

 

다음 글은 leva 라이브러리를 사용하여 light를 설정하는 방법을 다뤄보겠다.