렌더 타겟

Starter pack

React Three Fiber를 사용할 때 Canvas 컴포넌트를 추가하고 그 안에 3D 장면을 넣으면 화면에 렌더링됩니다.

Three.js에 대한 경험이 있다면, WebGLRenderer를 먼저 생성하고 renderer.render(scene, camera)를 호출하여 장면을 화면에 렌더링해야 한다는 것을 알고 있습니다.

다행히도, React Three Fiber는 이러한 모든 과정을 내부적으로 처리해주지만, 더 고급 사용 사례를 위해서는 이 과정이 어떻게 작동하는지 아는 것이 중요합니다.

렌더러의 역할은 3D 장면을 처리하여 우리가 화면에서 보는 실제 2D 이미지를 만드는 것입니다.

기본적으로, 렌더러의 출력은 Canvas 컴포넌트로 설정되어 화면에 표시되지만, 텍스처로도 출력할 수 있습니다 (이를 WebGLRenderTarget을 통해 수행합니다).

지금은 약간 개념적일 수 있지만, 실제로 어떻게 작동하는지 그리고 창의적으로 어떻게 사용할 수 있는지 살펴보겠습니다.

보안 카메라

Render Targets가 어떻게 작동하고, 이를 사용하여 어떤 작업을 할 수 있는지를 이해하기 위해 다음과 같은 3D 장면을 준비했습니다:

  • Alex Safayan의 거실 3D 모델 CC-BY 제공, Poly Pizza를 통해 제공
  • Ready Player Me 아바타 (우리가 포트폴리오 레슨에서 사용하는 것과 유사)가 TV에서 스폰지밥을 감상하는 모습
  • 여러 버튼이 있는 2D 리모컨

3D 아바타가 TV를 보는 모습

목표는 보안 카메라 관점에서 장면을 TV에 렌더링하여 감시 시스템을 만드는 것입니다.

장면을 텍스처로 렌더링하기

현재 장면을 텍스처로 렌더링하려면 Render Target을 만들어야 합니다.

Drei 라이브러리 덕분에 useFBO 훅을 사용하여 쉽게 Render Target을 만들 수 있습니다:

// ...
import { useFBO } from "@react-three/drei";

export const Experience = () => {
  const cornerRenderTarget = useFBO();
  // ...
};

Render Target을 처음부터 만드는 방법을 알고 싶다면, useFBO 훅의 소스 코드를 여기서 확인할 수 있습니다. 내부적으로 어떻게 작동하는지 이해하기 위해 이런 습관을 가지는 것이 좋습니다.

이제 Render Target이 있으므로, renderer에게 camera를 사용하여 scene을 렌더링하도록 지시해야 합니다.

이 모든 객체는 React Three Fiber 애플리케이션의 루트 상태에서 사용할 수 있습니다. 이는 useThree 훅이 반환하는 객체입니다.

gl 속성은 renderer입니다.

하지만, TV에서 실시간으로 무슨 일이 일어나는지를 보여주고자 하기 때문에, useFrame 훅을 사용하여 매 프레임마다 프로세스를 수행할 것입니다.

콜백 함수는 루트 상태를 포함하므로, 변수에 직접 접근할 수 있습니다:

// ...
import { useFrame } from "@react-three/fiber";

export const Experience = () => {
  // ...
  useFrame(({ gl, camera, scene }) => {
    gl.setRenderTarget(cornerRenderTarget);
    gl.render(scene, camera);
    gl.setRenderTarget(null);
  });
  // ...
};

여기서 우리는 다음을 수행하고 있습니다:

  • Render Targetrenderer 출력으로 설정
  • camera를 사용하여 scene을 렌더링하고, renderer 출력이 Render Target으로 설정되어 있으므로 장면을 그것에 렌더링하게 됩니다.
  • renderer 출력을 null로 설정하여 다시 캔버스에 장면을 렌더링

이제 Render Target이 준비되었으므로, TV에 표시해야 합니다. 현재 비디오를 렌더링하는 material에 대한 참조를 추가해 봅시다:

// ...
import { useRef } from "react";

export const Experience = () => {
  // ...
  const tvMaterial = useRef();
  // ...
  return (
    <>
      {/* ... */}
      <group position-y={-0.5}>
        <group>
          <Sky />
          <Avatar rotation-y={Math.PI} scale={0.45} position-z={0.34} />
          <Gltf src="models/Room.glb" scale={0.3} rotation-y={-Math.PI / 2} />
          <mesh position-x={0.055} position-y={0.48} position-z={-0.601}>
            <planeGeometry args={[0.63, 0.44]} />
            <meshBasicMaterial ref={tvMaterial} />
          </mesh>
        </group>
      </group>
      {/* ... */}
    </>
  );
};

그런 다음, useFrame 훅을 사용하여 Render Target을 사용하여 material의 map 속성을 업데이트할 수 있습니다:

// ...
export const Experience = () => {
  // ...
  useFrame(({ gl, camera, scene }) => {
    // ...
    tvMaterial.current.map = cornerRenderTarget.texture;
  });
  // ...
};

TV를 보며 3D 아바타가 보안 카메라의 장면을 보고 있는 모습

이제 TV에서 보안 카메라의 뷰를 볼 수 있지만, 보안 카메라 뷰의 TV 화면은 비어 있습니다.

이것은 우리가 Render Target에 장면을 렌더링할 때, 화면이 비어 있기 때문입니다.

인셉션 효과

TV 화면 안에 있는 보안 카메라 뷰를 보기 위해서는 🤯 다음과 같은 작업이 필요합니다:

  • 또 다른 render target을 생성합니다 (이름을 bufferRenderTarget이라고 하겠습니다)
  • 장면을 해당 target에 렌더링합니다
  • 그것을 TV 화면 material에 첨부합니다
  • 장면을 cornerRenderTarget에 렌더링합니다
  • 그것을 TV material에 첨부합니다
// ...
export const Experience = () => {
  const bufferRenderTarget = useFBO();

  // ...
  useFrame(({ gl, camera, scene }) => {
    gl.setRenderTarget(bufferRenderTarget);
    gl.render(scene, camera);
    tvMaterial.current.map = bufferRenderTarget.texture;
    gl.setRenderTarget(cornerRenderTarget);
    gl.render(scene, camera);
    gl.setRenderTarget(null);
    tvMaterial.current.map = cornerRenderTarget.texture;
  });
  // ...
};

무서워 보일 수 있지만 실제로는 아주 간단합니다. 각 단계에서 무슨 일이 일어나고 있는지 생각해 보세요.

이제 우리의 장면이 TV 화면 안에 무한히 렌더링 됩니다!

만약 우리가 만들 수 있는 가장 현실적인 효과라 하더라도, 그것은 가장 창의적인 것은 아닙니다. 인셉션 효과를 제거하고 대신 TV 화면에 스폰지밥을 표시해 봅시다:

Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.