R3F(React Three Fiber) - 3D model / Animation

H.GOO·2024년 4월 11일
1

WEB-3D-Project

목록 보기
8/10

🪴 3D model

R3F에서 지원하는 3D 모델 파일 형식


가장 많이 사용되는 형식: 3DS, FPX, glTF, OBJ

이 중에서도 glTF 형식은 Khronos Group 에서 관리하는 표준으로 효율성이 아주 뛰어난 형식임.




🪴 Animation

3D 모델 캐릭터 렌더링

glb 확장자의 모델 파일을 사용하여 3D 캐릭터를 렌더링할 수 있음. glb 확장자는 GL Transmission Format (glTF)의 이진 형식(Binary)으로 파일 크기와 렌더링 속도면에서 유리함.

3D 모델 다운로드 링크

import { useEffect, useState } from 'react';
import { useGLTF } from '@react-three/drei';

const Box = () => {
  const model = useGLTF('./models/model.glb');
  
  // 모델을 중앙에 맞추기 위한 모델 높이값 추출
  const [height, setHeight] = useState(0);
  useEffect(() => {
    let minY = Infinity, maxY = -Infinity;
    
    model.scene.traverse((item) => {
      if (item.isMesh) {
        const geomBbox = item.geometry.boundingBox;
        if (minY > geomBbox.min.y) minY = geomBbox.min.y;
        if (maxY < geomBbox.max.y) maxY = geomBbox.max.y;
      }
    });

    const h = maxY - minY; // 약 1.8
    setHeight(h);
  }, [model.scene]);
  
  return (
    <>
      <primitive
        scale={5} // 모델 사이즈
        position-y={-(height / 2) * 5} // 모델을 중앙에 맞추기 위한 y 값 설정
        object={model.scene} // 모델 객체
      />
    </>
  )
...

애니메이션 실행

  • reset(): 애니메이션을 초기 상태로 되돌림.
  • fadeIn(0.5): 0.5초 동안 애니메이션을 서서히 페이드 인
  • play(): 애니메이션을 재생
  • fadeOut(0.5): 0.5초 동안 애니메이션을 서서히 페이드 아웃

import { useEffect, useState } from 'react';
import { Environment, OrbitControls, useAnimations, useGLTF } from '@react-three/drei';
import { useControls } from 'leva';

const Box = () => {
  // 3D 캐릭터 모델 객체
  const model = useGLTF('./models/model.glb');
  
  // 애니메이션 객체
  const animation = useAnimations(model.animations, model.scene);
  
  // useControls로 애니메이션 선택할 수 있도록 함.
  const { actionName } = useControls({
    actionName: {
      value: animation.names[1],
      options: animation.names,
    },
  });

  // actionName이 바뀔 때마다 애니메이션 리셋 후 실행
  useEffect(() => {
    const action = animation.actions[actionName];
    action.reset().fadeIn(0.5).play();

    return () => {
      action.fadeOut(0.5);
    };
  }, [actionName]);

  // 모델을 중앙에 맞추기 위한 모델 높이값 추출
  const [height, setHeight] = useState(0);
  useEffect(() => {
    let minY = Infinity,
      maxY = -Infinity;

    model.scene.traverse((item) => {
      if (item.isMesh) {
        const geomBbox = item.geometry.boundingBox;
        if (minY > geomBbox.min.y) minY = geomBbox.min.y;
        if (maxY < geomBbox.max.y) maxY = geomBbox.max.y;
      }
    });

    const h = maxY - minY; // 약 1.8
    setHeight(h);
  }, [model.scene]);

  return (
    <>
      <OrbitControls />

      <Environment preset='sunset' />

      <primitive
        scale={5}
        position-y={-(height / 2) * 5}
        object={model.scene}
      />
    </>
  );
};

export default Box;


0개의 댓글