ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Udemy-R3F-로 배우는-인터랙티브-웹-개발-정리-및-후기
    카테고리 없음 2024. 3. 31. 09:36

    HTML - 웹에서 표현하는 3가지 방법

    1. DOM, 2. SVG

    • 일반적인 HTML 문법으로 그릴 수 있다.
    • CSS 적용
    • 계층적 구조를 가지기 때문에, 내부 엘리먼트들에 이벤트들을 부착할 수 있다. (click, hover, ...)
    • 웹표준 / 웹접근성 (screenReadableText, alt, ...)
    • SEO 검색엔진 최적화
    • Cmd + F 로 텍스트를 찾을 수 있다
    • SVG 는 벡터 기반이라서, 다양한 해상도에서 선명하고 깨끗한 상태 유지
    • SVG 는 정확한 오브젝트 영역 탐지 가능

    3. Canvas

    • javaScript 로 그릴 수 있다.
    • 픽셀 기반
    • 성능이 좋다. 1000~10000개 넘는 오브젝트를 그릴 때 좋은 성능을 보임
    • 2D, 3D(WebGL) 표현 가능

     

    R3F 환경 설정

    • vite + react 프로젝트 생성
    npm create vite@latest

    Select a Framework : React

    Select a variant : TypeScript

    • react-three-fiber 관련 라이브러리 설치
    npm install three @types/three @react-three/fiber @react-three/drei
    • three : 자바스크립트 3D 라이브러리.
    • @react-three/fiber : React 렌더링 방식으로 three.js 를 사용할 수 있게 해주는 라이브러리.
    • @react-three/drei : 개발 편의성을 위해 @react-three/fiber 를 사용할 때 필요할만한 컴포넌트들을 추상화하여 제공하는 라이브러리. 관련 컴포넌트들은 @react-three/drei에서 제공하는 스토리북 페이지에서 둘러볼 수 있다.

     

    Scene, Camera, Renderer 의 구분

    • Scene :  사람, 카메라, 사물 등이 존재하는 공간 자체를 의미한다.
    • Camera : Scene 에서 원하는 장면을 포착하기 위한 렌즈(눈)을 의미한다. 카메라에 담긴 시각적 정보들은 Renderer 에 전달된다.
    • Renderer : Camera 로부터 전달받은 시각적 정보들을 화면에 보여주는 객체를 의미한다.

     

    useFrame

    • 매 프레임 마다 실행되는 훅
    • 인자로 전달하는 콜백 함수 안에 코드를 삽입하면, 매 프레임마다 해당 코드가 실행된다.
      const boxRef = useRef<THREE.Mesh>(null);
      
      useFrame((state, delta, xrFrame) => {
          boxRef.current.rotation.x += 0.01;
          boxRef.current.scale.z += -0.01;
      })

    매 프레임마다 박스를 x 축으로 0.01px만큼 회전시키고, z 축으로 0.01px 만큼 크기를 키우는 코드.

     

     

    Renderer

    • 카메라가 포착한 화면을 어떻게 그릴지에 대한 책임을 지는 주체
    export default function ThreeElement() {
      useFrame((state, delta) => {
          boxRef.current?.rotateY(0.01);
      })
      
      return (
        <>
        </>
      )
    }
    ​
    • useFrame 을 통해 프레임마다 렌더링을 새로 할 수 있다.

     

    Camera

    • Scene 에 존재하는 객체를 포착하는 책임을 지는 주체
    • Camera 의 종류에는 Orthographic, Perspective 가 있다.
      • Orthographic: 원근법이 없는 2D 카메라
      • Perspective(default) : 원근법이 적용되는 3D 카메라
    • Canvas 컴포넌트에 camera 라는 prop 을 전달하여 카메라 속성을 변경할 수 있다.
    <Canvas camera={{
            near: 1,
            far: 20,
            fov: 75,
            position: [5,5,0]
          }}>

     

    Scene

    • 3D 공간 자체에 대한 책임을 지는 주체
    • Mesh
      • 하나의 Mesh 는 Geometery 와 Material 로 이루어져 있다.
      • Geometery : 모양. 형태, 구조, 모델링
      • Material : 모델링 겉에 씌우는 컬러, 스킨, 텍스쳐
    • Scene 에 Mesh 를 넣는 코드 예시1
    // 화면에 그릴 Mesh는 빨간색 박스 Geometry 이고, 빨간색 Material 을 갖고 있다.
    <mesh ref={boxRef}> 
      <boxGeometry />
      <meshStandardMaterial color={'red'} />
    </mesh>
    • Scene 에 Mesh 를 넣는 코드 예시2
    const { scene } = useThree();
    const geometry = new Three.BoxGeometry(1, 1, 1);
    const material = new Three.MeshBasicMaterial({color: 0x00ff00})
    const cube = new Three.Mesh(geometry, material);
    scene.add(cube);

     

    Controls, Helper

    • three.js 계열의 라이브러리들에서는 3D 개발을 할 때 사용할 수 있는 추상화된 컴포넌트, 또는 옵션들을 제공한다.
    • OrbitControls : @react-three/drei 에서 제공. 화면 회전, 확대, 축소 등을 마우스나 터치 이벤트로 핸들링 할 수 있게 도와주는 컨트롤러. 자세한 prop 인터페이스는 스토리북 링크에서 확인할 수 있다.
    • axesHelper : @react-three/fiber 에서 제공. x, y, z 축을 각각 빨강, 초록, 파랑 색으로 그려서 각 축의 방향을 알기 쉽게 해준다.
    • gridHelper : @react-three/fiber 에서 제공. 화면에 그리드를 그려준다.
    • useControls : leva 라이브러리에서 제공. 3D 개체에 적용할 수 있는 숫자 값을 쉽게 조절할 수 있는 GUI 를 제공한다.
    const color = useControls({
        value: 'green'
      })
    ​
    const grid = useControls({
      segment: {value: 10, min: 2, max: 100, step: 1}
    })
    ​
    return (
    <Canvas>
      <color attach={'background'} args={[color.value]} />
      <OrbitControls 
        minAzimuthAngle={-Math.PI / 4} // 최소 X 축 회전 각도
        maxAzimuthAngle={Math.PI / 4}  // 최대 X 축 회전 각도
        minPolarAngle={Math.PI / 6}    // 최소 Y 축 회전 각도
        maxPolarAngle={Math.PI - (Math.PI / 6)} // 최대 Y 축 회전 각도
      />
      <axesHelper args={[6]} /> // x,y,z 축 가이드라인 길이 (단위: m)
      <gridHelper args={[10, grid.segment]} /> // 그리드 크기, 분할 정도 (10m 의 그리드를 grid.segment칸으로 분할)
    </Canvas>
    )

     

    Object3D

    • Object 를 변환하는 방법에는 크게 3가지 방법이 있다.
      • Position
      • Rotation
      • Scale
    • Geometry, Material 은 위의 변환 방법을 사용하지 못한다. 왜냐하면 Geometry, Material 은 Object3D 를 상속받지 않았기 때문이다.
    <mesh ref={boxRef} 
            position={[0, 0, 0]}
            scale={[1, 1, 1]}
            rotation={[0, Three.MathUtils.degToRad(45), 0]}
          >
      <boxGeometry />
      <meshStandardMaterial color={'red'} />
    </mesh>
    export declare type MeshProps = Object3DNode<THREE.Mesh, typeof THREE.Mesh>;
    export declare type BoxGeometryProps = BufferGeometryNode<THREE.BoxGeometry, typeof THREE.BoxGeometry>;                    
    export declare type MeshStandardMaterialProps = MaterialNode<THREE.MeshStandardMaterial[THREE.MeshStandardMaterialParameters]>;

    @react-three/fiber 라이브러리 내부 코드를 보면, Mesh 는 Object3DNode 타입을 상속받았지만, Geometry 와 Material 은 각각 BufferGeometryNode, MaterialNode 타입을 상속받은 걸 확인할 수 있다.

    • group 컴포넌트로 묶은 컴포넌트 목록은 Object3D 방식으로 변환했을 때, 함께 변환된다.
    • Scene 은 World 좌표계, Object는 Local 좌표계를 갖고 있다. 위에서 살펴봤던 axesHelper를 mesh 안에 넣어보면 확인할 수 있음
      const { scene } = useThree();
    ​
    ​
      scene.rotation.x = Three.MathUtils.degToRad(45); // World 좌표계에 대한 x 축 회전
     
    ​
      return (
        <>
          <directionalLight position={[5,5,5]} />
          <group position={[0, 0, 0]} rotation={[0, 2, 0]}> {/* group 내부의 Local 좌표계에 대한 y 축 회전 */}
            <mesh ref={boxRef}
                scale={[1, 1, 1]}
              >
              <boxGeometry />
              <meshStandardMaterial color={'red'} />
            </mesh>
            <mesh ref={boxRef} 
                scale={[1, 1, 1]}
                position={[2, 0, 0]}
              >
              <boxGeometry />
              <meshStandardMaterial color={'blue'} />
            </mesh>
            <mesh ref={boxRef} 
                scale={[1, 1, 1]}
                position={[0, 2, 0]}                        {/* 초록색 박스의 Local 좌표계에 대한 y 축 회전 */}
              >
              <boxGeometry />
              <axesHelper args={[3]} />
              <meshStandardMaterial color={'green'} />
            </mesh>
          </group>
        </>
      )

     

    Geometry

    • 위에서 Object3D 인 Mesh는 Geometry 와 Material 로 이루어져 있다고 설명했다.
    • Geometry 는 모양. 형태, 구조, 모델링
    • 물체가 구성되는 순서는 점 -> 선 -> 면
    • 따라서 Geometry 의 최소 단위는 점 3개( = 선 3개) 가 모인 삼각형 면이다.
    • 모든 3D 물체는 삼각형 면이 모여서 이루어진다.
    • 작업자의 모델링 편의성을 위해서 사각형 면으로 모델링하는 경우가 많으나, 실제로 컴퓨터에는 삼각형 면으로 입력된다.
    • 모든 Geometry 는 BufferGeometry 를 상속받는다.
    • export declare type BoxGeometryProps = BufferGeometryNode<THREE.BoxGeometry, typeof THREE.BoxGeometry>;

     

    Event

    • Three.js 에서는 카메라에서 가까운 쪽 부터 이벤트가 발생된다. 따라서 화면에서 클릭이벤트가 걸린 두 물체가 겹쳐있으면 카메라에 가까운 물체부터 이벤트가 발생함. (일반적인 HTML 구조에서는 이벤트가 발생한 가장 안쪽 객체부터 이벤트가 발생하는 것과 반대임)
    • raycaster 가 이런 이벤트전파의 주된 역할

     

    후기

    이전에 MAYA 라는 3D 툴을 공부한적이 있었는데, 그 때 배웠던 개념들이 다시 떠올라서 반가운 기분이 들었다.

    수업에서는 개념 설명 이후 작은 프로젝트를 만드는 실습이 이어졌는데, 따라해보면서 느낀 점은 기존에 접하던 프론트엔드 개발과는 느낌이 많이 다르다는 것이었다. 기존의 프론트엔드 개발은 부모 컴포넌트와 자식 컴포넌트간의 관계가 데이터 구조, 스타일 등에 영향을 주는 느낌이었다면 , 3D 웹개발은 모든 객체가 서로 독립적으로 존재한다는 느낌을 받았다. (물론 아직 찍먹만 해본 수준이라 뭘 모르고 하는 얘기일 수도..)

    아무튼 개발을 배우기 전부터 3D 에 관심이 있었어서, 블로그에 조금씩 3D 프로젝트를 적용해볼 생각이다.

     

    레퍼런스

    https://www.udemy.com/course/react-three-fiber-r3f/

    해당 콘텐츠는 유데미로부터 강의 쿠폰을 제공받아 작성되었습니다.

Designed by Tistory.