渲染目标

Starter pack

当我们使用 React Three Fiber 时,我们只需添加一个 Canvas 组件,并将我们的3D场景放入其中,它就会渲染到屏幕上。

如果你有一些 Three.js 的经验,你会知道我们首先需要创建一个 WebGLRenderer,然后调用 renderer.render(scene, camera) 将我们的场景渲染到屏幕上。

幸运的是,React Three Fiber 在后台为我们做了所有这些工作,但对于更高级的用例,了解它的工作原理是很重要的。

renderer 的作用是处理3D场景,以创建我们在屏幕上看到的实际2D图像。

默认情况下,renderer 的输出设置为 Canvas 组件并显示在屏幕上,但我们也可以将其输出到纹理(通过 WebGLRenderTarget)。

目前这可能有点概念化,让我们看看它在实际中是如何工作的,以及我们可以用它来实现什么创造性的用途。

安全摄像头

为了理解 Render Targets 的工作原理以及我们可以用它们做什么,我准备了一个包含以下内容的3D场景:

  • 由 Alex Safayan 创建的客厅3D模型 CC-BY 来源于 Poly Pizza
  • 一个 Ready Player Me 的头像(就像我们在 portfolio 课程中使用的那个),正在观看电视上的海绵宝宝
  • 一个带有多个按钮的2D遥控器

3D头像正在看电视

我们的目标是通过从 安全摄像头 的视角在电视上渲染场景来创建一个 监控系统

将场景渲染到纹理

要将当前场景渲染到纹理,我们需要创建一个 Render Target

感谢 Drei library,我们可以使用 useFBO hook 轻松创建一个 Render Target

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

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

如果您想了解如何从头开始创建 Render Target,可以查看 useFBO hook 的源代码 这里。了解事物的工作原理是一个很好的反应。

现在我们有了 Render Target,需要告诉我们的 renderer 使用我们的 camera 渲染我们的 scene

所有这些对象都在 React Three Fiber 应用的 root state 中可用。这是 useThree hook 返回的对象。

gl 属性是 renderer

但由于我们想在电视上实时显示发生的事情,我们将在每个 frame 中使用 useFrame hook 进行处理。

回调函数包含 root state,因此我们可以直接从中访问我们的变量:

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

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

我们在这里做的是:

  • Render Target 设置为 renderer 输出
  • 使用 camera 渲染 scene,因为 renderer 输出已设置为 Render Target,它会将场景渲染到其中
  • renderer 输出设置为 null,再次将场景渲染到画布

现在我们有了 Render Target,需要在电视上显示它。让我们为当前渲染视频的 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 hook 将 material 的 map 属性更新为我们的 Render Target

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

3D avatar watching to the TV with the security camera view

我们现在可以在电视上看到 security camera 的画面,但 security camera 画面里的电视屏幕是空的。

这是因为当我们将场景渲染到 Render Target 时,我们的屏幕是空的。

梦境效应

要在电视屏幕内看到电视屏幕的监控摄像头视图 🤯,我们需要:

  • 创建另一个渲染目标(我们将其命名为 bufferRenderTarget
  • 将场景渲染到该目标
  • 将其附加到电视屏幕的 material
  • 将场景渲染到 cornerRenderTarget
  • 将其附加到电视的 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;
  });
  // ...
};

这可能听起来有些可怕,但实际上非常简单。只是想想每一步都发生了什么。

现在我们的场景在电视屏幕上无限次渲染!

即使这是我们能创造的最真实的效果,它也不是最具创意的一个。让我们移除 梦境效应 并在屏幕上显示 海绵宝宝

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.