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는 다음처럼 렌더링됩니다.

R3F에서 Shader를 통한 Material

먼저 다음처럼 drei의 shaderMaterial를 이용해 GLSL로 재질을 만들 수 있습니다.

import { shaderMaterial } from '@react-three/drei'

const WaveShaderMaterial = shaderMaterial(
  {
    uColor: new THREE.Color(1, 0, 0)
  },

  /* glsl */`
    varying vec2 vUv;

    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,

  /* glsl */`
    uniform vec3 uColor;
    varying vec2 vUv;

    void main() {
      gl_FragColor = vec4(vUv.y * uColor, 1.0);
    }
  `
)

리엑트는 선언형 프로그래밍(?) 방식을 권장하므로 위에서 만든 WaveShaderMaterial을 Tag처럼 선언해서 사용할 수 있도록 해야 합니다. 이때 R3F의 extend가 사용됩니다.

import { Canvas, extend } from '@react-three/fiber'

extend({ WaveShaderMaterial })

실제 사용은 다음과 같습니다.

const MyCanvas = () => {
  return (
    <Canvas>
      <pointLight position={[10,10,10]} />
      <mesh>
        <planeGeometry args={[5,5]} />
        <waveShaderMaterial uColor={"white"} />
      </mesh>
    </Canvas>
  )
}

function App() {
  return (
    <MyCanvas />
  )
}

결과는 다음과 같습니다.