Drei 컴포넌트 : RenderTexture

RenderTexture는 재질의 map 속성에 사용할 수 있는 텍스쳐를 별도의 카메라와 광원 그리고 모델로 구성된 씬을 통해 만들어 주는 컴포넌트로 그 활용성이 매우 높습니다.

RenderTexture가 적용된 Cube 컴포넌트입니다.

function Cube() {
  const textRef = useRef()
  useFrame((state) => (textRef.current.position.x = Math.sin(state.clock.elapsedTime) * 2))
  return (
    <mesh>
      <cylinderGeometry />
      <meshStandardMaterial>
        <RenderTexture attach="map" anisotropy={16}>
          <PerspectiveCamera makeDefault manual aspect={1 / 1} position={[0, 0, 5]} />
          <color attach="background" args={['orange']} />
          <ambientLight intensity={0.5} />
          <directionalLight position={[10, 10, 5]} />
          <Text font={suspend(inter).default} ref={textRef} fontSize={4} color="#555">
            hello
          </Text>
          <Dodecahedron />
        </RenderTexture>
      </meshStandardMaterial>
    </mesh>
  )
}

위의 코드에서 Dodecahedron 컴포넌트가 보이는데, 코드는 다음과 같습니다.

function Dodecahedron(props) {
  const meshRef = useRef()
  const [hovered, hover] = useState(false)
  const [clicked, click] = useState(false)
  useFrame(() => (meshRef.current.rotation.x += 0.01))
  return (
    <group {...props}>
      <mesh
        ref={meshRef}
        scale={clicked ? 1.5 : 1}
        onClick={() => click(!clicked)}
        onPointerOver={() => hover(true)}
        onPointerOut={() => hover(false)}>
        <dodecahedronGeometry args={[0.75]} />
        <meshStandardMaterial color={hovered ? 'hotpink' : '#5de4c7'} />
      </mesh>
    </group>
  )
}

실제 Cube는 다음처럼 렌더링됩니다.

SkinedMesh의 Clone

애니메이션 모델 리소스로부터 가져온 데이터를 여러개 복사해서 사용하고자 할때 필요한 코드입니다.

1개만 사용할때는 문제가 없는 기본적으로 작성된 코드는 다음과 같습니다.

import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useGLTF, useAnimations } from '@react-three/drei'
import * as THREE from "three"

export function Robot({ color = "gray", ...props }) {
  const group = useRef()
  const { nodes, materials, animations } = useGLTF('/Robot.glb')
  const { actions } = useAnimations(animations, group)
  const [ animation, setAnimation ] = useState("Idle")

  useEffect(() => {
    actions[animation].reset().fadeIn(0.5).play()
    return () => actions[animation].fadeOut(0.5)
  }, [ animation ])

  materials.Alpha_Body_MAT.color = new THREE.Color(color)

  return (
    <group ref={group} {...props} dispose={null}>
      <group name="Scene">
        <group name="Armature" rotation={[Math.PI / 2, 0, 0]} scale={0.01}>
          <primitive object={nodes.mixamorigHips} />
          <skinnedMesh name="Alpha_Joints" geometry={nodes.Alpha_Joints.geometry} material={materials.Alpha_Joints_MAT} skeleton={nodes.Alpha_Joints.skeleton} />
          <skinnedMesh name="Alpha_Surface" geometry={nodes.Alpha_Surface.geometry} material={materials.Alpha_Body_MAT} skeleton={nodes.Alpha_Surface.skeleton} />
        </group>
      </group>
    </group>
  )
}

useGLTF.preload('/Robot.glb')

위의 코드에 대한 모델 여러개를 장면에 추가하기 위해 다음처럼 코드를 수정해야 합니다.

import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useGLTF, useAnimations } from '@react-three/drei'
import * as THREE from "three"
import { useGraph } from '@react-three/fiber'
import { SkeletonUtils } from "three-stdlib"

export function Robot({ color = "gray", ...props }) {
  const group = useRef()
  const { scene, materials, animations } = useGLTF('/Robot.glb')
  const clone = useMemo(() => SkeletonUtils.clone(scene), [scene])
  const { nodes } = useGraph(clone)
  const { actions } = useAnimations(animations, group)
  const [ animation, setAnimation ] = useState("Idle")

  useEffect(() => {
    actions[animation].reset().fadeIn(0.5).play()
    return () => actions[animation].fadeOut(0.5)
  }, [ animation ])

  const material = materials.Alpha_Body_MAT.clone()
  material.color = new THREE.Color(color)

  return (
    <group ref={group} {...props} dispose={null}>
      <group name="Scene">
        <group name="Armature" rotation={[Math.PI / 2, 0, 0]} scale={0.01}>
          <primitive object={nodes.mixamorigHips} />
          <skinnedMesh name="Alpha_Joints" geometry={nodes.Alpha_Joints.geometry} material={materials.Alpha_Joints_MAT} skeleton={nodes.Alpha_Joints.skeleton} />
          <skinnedMesh name="Alpha_Surface" geometry={nodes.Alpha_Surface.geometry} material={material} skeleton={nodes.Alpha_Surface.skeleton} />
        </group>
      </group>
    </group>
  )
}

useGLTF.preload('/Robot.glb')

위의 코드에서 SkeletonUtils를 사용하고 있는데 이를 위해 아래의 코드를 실행하여 three-stdlib를 설치해야 합니다.

npm i three-stdlib